chore: deploy initial code base

This commit is contained in:
Thom Lamb
2026-05-12 10:34:30 -05:00
parent cce2961782
commit a81300a571
1829 changed files with 248331 additions and 1 deletions
@@ -0,0 +1,410 @@
using CsvHelper;
using CsvHelper.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Strata.ContinuousImprovement.Biz.Shared;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess.ChargeCode;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests.DistributionProcess
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class DistributionProcessChargeCodeTests : IntegrationTestBase
{
private const string SourceFilePath = @"SnowflakeIntegrationTests/DistributionProcess/TestData/ChargeCode";
private static readonly ImmutableDictionary<string, (Period<DateOnly> Source, Period<DateOnly> Target)> _dataPeriod = new Dictionary<string, (Period<DateOnly> Source, Period<DateOnly> Target)>()
{
{ "AllMonthsAreCross", (new Period<DateOnly>(new DateOnly(2022, 12, 1), new DateOnly(2023, 12, 31)), new Period<DateOnly>(new DateOnly(2024, 1, 1), new DateOnly(2024, 12, 31))) },
{ "AllMonthsAreNotCross", (new Period<DateOnly>(new DateOnly(2023, 1, 1), new DateOnly(2023, 5, 30)), new Period<DateOnly>(new DateOnly(2023, 6, 1), new DateOnly(2023, 12, 31))) },
{ "MixedMonths", (new Period<DateOnly>(new DateOnly(2022, 12, 1), new DateOnly(2023, 8, 31)), new Period<DateOnly>(new DateOnly(2023, 5, 1), new DateOnly(2023, 12, 31))) }
}.ToImmutableDictionary();
private IDistributionProcessFactory _distributionProcessFactory;
private const long TestOpportunityId = 1;
private Period<DateOnly> _baselinePeriod;
private Period<DateOnly> _goalPeriod;
private Period<DateOnly> _trackingPeriod;
private double[] _rampUps;
[OneTimeSetUp]
public async Task Setup()
{
await CleanData();
await SeedDataFromCsv("BaselineData.csv");
_distributionProcessFactory = ServiceProvider.GetRequiredService<IDistributionProcessFactory>();
_baselinePeriod = new Period<DateOnly>(new DateOnly(2023, 1, 1), new DateOnly(2023, 12, 31));
_goalPeriod = new Period<DateOnly>(new DateOnly(2024, 1, 1), new DateOnly(2024, 12, 31));
_trackingPeriod = new Period<DateOnly>(new DateOnly(2024, 1, 1), new DateOnly(2024, 12, 31));
_rampUps = new double[] { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.0, 1.0 };
}
[OneTimeTearDown]
public async Task Cleanup()
{
await CleanData();
}
[TearDown]
public async Task CleanupTestData()
{
await CleanData(false);
}
[TestCaseSource(nameof(DistributeAsync_FromSourceToOpportunity_TestCases))]
public async Task DistributeAsync_FromSourceToOpportunity
(
DistributionMethod distributionMethod,
Period<DateOnly> sourcePeriod,
Period<DateOnly> targetPeriod,
double[] rampUps
)
{
// Arrange
var distributionProcess = _distributionProcessFactory.CreateDistributionProcessChargeCode(
TestOpportunityId,
sourcePeriod ?? _baselinePeriod,
targetPeriod ?? _goalPeriod,
_trackingPeriod,
[]);
// Act
var summary = await distributionProcess.DistributeAsync(distributionMethod, DistributionStage.FromSourceToOpportunity);
// Assert
var sbResult = new StringBuilder();
var details = await GetDetailRecords(isBaseline: false);
sbResult.AppendLine(ConvertListDetailToCsvString(details));
sbResult.AppendLine(ConvertSummaryToCsvString(summary));
await Verifier.Verify(sbResult.ToString(), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[TestCaseSource(nameof(DistributeAsync_FromOpportunityToInitiative_TestCases))]
public async Task DistributeAsync_FromOpportunityToInitiative
(
DistributionMethod distributionMethod,
Period<DateOnly> sourcePeriod,
Period<DateOnly> targetPeriod,
double[] rampUps
)
{
// Arrange
await SeedDataFromCsv("GoalData.csv");
var distributionProcess = _distributionProcessFactory.CreateDistributionProcessChargeCode(
TestOpportunityId,
_baselinePeriod,
sourcePeriod ?? _goalPeriod,
targetPeriod ?? _trackingPeriod,
rampUps ?? _rampUps);
// Act
var summary = await distributionProcess.DistributeAsync(distributionMethod, DistributionStage.FromOpportunityToInitiative);
// Assert
var sbResult = new StringBuilder();
var details = await GetDetailRecords(isBaseline: false);
sbResult.AppendLine(ConvertListDetailToCsvString(details));
sbResult.AppendLine(ConvertSummaryToCsvString(summary));
await Verifier.Verify(sbResult.ToString(), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[TestCaseSource(nameof(DistributeAsync_FromInitiativeToOpportunity_TestCases))]
public async Task DistributeAsync_FromInitiativeToOpportunity
(
DistributionMethod distributionMethod,
Period<DateOnly> sourcePeriod,
Period<DateOnly> targetPeriod,
double[] rampUps
)
{
// Arrange
await SeedDataFromCsv("GoalData.csv");
var distributionProcess = _distributionProcessFactory.CreateDistributionProcessChargeCode(
TestOpportunityId,
_baselinePeriod,
sourcePeriod ?? _goalPeriod,
targetPeriod ?? _trackingPeriod,
rampUps ?? _rampUps);
// Act
var summary = await distributionProcess.DistributeAsync(distributionMethod, DistributionStage.FromInitiativeToOpportunity);
// Assert
var sbResult = new StringBuilder();
var details = await GetDetailRecords(isBaseline: false);
sbResult.AppendLine(ConvertListDetailToCsvString(details));
sbResult.AppendLine(ConvertSummaryToCsvString(summary));
await Verifier.Verify(sbResult.ToString(), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[TestCaseSource(nameof(UpdateDetailCommittedByRampUp_TestCases))]
public async Task UpdateDetailCommittedByRampUp
(
double[] oldRampUps,
double[] newRampUps
)
{
// Arrange
await SeedDataFromCsv("GoalData.csv");
var distributionProcess = _distributionProcessFactory.CreateDistributionProcessChargeCode(
TestOpportunityId,
_baselinePeriod,
_goalPeriod,
_trackingPeriod,
newRampUps ?? _rampUps);
// Act
var summary = await distributionProcess.UpdateDetailCommittedByRampUp(oldRampUps);
// Assert
var sbResult = new StringBuilder();
var details = await GetDetailRecords(isBaseline: false);
sbResult.AppendLine(ConvertListDetailToCsvString(details));
sbResult.AppendLine(ConvertSummaryToCsvString(summary));
await Verifier.Verify(sbResult.ToString(), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[TestCaseSource(nameof(GetDistributionSummary_TestCases))]
public async Task GetDistributionSummary
(
bool isInitiative,
double[] rampUps
)
{
// Arrange
await SeedDataFromCsv("GoalData.csv");
var distributionProcess = _distributionProcessFactory.CreateDistributionProcessChargeCode(
TestOpportunityId,
_baselinePeriod,
_goalPeriod,
_trackingPeriod,
rampUps ?? _rampUps);
// Act
var summary = await distributionProcess.GetDistributionSummaryAsync(isInitiative);
// Assert
var sbResult = new StringBuilder();
sbResult.AppendLine(ConvertSummaryToCsvString(summary));
await Verifier.Verify(sbResult.ToString(), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
#region Test Case Sources
private static IEnumerable<TestCaseData> DistributeAsync_FromSourceToOpportunity_TestCases()
{
var distributionStageName = DistributionStage.FromSourceToOpportunity.ToString();
return DistributeAsync_TestCases(distributionStageName);
}
private static IEnumerable<TestCaseData> DistributeAsync_FromOpportunityToInitiative_TestCases()
{
var distributionStageName = DistributionStage.FromOpportunityToInitiative.ToString();
foreach (var x in DistributeAsync_TestCases(distributionStageName))
yield return x;
if (_dataPeriod.TryGetValue("AllMonthsAreCross", out var periods))
{
yield return new TestCaseData(
DistributionMethod.Average,
periods.Source,
periods.Target,
Array.Empty<double>()
).SetName($"DistributeAsync_{distributionStageName}_Average_AllMonthsAreCross_WithoutRampups");
}
}
private static IEnumerable<TestCaseData> DistributeAsync_FromInitiativeToOpportunity_TestCases()
{
var distributionStageName = DistributionStage.FromInitiativeToOpportunity.ToString();
foreach (var x in DistributeAsync_TestCases(distributionStageName))
yield return x;
if (_dataPeriod.TryGetValue("AllMonthsAreCross", out var periods))
{
yield return new TestCaseData(
DistributionMethod.Average,
periods.Source,
periods.Target,
Array.Empty<double>()
).SetName($"DistributeAsync_{distributionStageName}_Average_AllMonthsAreCross_WithoutRampups");
}
}
private static IEnumerable<TestCaseData> DistributeAsync_TestCases(string distributionStageName)
{
if (_dataPeriod.TryGetValue("AllMonthsAreCross", out var periods))
{
yield return new TestCaseData(
DistributionMethod.Average,
periods.Source,
periods.Target,
null
).SetName($"DistributeAsync_{distributionStageName}_Average_AllMonthsAreCross");
}
foreach (var x in _dataPeriod)
{
yield return new TestCaseData(
DistributionMethod.Monthly,
x.Value.Source,
x.Value.Target,
null
).SetName($"DistributeAsync_{distributionStageName}_Monthly_{x.Key}");
}
}
private static IEnumerable<TestCaseData> UpdateDetailCommittedByRampUp_TestCases()
{
var rampUp = new double[] { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.0, 1.0 };
yield return new TestCaseData(
rampUp,
rampUp.Reverse().ToArray()
).SetName($"UpdateDetailCommittedByRampUp_RampUpToRampUp");
yield return new TestCaseData(
rampUp,
Array.Empty<double>()
).SetName($"UpdateDetailCommittedByRampUp_RampUpToNoRampUp");
yield return new TestCaseData(
Array.Empty<double>(),
rampUp
).SetName($"UpdateDetailCommittedByRampUp_NoRampUpToRampUp");
yield return new TestCaseData(
Array.Empty<double>(),
Array.Empty<double>()
).SetName($"UpdateDetailCommittedByRampUp_NoRampUpToNoRampUp");
yield return new TestCaseData(
rampUp,
rampUp
).SetName($"UpdateDetailCommittedByRampUp_NoChangeRampUp");
}
private static IEnumerable<TestCaseData> GetDistributionSummary_TestCases()
{
yield return new TestCaseData(
true,
null
).SetName($"GetDistributionSummary_Initiative");
yield return new TestCaseData(
true,
Array.Empty<double>()
).SetName($"GetDistributionSummary_Initiative_NoRampUp");
yield return new TestCaseData(
false,
null
).SetName($"GetDistributionSummary_Opportunity");
yield return new TestCaseData(
false,
Array.Empty<double>()
).SetName($"GetDistributionSummary_Opportunity_NoRampUp");
}
#endregion
#region Helper Methods
private async Task SeedDataFromCsv(string fileName)
{
var filePath = Path.IsPathRooted(fileName)
? fileName
: Path.Combine(AppContext.BaseDirectory, SourceFilePath, fileName);
var insertSql = await GetSqlInsertFromCsv(filePath, StrategicOpportunityService.StrategicOpportunityDetailTableName);
await SnowflakeDatabaseContext.ExecuteCommandAsync(insertSql);
}
private async Task<string> GetSqlInsertFromCsv(string filePath, string tableName)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"Test data file not found: {filePath}");
}
var lines = await File.ReadAllLinesAsync(filePath);
if (lines.Length < 2)
{
throw new InvalidOperationException($"CSV file {filePath} must contain a header and at least one data row");
}
var values = lines.Skip(1).Select(x => $"({x})").ToList();
var insertSql = $@"
INSERT INTO {tableName} ({lines[0]})
VALUES {string.Join(",\n", values)}
";
return insertSql;
}
private string ConvertListDetailToCsvString(IEnumerable<StrategicOpportunityDetail> records)
{
using (var writer = new StringWriter())
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.Context.RegisterClassMap<StrategicOpportunityDetailCsvMap>();
csv.WriteRecords(records);
return writer.ToString();
}
}
private string ConvertSummaryToCsvString(DistributionChargeCodeSummary summary)
{
using (var writer = new StringWriter())
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords([summary]);
return writer.ToString();
}
}
private async Task<IEnumerable<StrategicOpportunityDetail>> GetDetailRecords(bool? isBaseline = null)
{
var sql = $@"
SELECT * FROM {StrategicOpportunityService.StrategicOpportunityDetailTableName}
WHERE OpportunityId = {TestOpportunityId} {(isBaseline.HasValue ? $"AND IsBaseline = {isBaseline}" : "")}
ORDER BY DischargeFiscalYearId, DischargeFiscalMonthId, EntityId
";
var records = await SnowflakeDatabaseContext.QueryAsync<StrategicOpportunityDetail>(sql, nameof(GetDetailRecords));
return records;
}
private async Task CleanData(bool? isBaseline = null)
{
await SnowflakeDatabaseContext.ExecuteCommandAsync($@"
DELETE FROM {StrategicOpportunityService.StrategicOpportunityDetailTableName}
WHERE OpportunityId = {TestOpportunityId} {(isBaseline.HasValue ? $"AND IsBaseline = {isBaseline}" : "")}
");
}
#endregion
}
public class StrategicOpportunityDetailCsvMap : ClassMap<StrategicOpportunityDetail>
{
public StrategicOpportunityDetailCsvMap()
{
AutoMap(CultureInfo.InvariantCulture);
Map(m => m.RowId).Ignore();
}
}
}
@@ -0,0 +1,302 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Strata.ContinuousImprovement.Biz.DbContexts;
using Strata.ContinuousImprovement.Biz.Exploration;
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
using Strata.ContinuousImprovement.Biz.FiscalMonths;
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
using Strata.ContinuousImprovement.Biz.Utilities;
using Strata.Schema.Client;
using Strata.Schema.Client.Dtos;
using Strata.Schema.Client.Dtos.Info;
using Strata.SqlTools.Configuration.Common.AsyncFactory;
using Strata.StrataSphereCompare.Client;
using System.Diagnostics.CodeAnalysis;
using VerifyNUnit;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
/// <summary>
/// Baseline regression tests for the exploration query refactor.
///
/// WORKFLOW:
/// 1. On main (or a branch from main): run these tests to generate golden snapshots.
/// Commit the resulting *.verified.txt files.
/// 2. On the refactor branch: run the same tests.
/// Verify diffs output against the golden snapshots — any divergence fails.
/// </summary>
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class FlexibleExplorationBaselineTest : IntegrationTestBase
{
private static readonly Guid _configurationGuid = Guid.Parse("aa4cba4f-2506-462c-8e34-c45b21653988");
private ExplorationDetailInfo _detailInfo;
[OneTimeSetUp]
public async Task SetupDetailInfo()
{
var summaryInfo = CreateSummaryInfo();
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(summaryInfo, CancellationToken.None);
if (!summaries.Any())
throw new InvalidOperationException("No Case Type Family summaries returned — cannot run baseline tests.");
// Configure the schema service mock so the Gender ScoreMember filter resolves to
// a real SQL fragment (fpes.GenderID IN (1)) rather than being silently skipped.
// GenderID 1 = Male in the GM Automation test database.
var genderDimensionGuid = Guid.NewGuid();
Mock.Get(SchemaServiceClient)
.Setup(s => s.GetDimensionByGlobalIdAsync("Gender", It.IsAny<CancellationToken>()))
.ReturnsAsync(new ScoreDimensionInfoDto { DimensionGuid = genderDimensionGuid });
Mock.Get(SchemaServiceClient)
.Setup(s => s.GetDimensionMembersFromHierarchyPathsAsync(genderDimensionGuid, It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new[] { new MemberDto { Id = "1" } });
// Rebuild ExplorationFilterService and ExplorationService with the configured mock
// so the gender filter is active for all tests in this fixture.
var filterService = new ExplorationFilterService(
ServiceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>(),
SchemaServiceClient,
Mock.Of<ILogger<ExplorationFilterService>>());
var featureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
Mock.Get(featureFlagWrapper)
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
.ReturnsAsync(true);
ExplorationService = new ExplorationService(
JazzConnBuilderFactory,
SnowflakeDatabaseContext,
Mock.Of<IFiscalMonthResolver>(),
featureFlagWrapper,
Mock.Of<IStrataSphereCompareService>(),
filterService,
Mock.Of<ILogger<ExplorationService>>());
// Always pick the same CTF (highest encounter count) so snapshots are deterministic.
var busiest = summaries.OrderByDescending(s => s.Data.TotalEncounters).First();
// Derive a real cost driver from the CTF's detail breakdown so the
// encounter query (which filters on cc.costdriver = @CostDriver) returns data.
var costDriverInfo = CreateSummaryInfo();
costDriverInfo.CaseTypeFamilyId = busiest.Data.Id;
var costDriverDetails = await ExplorationService.GetCaseTypeFamilySummaryDetailsAsync(costDriverInfo, CancellationToken.None);
var costDriver = costDriverDetails
.SelectMany(d => d.Children ?? [])
.OrderByDescending(c => c.Data.TotalEncounters)
.FirstOrDefault()?.Name
?? throw new InvalidOperationException("No cost driver children found for the busiest CTF.");
_detailInfo = new ExplorationDetailInfo
{
ConfigurationGuid = _configurationGuid,
CaseTypeFamilyId = busiest.Data.Id,
CostDriver = costDriver,
ExclusionCriteria = new ExclusionCriteria(),
ExcludedEncounters = new List<long>(),
IncludedEncounters = new List<long>(),
Filters = new Biz.Exploration.Filters.Filter
{
DischargeDateStart = new DateTime(2024, 1, 1),
DischargeDateEnd = new DateTime(2025, 1, 1),
FilterChipItems = new List<FilterChipItem>
{
new FilterChipItem
{
ChipType = ChipType.DateRange,
Key = ChipKey.DischargeDateID,
Filters = [],
DateRange = [new DateTime(2024, 1, 1), new DateTime(2025, 1, 1)]
}
}
}
};
}
// ── No filters ──────────────────────────────────────────────────────────
[Test]
public async Task GetCaseTypeFamilyDetailsAsync_MatchesBaseline()
{
var result = await ExplorationService.GetCaseTypeFamilyDetailsAsync(_detailInfo, CancellationToken.None);
await Verifier.Verify(result, verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[Test]
public async Task GetEncountersAsync_MatchesBaseline()
{
var result = await ExplorationService.GetEncountersAsync(_detailInfo, _configurationGuid, CancellationToken.None);
await Verifier.Verify(result.OrderBy(e => e.EncounterId), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
// ── Boolean filter (charges > 0) ─────────────────────────────────────
// IsGreaterThanZeroCharges hits the queryFilter path and reliably
// produces a meaningful subset without risking empty results.
[Test]
public async Task GetCaseTypeFamilyDetailsAsync_WithChargesFilter_MatchesBaseline()
{
var info = CreateDetailInfo(extraFilter: CreateChargesFilter());
var result = await ExplorationService.GetCaseTypeFamilyDetailsAsync(info, CancellationToken.None);
await Verifier.Verify(result, verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[Test]
public async Task GetEncountersAsync_WithChargesFilter_MatchesBaseline()
{
var info = CreateDetailInfo(extraFilter: CreateChargesFilter());
var result = await ExplorationService.GetEncountersAsync(info, _configurationGuid, CancellationToken.None);
await Verifier.Verify(result.OrderBy(e => e.EncounterId), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
// ── FPES ScoreMember filter (gender) ────────────────────────────────
// Gender is an FPES-level filter — it resolves to fpes.GenderID IN (...)
// via the schema service and lands in the encounterSummaryFilter placeholder.
[Test]
public async Task GetCaseTypeFamilyDetailsAsync_WithGenderFilter_MatchesBaseline()
{
var info = CreateDetailInfo(extraFilter: CreateGenderFilter());
var result = await ExplorationService.GetCaseTypeFamilyDetailsAsync(info, CancellationToken.None);
await Verifier.Verify(result, verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[Test]
public async Task GetEncountersAsync_WithGenderFilter_MatchesBaseline()
{
var info = CreateDetailInfo(extraFilter: CreateGenderFilter());
var result = await ExplorationService.GetEncountersAsync(info, _configurationGuid, CancellationToken.None);
await Verifier.Verify(result.OrderBy(e => e.EncounterId), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
// ── IQR exclusions ───────────────────────────────────────────────────
[Test]
public async Task GetCaseTypeFamilyDetailsAsync_WithIqrExclusions_MatchesBaseline()
{
var info = CreateDetailInfo(exclusion: new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Iqr,
CostExclusionValue = 1.5,
LosExclusionType = ExclusionCriteriaType.Iqr,
LosExclusionValue = 1.5
});
var result = await ExplorationService.GetCaseTypeFamilyDetailsAsync(info, CancellationToken.None);
await Verifier.Verify(result, verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
[Test]
public async Task GetEncountersAsync_WithIqrExclusions_MatchesBaseline()
{
var info = CreateDetailInfo(exclusion: new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Iqr,
CostExclusionValue = 1.5,
LosExclusionType = ExclusionCriteriaType.Iqr,
LosExclusionValue = 1.5
});
var result = await ExplorationService.GetEncountersAsync(info, _configurationGuid, CancellationToken.None);
await Verifier.Verify(result.OrderBy(e => e.EncounterId), verifySettings)
.UseFileName(TestContext.CurrentContext.TestName());
}
// ── Helpers ──────────────────────────────────────────────────────────
private ExplorationDetailInfo CreateDetailInfo(
FilterChipItem extraFilter = null,
ExclusionCriteria exclusion = null)
{
var dateFilter = new FilterChipItem
{
ChipType = ChipType.DateRange,
Key = ChipKey.DischargeDateID,
Filters = [],
DateRange = [new DateTime(2024, 1, 1), new DateTime(2025, 1, 1)]
};
var chips = extraFilter != null
? new List<FilterChipItem> { dateFilter, extraFilter }
: new List<FilterChipItem> { dateFilter };
return new ExplorationDetailInfo
{
ConfigurationGuid = _detailInfo.ConfigurationGuid,
CaseTypeFamilyId = _detailInfo.CaseTypeFamilyId,
CostDriver = _detailInfo.CostDriver,
ExclusionCriteria = exclusion ?? new ExclusionCriteria(),
ExcludedEncounters = new List<long>(),
IncludedEncounters = new List<long>(),
Filters = new Biz.Exploration.Filters.Filter
{
DischargeDateStart = new DateTime(2024, 1, 1),
DischargeDateEnd = new DateTime(2025, 1, 1),
FilterChipItems = chips
}
};
}
// Gender filter — FPES-level (encounterSummaryFilter placeholder).
// GlobalId "Gender" maps to the Gender dimension in the schema service.
// The Filters list contains the hierarchy path ID for "Male" as resolved
// by the schema service; adjust the Id if needed for your test database.
private static FilterChipItem CreateGenderFilter() => new()
{
ChipType = ChipType.ScoreMember,
GlobalId = "Gender",
Key = ChipKey.GenderID,
Operator = OperatorType.In,
Filters = new List<TreeNode> { new() { Id = "Male", DisplayText = "Male" } }
};
private static FilterChipItem CreateChargesFilter() => new()
{
ChipType = ChipType.Boolean,
Key = ChipKey.IsGreaterThanZeroCharges,
IsSelected = true,
Operator = OperatorType.In,
Filters = new List<TreeNode>()
};
private ExplorationCostDriverInfo CreateSummaryInfo() => new()
{
ConfigurationGuid = _configurationGuid,
ExclusionCriteria = new ExclusionCriteria(),
Filters = new Biz.Exploration.Filters.Filter
{
DischargeDateStart = new DateTime(2024, 1, 1),
DischargeDateEnd = new DateTime(2025, 1, 1),
FilterChipItems = new List<FilterChipItem>
{
new FilterChipItem
{
ChipType = ChipType.DateRange,
Key = ChipKey.DischargeDateID,
Filters = [],
DateRange = [new DateTime(2024, 1, 1), new DateTime(2025, 1, 1)]
}
}
}
};
}
}
@@ -0,0 +1,193 @@
using FluentAssertions;
using Strata.ContinuousImprovement.Biz.Exploration;
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
using System.Diagnostics.CodeAnalysis;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class FlexibleExplorationSummaryDetailTest : IntegrationTestBase
{
private static readonly Guid _configurationGuid = Guid.Parse("aa4cba4f-2506-462c-8e34-c45b21653988");
[OneTimeSetUp]
public async Task SetupExploration()
{
var explorationInfo = CreateExplorationInfo();
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
if (!summaries.Any())
throw new InvalidOperationException("No Case Type Family summaries returned for setup.");
}
[Test]
public async Task GetCaseTypeFamilySummaryDetails_WhenNoFilters_IsEquivalent()
{
// arrange
var explorationInfo = CreateExplorationInfo();
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
var testSummary = summaries.OrderByDescending(x => x.Data.TotalEncounters).First();
explorationInfo.CaseTypeFamilyId = testSummary.Data.Id;
var testDetails = await ExplorationService.GetCaseTypeFamilySummaryDetailsAsync(explorationInfo, CancellationToken.None);
var testDetail = testDetails.First();
// assert
testDetail.Data.Should().BeEquivalentTo(testSummary.Data,
options => options
.Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, 0.001))
.WhenTypeIs<double>());
}
[TestCaseSource(nameof(ExclusionCriteriaTestCases))]
public async Task GetCaseTypeFamilySummaries_WithExclusions(ExclusionCriteria exclusions)
{
await RunExclusionSummaryTests(exclusions);
}
[Test]
public async Task GetCaseTypeFamilySummaryDetails_WithFilters_IsEquivalent()
{
// arrange - filtering on mortality (IsSelected = true => MortalityRate = 1)
var booleanFilter = CreateBooleanFilter(ChipKey.Mortality);
var explorationInfo = CreateExplorationInfo(booleanFilter);
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
var testSummary = summaries.OrderByDescending(x => x.Data.TotalEncounters).First();
explorationInfo.CaseTypeFamilyId = testSummary.Data.Id;
var testDetails = await ExplorationService.GetCaseTypeFamilySummaryDetailsAsync(explorationInfo, CancellationToken.None);
var testDetail = testDetails.First();
// assert
testDetail.Data.Should().BeEquivalentTo(testSummary.Data,
options => options
.Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, 0.001))
.WhenTypeIs<double>());
}
[Test]
public async Task GetCaseTypeFamilySummaryDetails_WithFiltersAndExclusions_IsEquivalent()
{
// arrange - iqr + mad exclusion combo with a boolean filter
var booleanFilter = CreateBooleanFilter(ChipKey.Mortality);
var exclusions = new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Iqr,
CostExclusionValue = 1.5,
LosExclusionType = ExclusionCriteriaType.Mad,
LosExclusionValue = 2
};
var explorationInfo = CreateExplorationInfo(booleanFilter, exclusions);
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
var testSummary = summaries.OrderByDescending(x => x.Data.TotalEncounters).First();
explorationInfo.CaseTypeFamilyId = testSummary.Data.Id;
var testDetails = await ExplorationService.GetCaseTypeFamilySummaryDetailsAsync(explorationInfo, CancellationToken.None);
var testDetail = testDetails.First();
// assert
testDetail.Data.Should().BeEquivalentTo(testSummary.Data,
options => options
.Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, 0.001))
.WhenTypeIs<double>());
}
private FilterChipItem CreateDischargeDateFilter() => new()
{
ChipType = ChipType.DateRange,
Filters = [],
DateRange = [new DateTime(2024, 1, 1), new DateTime(2025, 1, 1)]
};
private FilterChipItem CreateBooleanFilter(ChipKey key)
{
if (key is ChipKey.Mortality)
{
return new FilterChipItem
{
ChipType = ChipType.Boolean,
GlobalId = "Mortality",
IsSelected = true, // determines true/false of filter
Key = ChipKey.Mortality,
Operator = OperatorType.In,
Filters = new List<TreeNode>()
{
new TreeNode
{
DisplayText = "Yes"
}},
};
}
throw new ArgumentException($"Unsupported boolean filter: {key}", nameof(key));
}
private ExplorationCostDriverInfo CreateExplorationInfo(FilterChipItem? additionalFilter = null, ExclusionCriteria? exclusion = null)
{
var dischargeDateFilter = CreateDischargeDateFilter();
var filterChipItems = additionalFilter != null ?
new List<FilterChipItem> { dischargeDateFilter, additionalFilter }
: new List<FilterChipItem> { dischargeDateFilter };
return new ExplorationCostDriverInfo
{
ConfigurationGuid = _configurationGuid,
ExclusionCriteria = exclusion ?? new ExclusionCriteria(),
Filters = new Biz.Exploration.Filters.Filter
{
DischargeDateStart = dischargeDateFilter.DateRange.First(),
DischargeDateEnd = dischargeDateFilter.DateRange.Last(),
FilterChipItems = filterChipItems
}
};
}
private async Task RunExclusionSummaryTests(ExclusionCriteria exclusions)
{
// arrange
var explorationInfo = CreateExplorationInfo(null, exclusions);
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
var testSummary = summaries.OrderByDescending(x => x.Data.TotalEncounters).First();
explorationInfo.CaseTypeFamilyId = testSummary.Data.Id;
var testDetails = await ExplorationService.GetCaseTypeFamilySummaryDetailsAsync(explorationInfo, CancellationToken.None);
var testDetail = testDetails.First();
// assert
testDetail.Data.Should().BeEquivalentTo(testSummary.Data,
options => options
.Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, 0.001))
.WhenTypeIs<double>());
}
private static IEnumerable<TestCaseData> ExclusionCriteriaTestCases()
{
yield return new TestCaseData(
new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Mad,
CostExclusionValue = 2,
LosExclusionType = ExclusionCriteriaType.Mad,
LosExclusionValue = 2
}
).SetName("GetCaseTypeFamilySummaryDetails_WithMADExclusions_IsEquivalent");
yield return new TestCaseData(
new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Iqr,
CostExclusionValue = 1.5,
LosExclusionType = ExclusionCriteriaType.Iqr,
LosExclusionValue = 1.5
}
).SetName("GetCaseTypeFamilySummaryDetails_WithIQRExclusions_IsEquivalent");
}
}
}
@@ -0,0 +1,207 @@
using FluentAssertions;
using Strata.ContinuousImprovement.Biz.Exploration;
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
using System.Diagnostics.CodeAnalysis;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class FlexibleExplorationSummaryTest : IntegrationTestBase
{
private static readonly Guid _configurationGuid = Guid.Parse("aa4cba4f-2506-462c-8e34-c45b21653988");
private static int _totalCaseTypeFamilyCount;
[OneTimeSetUp]
public async Task SetupExploration()
{
var explorationInfo = CreateExplorationInfo();
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
if (!summaries.Any())
throw new InvalidOperationException("No Case Type Family summaries returned for setup.");
_totalCaseTypeFamilyCount = summaries.Count();
}
[Test]
public async Task GetCaseTypeFamilySummaries_WhenNoFilters_ReturnsAll()
{
// arrange
var explorationInfo = CreateExplorationInfo();
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
// assert
summaries.Should().HaveCount(_totalCaseTypeFamilyCount);
await VerifySummaries(summaries);
}
[TestCaseSource(nameof(ExclusionCriteriaTestCases))]
public async Task GetCaseTypeFamilySummaries_WithExclusions(ExclusionCriteria exclusions)
{
await RunExclusionSummaryTests(exclusions);
}
[Test]
public async Task GetCaseTypeFamilySummaries_WithFilters_ReturnsFiltered()
{
// arrange - filtering on mortality (IsSelected = true => MortalityRate = 1)
var booleanFilter = CreateBooleanFilter(ChipKey.Mortality);
var explorationInfo = CreateExplorationInfo(booleanFilter);
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
// assert
summaries.Should().HaveCountGreaterThan(0);
summaries.Should().HaveCountLessThan(_totalCaseTypeFamilyCount, "Filters should reduce data returned");
// mortality rate only applies to included encounters
summaries.Where(x => x.Data.IncludedEncounterCount > 0).Should().OnlyContain(s => s.Data.MortalityRate == 1);
await VerifySummaries(summaries);
}
[Test]
public async Task GetCaseTypeFamilySummaries_WithFiltersAndExclusions_ReturnsFilteredAndExcluded()
{
// arrange - iqr + mad exclusion combo with a boolean filter
var booleanFilter = CreateBooleanFilter(ChipKey.Mortality);
var exclusions = new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Iqr,
CostExclusionValue = 1.5,
LosExclusionType = ExclusionCriteriaType.Mad,
LosExclusionValue = 2
};
var explorationInfo = CreateExplorationInfo(booleanFilter, exclusions);
// act
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
// assert
summaries.Should().HaveCountGreaterThan(0);
summaries.Should().HaveCountLessThan(_totalCaseTypeFamilyCount);
// mortality rate only applies to included encounters
summaries.Where(x => x.Data.IncludedEncounterCount > 0).Should().OnlyContain(s => s.Data.MortalityRate == 1);
summaries.Should().Contain(s => s.Data.ExcludedEncountersAboveMedian > 0);
await VerifySummaries(summaries);
}
private FilterChipItem CreateDischargeDateFilter() => new()
{
ChipType = ChipType.DateRange,
Filters = [],
DateRange = [new DateTime(2024, 1, 1), new DateTime(2025, 1, 1)]
};
private FilterChipItem CreateBooleanFilter(ChipKey key)
{
if (key is ChipKey.Mortality)
{
return new FilterChipItem
{
ChipType = ChipType.Boolean,
GlobalId = "Mortality",
IsSelected = true, // determines true/false of filter
Key = ChipKey.Mortality,
Operator = OperatorType.In,
Filters = new List<TreeNode>()
{
new TreeNode
{
DisplayText = "Yes"
}},
};
}
throw new ArgumentException($"Unsupported boolean filter: {key}", nameof(key));
}
private ExplorationInfo CreateExplorationInfo(FilterChipItem? additionalFilter = null, ExclusionCriteria? exclusion = null)
{
var dischargeDateFilter = CreateDischargeDateFilter();
var filterChipItems = additionalFilter != null ?
new List<FilterChipItem> { dischargeDateFilter, additionalFilter }
: new List<FilterChipItem> { dischargeDateFilter };
return new ExplorationInfo
{
ConfigurationGuid = _configurationGuid,
ExclusionCriteria = exclusion ?? new ExclusionCriteria(),
Filters = new Biz.Exploration.Filters.Filter
{
DischargeDateStart = dischargeDateFilter.DateRange.First(),
DischargeDateEnd = dischargeDateFilter.DateRange.Last(),
FilterChipItems = filterChipItems
}
};
}
private async Task AssertExclusionsWereApplied(IEnumerable<CaseTypeFamilySummaryData> summaries)
{
summaries.Should().HaveCount(_totalCaseTypeFamilyCount);
summaries.Should().Contain(s => s.Data.ExcludedEncountersAboveMedian > 0);
}
private Task VerifySummaries(IEnumerable<CaseTypeFamilySummaryData> summaries)
{
summaries.Should().NotBeNull().And.NotBeEmpty();
summaries.Should().OnlyContain(s => !string.IsNullOrWhiteSpace(s.Key));
summaries.Should().OnlyContain(s => !string.IsNullOrWhiteSpace(s.Name));
summaries.Should().AllSatisfy(s => s.Key.Should().MatchRegex(@"^\d+-\d+$", "Key should follow format 'level-id'"));
summaries.Should().AllSatisfy(s => s.Data.Should().NotBeNull("Each summary should have data"));
summaries.Should().AllSatisfy(s => s.Data.Key.Should().MatchRegex(@"^\d+-\d+$", "Key should follow format 'level-id'"));
summaries.Should().AllSatisfy(s => s.Data.Id.Should().BeGreaterThan(0, "Each summary data should have a valid ID"));
summaries.Should().AllSatisfy(s => s.Data.TotalEncounters.Should().BeGreaterThanOrEqualTo(0, "Each summary data should have a valid TotalEncounters"));
summaries.Should().AllSatisfy(s => s.Data.TotalVariationEncounters.Should().BeGreaterThanOrEqualTo(0, "Each summary data should have a valid TotalVariationEncounters"));
summaries.Should().AllSatisfy(s => s.Data.IncludedEncounterCount.Should().BeGreaterThanOrEqualTo(0, "Each summary data should have a valid IncludedEncounterCount"));
summaries.Should().AllSatisfy(s => s.Data.IdentifiedVariation.Should().BeGreaterThanOrEqualTo(0, "Each summary data should have a valid IdentifiedVariation"));
summaries.Should().AllSatisfy(s => s.Data.MortalityRate.Should().BeInRange(0, 1, "MortalityRate should be between 0 and 1"));
summaries.Should().AllSatisfy(s => s.Data.MedianLos.Should().BeGreaterOrEqualTo(0, "MedianLos should be non-negative"));
summaries.Should().AllSatisfy(s => s.Data.MedianCost.Should().BeGreaterOrEqualTo(0, "MedianCost should be non-negative"));
summaries.Should().AllSatisfy(s => s.Data.IncludedEncountersBelowMedian.Should().BeGreaterOrEqualTo(0, "IncludedEncountersBelowMedian should be non-negative"));
summaries.Should().AllSatisfy(s => s.Data.IncludedEncountersAboveMedian.Should().BeGreaterOrEqualTo(0, "IncludedEncountersAboveMedian should be non-negative"));
summaries.Should().AllSatisfy(s => s.Data.IncludedVariation.Should().BeGreaterOrEqualTo(0, "IncludedVariation should be non-negative"));
return Verify(summaries, verifySettings)
.UseDirectory("Exploration")
.UseFileName(TestContext.CurrentContext.TestName());
}
private async Task RunExclusionSummaryTests(ExclusionCriteria exclusions)
{
// act
var explorationInfo = CreateExplorationInfo(null, exclusions);
var summaries = await ExplorationService.GetCaseTypeFamilySummariesAsync(explorationInfo, CancellationToken.None);
// assert
await AssertExclusionsWereApplied(summaries);
await VerifySummaries(summaries);
}
private static IEnumerable<TestCaseData> ExclusionCriteriaTestCases()
{
yield return new TestCaseData(
new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Mad,
CostExclusionValue = 2,
LosExclusionType = ExclusionCriteriaType.Mad,
LosExclusionValue = 2
}
).SetName("GetCaseTypeFamilySummaries_WithMADExclusions_ReturnsWithExclusions");
yield return new TestCaseData(
new ExclusionCriteria
{
CostExclusionType = ExclusionCriteriaType.Iqr,
CostExclusionValue = 1.5,
LosExclusionType = ExclusionCriteriaType.Iqr,
LosExclusionValue = 1.5
}
).SetName("GetCaseTypeFamilySummaries_WithIQRExclusions_ReturnsWithExclusions");
}
}
}
@@ -0,0 +1,457 @@
using FluentAssertions;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Moq;
using Strata.ContinuousImprovement.Biz.DbContexts;
using Strata.ContinuousImprovement.Biz.Exploration;
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
using Strata.ContinuousImprovement.Biz.Notification;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
using Strata.DataSchema.Client;
using Strata.DataSchema.Models.Query;
using Strata.DataSchema.Models.Schema;
using Strata.Schema.Client;
using Strata.Schema.Client.Dtos;
using System.Diagnostics.CodeAnalysis;
using static Strata.ContinuousImprovement.Biz.Exploration.ExplorationService;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class StrategicChargeCodeOpportunityDetailServiceTests : IntegrationTestBase
{
private IStrategicChargeCodeOpportunityDetailService _strategicOpportunityDetailService;
private Mock<IDataSchemaService> _mockDataSchemaService = new Mock<IDataSchemaService>();
private Mock<ISchemaServiceClient> _mockSchemaServiceClient = new Mock<ISchemaServiceClient>();
private Mock<IExplorationFilterService> _mockExplorationService = new Mock<IExplorationFilterService>();
private Mock<IStrategicItemDimensionSyncService> _mockStrategicItemDimensionSyncService = new Mock<IStrategicItemDimensionSyncService>();
private IStrategicOpportunityDetailQueryBuilder _strategicOpportunityDetailQueryBuilder;
private const long TestOpportunityId = 999_000_001;
private const long TestMeasureId = TestOpportunityId * 10 + 2;
private const string MeasureFiltersJSON = "[17993, 17995, 18001]";
private static readonly Guid ChargeCodeDimensionGuid = new("aaaaaaaa-0000-0000-0000-000000000001");
private static readonly Guid ClinicalIndicatorDimensionGuid = new("bbbbbbbb-0000-0000-0000-000000000002");
private const long TestExplorationPopulationId = 999_000_002;
private const long TestOpportunityId_Exploration = 999_000_002;
private const long TestMeasureId_Exploration = TestOpportunityId_Exploration * 10 + 2;
[OneTimeSetUp]
public void Setup()
{
var notificationHubClient = new Mock<IHubContext<NotificationHub, INotificationHub>>();
var mockNotificationClients = new Mock<IHubClients<INotificationHub>>();
var mockNotificationGroup = new Mock<INotificationHub>();
var distributionProcessFactory = new Mock<IDistributionProcessFactory>();
notificationHubClient.Setup(h => h.Clients).Returns(mockNotificationClients.Object);
mockNotificationClients.Setup(c => c.Group(It.IsAny<string>())).Returns(mockNotificationGroup.Object);
mockNotificationGroup
.Setup(g => g.SendStrategicInitiativeTrackerSuccess(It.IsAny<string>(), It.IsAny<byte>(), It.IsAny<DateTime?>()))
.Returns(Task.CompletedTask);
mockNotificationGroup
.Setup(g => g.SendStrategicInitiativeTrackerError(It.IsAny<string>(), It.IsAny<byte>(), It.IsAny<DateTime?>()))
.Returns(Task.CompletedTask);
_mockDataSchemaService.Setup(x =>
x.GetDataTableBySqlFullNameAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((string tableName, CancellationToken _) => new DataTable
{
SqlAlias = tableName.Split('.').Last()
});
_mockDataSchemaService.Setup(x =>
x.GetDataColumnsBySqlColumnNameAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
_mockSchemaServiceClient
.Setup(x => x.GetDimensionByGlobalIdAsync("Charge Code", It.IsAny<CancellationToken>()))
.ReturnsAsync(new Schema.Client.Dtos.Info.ScoreDimensionInfoDto { DimensionGuid = ChargeCodeDimensionGuid });
_mockSchemaServiceClient
.Setup(x => x.GetDimensionByGlobalIdAsync(It.Is<string>(s => s != "Charge Code"), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Schema.Client.Dtos.Info.ScoreDimensionInfoDto { DimensionGuid = ClinicalIndicatorDimensionGuid });
_mockSchemaServiceClient
.Setup(x => x.GetDimensionMembersFromHierarchyPathsAsync(
ChargeCodeDimensionGuid,
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync([
new MemberDto { Id = "17993", Name = "39017762 - LAB CAB FEE(FROM BBACCA)*ARC" },
new MemberDto { Id = "17995", Name = "39022140 - LAB FROZEN POOLED CRYO PRODUCT(5)*ARC" },
new MemberDto { Id = "18001", Name = "39022185 - LAB LEUKOFILTERED CELLS 1 UNIT *ARC" },
]);
_mockSchemaServiceClient
.Setup(x => x.GetDimensionMembersFromHierarchyPathsAsync(
ClinicalIndicatorDimensionGuid,
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync([new MemberDto { Id = "188", Name = "All Readmissions" }]);
_mockSchemaServiceClient.Setup(x =>
x.GetHierarchyNodesFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
_strategicOpportunityDetailQueryBuilder = new StrategicOpportunityDetailQueryBuilder();
_strategicOpportunityDetailService = new StrategicChargeCodeOpportunityDetailService(
JazzConnBuilderFactory,
(CentralDbContext)CentralDbContext,
TestUtilities.GetClaimsPrincipalAccessor(),
_mockDataSchemaService.Object,
_mockSchemaServiceClient.Object,
SnowflakeDatabaseContext,
_mockExplorationService.Object,
distributionProcessFactory.Object,
_strategicOpportunityDetailQueryBuilder,
notificationHubClient.Object,
_mockStrategicItemDimensionSyncService.Object,
(Microsoft.Extensions.Logging.ILogger<StrategicChargeCodeOpportunityDetailService>)logger);
}
[TearDown]
public async Task CleanupData()
{
await SnowflakeDatabaseContext.ExecuteCommandAsync(
$"DELETE FROM cci.FactStrategicChargeCodeDetail WHERE OpportunityId IN ({TestOpportunityId}, {TestOpportunityId_Exploration})");
await SnowflakeDatabaseContext.ExecuteCommandAsync(
$"DELETE FROM cci.StrategicOpportunityMeasureDetail WHERE MeasureId IN " +
$"(SELECT MeasureId FROM cci.StrategicOpportunityMeasureDetail WHERE MeasureId >= {TestOpportunityId * 10})");
var centralDb = (CentralDbContext)CentralDbContext;
var measures = centralDb.StrategicOpportunityMeasures
.Where(m => m.OpportunityId == TestOpportunityId || m.OpportunityId == TestOpportunityId_Exploration);
centralDb.StrategicOpportunityMeasures.RemoveRange(measures);
var opportunity = await centralDb.StrategicOpportunityChargeCodes
.FirstOrDefaultAsync(o => o.OpportunityId == TestOpportunityId || o.OpportunityId == TestOpportunityId_Exploration);
if (opportunity != null)
centralDb.StrategicOpportunityChargeCodes.Remove(opportunity);
var explorationPopulation = await centralDb.ExplorationPopulations
.FirstOrDefaultAsync(p => p.ExplorationPopulationId == TestExplorationPopulationId);
if (explorationPopulation != null)
centralDb.ExplorationPopulations.Remove(explorationPopulation);
await centralDb.SaveChangesAsync();
}
// Tests for standard Tracking opportunity (patient population)
[Test, Ignore("Test database needs DS Encounter Cost datasource")]
public async Task PopulateTrackingDetails_WithMeasures_PopulatesMeasureDetailRows()
{
// Arrange
var opportunity = CreateTrackingOpportunity(patientPopulationHPath: "CLINICALINDICATOR|CLINICALINDI|188");
opportunity.Measures =
[
new StrategicOpportunityMeasure
{
MeasureId = TestMeasureId,
OpportunityId = TestOpportunityId,
MeasureType = MeasureType.DirectVariableCost,
MeasureGroup = MeasureGroup.Primary,
FiltersJSON = MeasureFiltersJSON,
StrataId = 1
}
];
await SeedOpportunityInCentralDb(opportunity);
SetupBuildSqlQueryForTracking();
// Act
await _strategicOpportunityDetailService.PopulateTrackingDetails(opportunity, CancellationToken.None);
// Assert - measure detail rows written
var measureRows = await SnowflakeDatabaseContext.QueryAsync<StrategicOpportunityMeasureDetail>(
$"SELECT * FROM cci.StrategicOpportunityMeasureDetail WHERE MeasureId = {opportunity.Measures.First().MeasureId}",
nameof(PopulateTrackingDetails_WithMeasures_PopulatesMeasureDetailRows));
// Assert - verify rows were written to Snowflake
var rows = await SnowflakeDatabaseContext.QueryAsync<StrategicOpportunityDetail>(
$"SELECT * FROM cci.FactStrategicChargeCodeDetail WHERE OpportunityId = {TestOpportunityId} AND IsBaseline = FALSE",
nameof(PopulateTrackingDetails_WithMeasures_PopulatesMeasureDetailRows));
measureRows.Should().NotBeNullOrEmpty();
rows.Should().NotBeNullOrEmpty();
// Assert - AverageValue is written back to CentralDb
var savedMeasure = await ((CentralDbContext)CentralDbContext)
.StrategicOpportunityMeasures
.FirstOrDefaultAsync(m => m.MeasureId == opportunity.Measures.First().MeasureId);
savedMeasure.Should().NotBeNull();
}
[Test, Ignore("Test database needs DS Encounter Cost datasource")]
public async Task PopulateTrackingDetails_WhenStatusIsNotTracking_SkipsDetailPopulationAndSucceeds()
{
// Arrange
var opportunity = CreateTrackingOpportunity(patientPopulationHPath: "CLINICALINDICATOR|CLINICALINDI|188");
opportunity.Status = (byte)StrategicOpportunityStatus.CompleteMonitoring;
opportunity.Measures =
[
new StrategicOpportunityMeasure
{
MeasureId = TestMeasureId,
OpportunityId = TestOpportunityId,
MeasureType = MeasureType.DirectVariableCost,
MeasureGroup = MeasureGroup.Primary,
FiltersJSON = MeasureFiltersJSON,
StrataId = 1
}
];
await SeedOpportunityInCentralDb(opportunity);
SetupBuildSqlQueryForMonitoring();
// Act
await _strategicOpportunityDetailService.PopulateTrackingDetails(opportunity, CancellationToken.None);
// Assert - no detail rows written for non-Tracking status
var rows = await SnowflakeDatabaseContext.QueryAsync<StrategicOpportunityDetail>(
$"SELECT * FROM cci.FactStrategicChargeCodeDetail WHERE OpportunityId = {TestOpportunityId} AND IsBaseline = FALSE",
nameof(PopulateTrackingDetails_WhenStatusIsNotTracking_SkipsDetailPopulationAndSucceeds));
rows.Should().BeEmpty("detail rows should not be populated when status is not Tracking");
// Assert - tracking is still updated
var savedOpportunity = await ((CentralDbContext)CentralDbContext)
.StrategicOpportunityChargeCodes
.FirstOrDefaultAsync(o => o.OpportunityId == TestOpportunityId);
savedOpportunity.LastRunTrackingStatus.Should().Be(RunTrackingStatus.Success);
}
[Test, Ignore("Test database needs DS Encounter Cost datasource")]
public async Task PopulateTrackingDetails_WithMeasures_PopulatesMeasureDetailRows_Exploration()
{
// Arrange
await SeedExplorationPopulationInCentralDb();
var opportunity = CreateExplorationOpportunity();
opportunity.Measures =
[
new StrategicOpportunityMeasure
{
MeasureId = TestMeasureId_Exploration,
OpportunityId = opportunity.OpportunityId,
MeasureType = MeasureType.DirectVariableCost,
MeasureGroup = MeasureGroup.Primary,
FiltersJSON = MeasureFiltersJSON,
StrataId = 1
}
];
await SeedOpportunityInCentralDb(opportunity);
_mockExplorationService
.Setup(x => x.GetFilterStringsAsync(
It.IsAny<IEnumerable<FilterChipItem>>(),
It.IsAny<IQueryParamBase>(),
It.IsAny<CancellationToken>()))
.Callback<IEnumerable<FilterChipItem>, IQueryParamBase, CancellationToken>(
(_, queryParams, _) =>
{
queryParams.DischargeDateStart = "2024-01-01";
queryParams.DischargeDateEnd = "2024-12-31";
})
.ReturnsAsync((
string.Empty,
string.Empty));
SetupBuildSqlQueryForTrackingExploration();
// Act
await _strategicOpportunityDetailService.PopulateTrackingDetails(opportunity, CancellationToken.None);
// Assert - measure detail rows written
var measureRows = await SnowflakeDatabaseContext.QueryAsync<StrategicOpportunityMeasureDetail>(
$"SELECT * FROM cci.StrategicOpportunityMeasureDetail WHERE MeasureId = {opportunity.Measures.First().MeasureId}",
nameof(PopulateTrackingDetails_WithMeasures_PopulatesMeasureDetailRows));
// Assert - verify rows were written to Snowflake
var rows = await SnowflakeDatabaseContext.QueryAsync<StrategicOpportunityDetail>(
$"SELECT * FROM cci.FactStrategicChargeCodeDetail WHERE OpportunityId = {opportunity.OpportunityId} AND IsBaseline = FALSE",
nameof(PopulateTrackingDetails_WithMeasures_PopulatesMeasureDetailRows));
measureRows.Should().NotBeNullOrEmpty();
rows.Should().NotBeNullOrEmpty();
// Assert - AverageValue is written back to CentralDb
var savedMeasure = await ((CentralDbContext)CentralDbContext)
.StrategicOpportunityMeasures
.FirstOrDefaultAsync(m => m.MeasureId == opportunity.Measures.First().MeasureId);
savedMeasure.Should().NotBeNull();
}
private StrategicOpportunityChargeCode CreateTrackingOpportunity(
string patientPopulationHPath,
DateOnly? trackingStart = null,
DateOnly? trackingEnd = null) =>
new()
{
OpportunityId = TestOpportunityId,
PatientPopulationHPath = patientPopulationHPath,
ExplorationPopulationId = 0,
ChargeCodeHPath = "CC|CCRU|Blood",
OpportunityFiltersJSON = "[]",
Status = (byte)StrategicOpportunityStatus.Tracking,
TrackingStartDate = trackingStart ?? new DateOnly(2024, 1, 1),
TrackingEndDate = trackingEnd ?? new DateOnly(2024, 12, 31),
BaselineStartDate = new DateOnly(2023, 1, 1),
BaselineEndDate = new DateOnly(2023, 12, 31),
LastRunTrackingDate = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc),
EstimatedTrackingDuration = 12,
StrataId = 1,
Measures = []
};
private StrategicOpportunityChargeCode CreateExplorationOpportunity(
DateOnly? trackingStart = null,
DateOnly? trackingEnd = null) =>
new()
{
OpportunityId = TestOpportunityId_Exploration,
PatientPopulationHPath = null,
ExplorationPopulationId = TestExplorationPopulationId,
ChargeCodeHPath = "CC|CCRU|Blood",
OpportunityFiltersJSON = "[]",
Status = (byte)StrategicOpportunityStatus.Tracking,
TrackingStartDate = trackingStart ?? new DateOnly(2024, 1, 1),
TrackingEndDate = trackingEnd ?? new DateOnly(2024, 12, 31),
BaselineStartDate = new DateOnly(2023, 1, 1),
BaselineEndDate = new DateOnly(2023, 12, 31),
LastRunTrackingDate = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc),
EstimatedTrackingDuration = 12,
StrataId = 1,
Measures = []
};
private async Task SeedOpportunityInCentralDb(StrategicOpportunityChargeCode opportunity)
{
var centralDb = (CentralDbContext)CentralDbContext;
// Detach any already-tracked instance with the same key to avoid tracking conflicts
var tracked = centralDb.ChangeTracker.Entries<StrategicOpportunityChargeCode>()
.FirstOrDefault(e => e.Entity.OpportunityId == opportunity.OpportunityId);
if (tracked != null)
tracked.State = Microsoft.EntityFrameworkCore.EntityState.Detached;
var existing = await centralDb.StrategicOpportunityChargeCodes
.AsNoTracking()
.FirstOrDefaultAsync(o => o.OpportunityId == opportunity.OpportunityId);
if (existing != null)
{
centralDb.StrategicOpportunityChargeCodes.Remove(existing);
await centralDb.SaveChangesAsync();
}
centralDb.StrategicOpportunityChargeCodes.Add(opportunity);
await centralDb.SaveChangesAsync();
}
private async Task SeedExplorationPopulationInCentralDb()
{
var centralDb = (CentralDbContext)CentralDbContext;
var existing = await centralDb.ExplorationPopulations
.AsNoTracking()
.FirstOrDefaultAsync(p => p.ExplorationPopulationId == TestExplorationPopulationId);
if (existing != null) return;
centralDb.ExplorationPopulations.Add(new Population.ExplorationPopulation
{
ExplorationPopulationId = TestExplorationPopulationId,
Name = "Test Exploration Population",
CaseTypeFamilyId = 8016,
FiltersJSON = $@"[{{
""key"": 25,
""chipType"": {(byte)ChipType.DateRange},
""operator"": 0,
""dateRange"": [""{new DateTime(2024, 1, 1):yyyy-MM-dd}"", ""{new DateTime(2024, 12, 31):yyyy-MM-dd}""],
""filters"": [],
""isSelected"": null
}}]",
StrataId = 1
});
await centralDb.SaveChangesAsync();
}
private void SetupBuildSqlQueryForTracking()
{
_mockDataSchemaService.SetupSequence(x =>
x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SqlResponse
{
SqlQuery = StrategicOpportunityQueryConstants.TrackingSelectSql,
Parameters =
[
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
new(":clinicalindicatorid_2", 188),
new(":dischargedatetime_Start_3", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_4", new DateTime(2024, 12, 31)),
new(":clinicalindicatorid_5", 188),
]
})
.ReturnsAsync(new SqlResponse
{
SqlQuery = StrategicOpportunityQueryConstants.MeasureSelectSql,
Parameters =
[
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
]
});
}
private void SetupBuildSqlQueryForTrackingExploration()
{
_mockDataSchemaService.SetupSequence(x =>
x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SqlResponse
{
SqlQuery = StrategicOpportunityQueryConstants.ExplorationTrackingSelectSql,
Parameters =
[
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
new(":explorationpopulationid_2", TestExplorationPopulationId),
new(":dischargedatetime_Start_3", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_4", new DateTime(2024, 12, 31)),
new(":explorationpopulationid_5", TestExplorationPopulationId),
]
})
.ReturnsAsync(new SqlResponse
{
SqlQuery = StrategicOpportunityQueryConstants.ExplorationMeasureSelectSql,
Parameters =
[
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
new(":explorationpopulationid_2", TestExplorationPopulationId),
]
});
}
private void SetupBuildSqlQueryForMonitoring()
{
_mockDataSchemaService.Setup(x =>
x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SqlResponse
{
SqlQuery = StrategicOpportunityQueryConstants.MeasureSelectSql,
Parameters =
[
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
]
});
}
}
}
@@ -0,0 +1,94 @@
using FluentAssertions;
using NUnit.Framework;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
/// <summary>
/// Validates that the SQL generated by StrategicOpportunityGLDetailQueryBuilder
/// is syntactically correct and references valid objects in Snowflake.
/// Uses EXPLAIN to parse and plan each statement without executing DML.
/// </summary>
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class StrategicOpportunityGLQueryBuilderIntegrationTests : IntegrationTestBase
{
private StrategicOpportunityGLDetailQueryBuilder _queryBuilder;
private static readonly List<int> DepartmentIds = [1];
private static readonly List<int> RevenueAccountIds = [1];
private static readonly List<int> ExpenseAccountIds = [2];
private static readonly List<int> StatisticAccountIds = [3];
private static readonly DateOnly BaselineStart = new(2024, 1, 1);
private static readonly DateOnly BaselineEnd = new(2024, 12, 31);
[OneTimeSetUp]
public void Setup()
{
_queryBuilder = new StrategicOpportunityGLDetailQueryBuilder();
}
[Test]
public async Task BuildInsertQuery_FwTable_IsValidSnowflakeSql()
{
var query = _queryBuilder.BuildInsertQuery(
opportunityId: 0,
sourceTable: "fw.factglsampled",
departmentIds: DepartmentIds,
revenueAccountIds: RevenueAccountIds,
expenseAccountIds: ExpenseAccountIds,
statisticAccountIds: StatisticAccountIds,
baselineStart: BaselineStart,
baselineEnd: BaselineEnd);
var act = async () => await SnowflakeDatabaseContext.QueryAsync<dynamic>($"EXPLAIN {query}", nameof(BuildInsertQuery_FwTable_IsValidSnowflakeSql));
await act.Should().NotThrowAsync("the generated INSERT SQL should be valid Snowflake syntax");
}
[Test]
public async Task BuildInsertQuery_IntTable_IsValidSnowflakeSql()
{
var query = _queryBuilder.BuildInsertQuery(
opportunityId: 0,
sourceTable: "int.factgl",
departmentIds: DepartmentIds,
revenueAccountIds: RevenueAccountIds,
expenseAccountIds: ExpenseAccountIds,
statisticAccountIds: StatisticAccountIds,
baselineStart: BaselineStart,
baselineEnd: BaselineEnd);
var act = async () => await SnowflakeDatabaseContext.QueryAsync<dynamic>($"EXPLAIN {query}", nameof(BuildInsertQuery_IntTable_IsValidSnowflakeSql));
await act.Should().NotThrowAsync("the generated INSERT SQL should be valid Snowflake syntax");
}
[Test]
public async Task BuildMergeQuery_FwTable_IsValidSnowflakeSql()
{
var query = _queryBuilder.BuildMergeQuery(
opportunityId: 0,
sourceTable: "fw.factglsampled",
departmentIds: DepartmentIds,
revenueAccountIds: RevenueAccountIds,
expenseAccountIds: ExpenseAccountIds,
statisticAccountIds: StatisticAccountIds,
trackingStart: new DateOnly(2025, 1, 1),
trackingEnd: new DateOnly(2025, 12, 31));
var act = async () => await SnowflakeDatabaseContext.QueryAsync<dynamic>($"EXPLAIN {query}", nameof(BuildMergeQuery_FwTable_IsValidSnowflakeSql));
await act.Should().NotThrowAsync("the generated MERGE SQL should be valid Snowflake syntax");
}
[Test]
public async Task BuildDeleteQuery_WithOpportunityId_IsValidSnowflakeSql()
{
var query = _queryBuilder.BuildDeleteQuery(opportunityId: 0);
var act = async () => await SnowflakeDatabaseContext.QueryAsync<dynamic>($"EXPLAIN {query}", nameof(BuildDeleteQuery_WithOpportunityId_IsValidSnowflakeSql));
await act.Should().NotThrowAsync("the generated DELETE SQL should be valid Snowflake syntax");
}
}
}
@@ -0,0 +1,411 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
public static class StrategicOpportunityQueryConstants
{
public static readonly string TrackingSelectSql = @"SELECT
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup"",
ZEROIFNULL(""Units"") AS ""Units"",
ZEROIFNULL(""Cost"") AS ""Cost""
FROM (
SELECT
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup"",
SUM(""Units"") AS ""Units"",
SUM(""Cost"") AS ""Cost""
FROM (
SELECT
DSSPES.encounterid AS ""dsspesEncounterId"",
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
DSSPES.entityid AS ""dsspesEntityID"",
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
PBLID.departmentid AS ""pblidDepartmentId"",
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
CC.chargecodeid AS ""ccChargeCodeID"",
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
NULL AS ""Cost"",
SUM(PBLID.unitsofservice) AS ""Units""
FROM dss.FactPatientBillingLineItemDetail AS PBLID
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
LEFT JOIN dss.FactPatientEncounterClinicalIndicator AS DSSPECI ON DSSPECI.encounterid = PBLID.encounterid
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND DSSPECI.clinicalindicatorid = :clinicalindicatorid_2)
GROUP BY
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup""
UNION ALL
SELECT
DSSPES.encounterid AS ""dsspesEncounterId"",
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
DSSPES.entityid AS ""dsspesEntityID"",
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
PBLID.departmentid AS ""pblidDepartmentId"",
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
CC.chargecodeid AS ""ccChargeCodeID"",
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
SUM(COSTDETAIL.variabledirectcost) AS ""Cost"",
NULL AS ""Units""
FROM dss.FactPatientCostDetail AS COSTDETAIL
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
LEFT JOIN dss.FactPatientEncounterClinicalIndicator AS DSSPECI ON DSSPECI.encounterid = PBLID.encounterid
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_3 AND DSSPES.dischargedatetime <= :dischargedatetime_End_4) AND DSSPECI.clinicalindicatorid = :clinicalindicatorid_5)
GROUP BY
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup""
)
GROUP BY
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup""
)
ORDER BY
""dsspesEncounterId"" ASC NULLS FIRST,
""dsspesDischargeDateTime"" ASC NULLS FIRST,
""dsspesEntityID"" ASC NULLS FIRST,
""dsspesPhysicianId"" ASC NULLS FIRST,
""dsspesPrimaryServiceLineId"" ASC NULLS FIRST,
""dsspesCPTCodeCaseTypeFamilyId"" ASC NULLS FIRST,
""dsspesMSDRGCaseTypeFamilyId"" ASC NULLS FIRST,
""dsspesAPRDRGCaseTypeFamilyId"" ASC NULLS FIRST,
""pblidDepartmentId"" ASC NULLS FIRST,
""physPrimaryPhysicianSpecialtyId"" ASC NULLS FIRST,
""ccChargeCodeID"" ASC NULLS FIRST,
""ccCostDriver"" ASC NULLS FIRST,
""encounterPatientTypeRollupPatientTypeRollup"" ASC NULLS FIRST
";
// SQL returned by BuildSqlQuery for the measure detail query (PopulateMeasureDetails, line 215).
// Uses real column names from clientdss.FactPatientEncounterSummary and dss.FactPatientBillingLineItemDetail
// that exist in the test client's Snowflake database.
public static readonly string MeasureSelectSql = @"SELECT
""DischargeDateTime"",
ZEROIFNULL(""Value"") AS ""Value""
FROM (
SELECT
""DischargeDateTime"",
SUM(""Value"") AS ""Value""
FROM (
SELECT
DSSPES.dischargedatetime AS ""DischargeDateTime"",
SUM(PBLID.unitsofservice) AS ""Value""
FROM dss.FactPatientBillingLineItemDetail AS PBLID
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
WHERE
(DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1)
GROUP BY
""DischargeDateTime""
)
GROUP BY
""DischargeDateTime""
)
ORDER BY
""DischargeDateTime"" ASC NULLS FIRST
";
// SQL for the tracking merge when opportunity uses an ExplorationPopulation instead of PatientPopulationHPath.
// Contains LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER which ApplyExplorationFilters replaces
// with an INNER JOIN subquery at runtime.
public static readonly string ExplorationTrackingSelectSql = @"SELECT
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup"",
ZEROIFNULL(""Units"") AS ""Units"",
ZEROIFNULL(""Cost"") AS ""Cost""
FROM (
SELECT
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup"",
SUM(""Units"") AS ""Units"",
SUM(""Cost"") AS ""Cost""
FROM (
SELECT
DSSPES.encounterid AS ""dsspesEncounterId"",
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
DSSPES.entityid AS ""dsspesEntityID"",
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
PBLID.departmentid AS ""pblidDepartmentId"",
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
CC.chargecodeid AS ""ccChargeCodeID"",
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
NULL AS ""Cost"",
SUM(PBLID.unitsofservice) AS ""Units""
FROM dss.FactPatientBillingLineItemDetail AS PBLID
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_2)
GROUP BY
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup""
UNION ALL
SELECT
DSSPES.encounterid AS ""dsspesEncounterId"",
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
DSSPES.entityid AS ""dsspesEntityID"",
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
PBLID.departmentid AS ""pblidDepartmentId"",
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
CC.chargecodeid AS ""ccChargeCodeID"",
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
SUM(COSTDETAIL.variabledirectcost) AS ""Cost"",
NULL AS ""Units""
FROM dss.FactPatientCostDetail AS COSTDETAIL
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_3 AND DSSPES.dischargedatetime <= :dischargedatetime_End_4) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_5)
GROUP BY
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup""
)
GROUP BY
""dsspesEncounterId"",
""dsspesDischargeDateTime"",
""dsspesEntityID"",
""dsspesPhysicianId"",
""dsspesPrimaryServiceLineId"",
""dsspesCPTCodeCaseTypeFamilyId"",
""dsspesMSDRGCaseTypeFamilyId"",
""dsspesAPRDRGCaseTypeFamilyId"",
""pblidDepartmentId"",
""physPrimaryPhysicianSpecialtyId"",
""ccChargeCodeID"",
""ccCostDriver"",
""encounterPatientTypeRollupPatientTypeRollup""
)
ORDER BY
""dsspesEncounterId"" ASC NULLS FIRST,
""dsspesDischargeDateTime"" ASC NULLS FIRST,
""dsspesEntityID"" ASC NULLS FIRST,
""dsspesPhysicianId"" ASC NULLS FIRST,
""dsspesPrimaryServiceLineId"" ASC NULLS FIRST,
""dsspesCPTCodeCaseTypeFamilyId"" ASC NULLS FIRST,
""dsspesMSDRGCaseTypeFamilyId"" ASC NULLS FIRST,
""dsspesAPRDRGCaseTypeFamilyId"" ASC NULLS FIRST,
""pblidDepartmentId"" ASC NULLS FIRST,
""physPrimaryPhysicianSpecialtyId"" ASC NULLS FIRST,
""ccChargeCodeID"" ASC NULLS FIRST,
""ccCostDriver"" ASC NULLS FIRST,
""encounterPatientTypeRollupPatientTypeRollup"" ASC NULLS FIRST
";
// SQL for exploration-path tests (instead of clinical indicator)
public static readonly string ExplorationMeasureSelectSql = @"SELECT
""DischargeDateTime"",
ZEROIFNULL(""Value"") AS ""Value""
FROM (
SELECT
""DischargeDateTime"",
SUM(""Value"") AS ""Value""
FROM (
SELECT
DSSPES.dischargedatetime AS ""DischargeDateTime"",
SUM(PBLID.unitsofservice) AS ""Value""
FROM dss.FactPatientBillingLineItemDetail AS PBLID
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_2)
GROUP BY
""DischargeDateTime""
)
GROUP BY
""DischargeDateTime""
)
ORDER BY
""DischargeDateTime"" ASC NULLS FIRST
";
}
}
@@ -0,0 +1,653 @@
using FluentAssertions;
using Hangfire;
using Microsoft.EntityFrameworkCore;
using Moq;
using Strata.ContinuousImprovement.Biz.Generics;
using Strata.ContinuousImprovement.Biz.InitiativeRollupColumns;
using Strata.ContinuousImprovement.Biz.OpportunityRoleAssignment;
using Strata.ContinuousImprovement.Biz.OpportunityWorkbooks;
using Strata.ContinuousImprovement.Biz.Security;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
using Strata.CoreLib.Claims.Extensions;
using Strata.DataSchema.Client;
using Strata.DataSchema.Models.Query;
using Strata.DataSchema.Models.Schema;
using Strata.Id.Client;
using Strata.Schema.Client;
using Strata.Schema.Client.Dtos;
using System.Diagnostics.CodeAnalysis;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
[ExcludeFromCodeCoverage]
[Ignore("temporarily ignoring because schema is out of sync")]
[TestFixture, Category("Integration"), Category("CCI")]
public class StrategicOpportunityServiceTests : IntegrationTestBase
{
private const string POPULATION_NAME = "Mocked Population Name";
private IStrategicOpportunityService _strategicOpportunityService;
private IOpportunityRoleAssignmentService _opportunityRoleAssignmentService;
[OneTimeSetUp]
public void Setup()
{
// Arrange mocks
var mockSchemaServiceClient = new Mock<ISchemaServiceClient>();
var mockDataSchemaService = new Mock<IDataSchemaService>();
var mockIdServiceClient = new Mock<IIdServiceClient>();
var mockIdRollupColumn = new Mock<IInitiativeRollupColumnService>();
var mockOpportunityWorkbookService = new Mock<IOpportunityWorkbookService>();
var mockStrategicOpportunityDetailService = new Mock<IStrategicChargeCodeOpportunityDetailService>();
var mockStrategicGLOpportunityDetailService = new Mock<IStrategicGLOpportunityDetailService>();
var mockSimpleSecurityServiceClient = new Mock<ISimpleSecurityService>();
var mockOpportunityRoleAssignmentServiceClient = new Mock<IOpportunityRoleAssignmentService>();
var mockBackgroundJobClient = new Mock<IBackgroundJobClient>();
var mockDistributionProcessFactory = new Mock<IDistributionProcessFactory>();
// Add mock responses
mockDataSchemaService.Setup(x =>
x.GetDataTableBySqlFullNameAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new DataTable());
mockDataSchemaService.Setup(x =>
x.GetDataColumnsBySqlColumnNameAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
mockDataSchemaService.Setup(x =>
x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SqlResponse
{
SqlQuery = DataSchemaServiceSelectSql,
Parameters = new List<KeyValuePair<string, object>>
{
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
new(":chargecodeid_2", 3541)
}
});
mockSchemaServiceClient.Setup(x =>
x.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Schema.Client.Dtos.Info.ScoreDimensionInfoDto());
var list = new List<HierarchyNodeDto>();
list.Add(new HierarchyNodeDto
{
Path = "CLINICALINDICATOR|CLINICALINDI|188",
Name = POPULATION_NAME
});
mockSchemaServiceClient.Setup(x =>
x.GetHierarchyNodesFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(list);
mockSchemaServiceClient.Setup(x =>
x.GetDimensionMembersFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<MemberDto> {
new MemberDto
{
Id = "188",
Name = "All Readmissions"
}});
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
_opportunityRoleAssignmentService = new OpportunityRoleAssignmentService(
(DbContexts.CentralDbContext)CentralDbContext,
mockIdServiceClient.Object,
mockSimpleSecurityServiceClient.Object,
claimsPrincipalAccessor
);
_strategicOpportunityService = new StrategicOpportunityService(
JazzConnBuilderFactory,
(DbContexts.CentralDbContext)CentralDbContext,
claimsPrincipalAccessor,
mockSchemaServiceClient.Object,
SnowflakeDatabaseContext,
mockIdServiceClient.Object,
_opportunityRoleAssignmentService,
mockIdRollupColumn.Object,
mockOpportunityWorkbookService.Object,
mockBackgroundJobClient.Object,
mockStrategicOpportunityDetailService.Object,
new StrategicItemDimensionSyncService(JazzConnBuilderFactory, (DbContexts.CentralDbContext)CentralDbContext, SnowflakeDatabaseContext),
mockStrategicGLOpportunityDetailService.Object,
(Microsoft.Extensions.Logging.ILogger<StrategicOpportunityService>)logger);
}
[Test, Ignore("temporarily ignoring because schema is out of sync")]
public async Task CreatingOpportunityShouldAlsoCreateDetails()
{
//arrange
var opportunity = CreateOpportunityDto();
//act
var result = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
//assert
result.Should().Be(1, "On success should return opportunity id.");
}
private static StrategicOpportunityChargeCodeDto CreateOpportunityDto()
{
var opportunity = new StrategicOpportunityChargeCodeDto()
{
Name = "Integration-StrategicOpportunityChargeCode",
OpportunityType = StrategicOpportunityTypes.ChargeCode,
OpportunityFiltersJSON = "[]",
Rollup1Id = 0,
Rollup2Id = 0,
Rollup3Id = 0,
BaselineType = BaselineType.MonthRange,
BaselineDistribution = DistributionMethod.Average,
BaselineStartDate = new DateOnly(2024, 1, 1),
BaselineEndDate = new DateOnly(2024, 12, 31),
EstimatedTrackingDuration = 12,
// Assume empty or test collections for these
Measures = Enumerable.Empty<StrategicOpportunityMeasureDto>(),
WorkbookIds = Enumerable.Empty<int>(),
// ChargeCode-specific properties
PatientPopulationHPath = "CLINICALINDICATOR|CLINICALINDI|188",
ExplorationPopulationId = 0,
ChargeCodeHPath = "CC|CCRU|Blood"
};
return opportunity;
}
[Test]
public async Task UpdateStrategicOpportunityRampUpsAsync_ShouldUpdateRampUps()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var rampUps = new List<StrategicOpportunityRampUpDto>
{
new() { OpportunityId = opportunityId, MonthNumber = 1, Percentage = 0.1 },
new() { OpportunityId = opportunityId, MonthNumber = 2, Percentage = 0.2 }
};
// Act
var updateResult = await _strategicOpportunityService.UpdateStrategicOpportunityRampUpsAsync(opportunityId, rampUps, CancellationToken.None);
// Assert
updateResult.Should().BeTrue();
// Verify persisted values
var retrieved = await _strategicOpportunityService.GetStrategicOpportunityRampUpsAsync(opportunityId, CancellationToken.None);
retrieved.Should().HaveCount(rampUps.Count);
retrieved[0].MonthNumber.Should().Be(rampUps[0].MonthNumber);
retrieved[0].Percentage.Should().Be(rampUps[0].Percentage);
retrieved[1].MonthNumber.Should().Be(rampUps[1].MonthNumber);
retrieved[1].Percentage.Should().Be(rampUps[1].Percentage);
}
[Test]
public async Task ResetStrategicOpportunityRampUps_ShouldResetRampUps()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var rampUps = new List<StrategicOpportunityRampUpDto>
{
new() { OpportunityId = opportunityId, MonthNumber = 1, Percentage = 0.1 },
new() { OpportunityId = opportunityId, MonthNumber = 2, Percentage = 0.2 }
};
await _strategicOpportunityService.UpdateStrategicOpportunityRampUpsAsync(opportunityId, rampUps, CancellationToken.None);
// Act
var resetResult = await _strategicOpportunityService.ResetStrategicOpportunityRampUps(opportunityId, CancellationToken.None);
// Assert
resetResult.Should().BeTrue();
var retrieved = await _strategicOpportunityService.GetStrategicOpportunityRampUpsAsync(opportunityId, CancellationToken.None);
// Verify persisted values
retrieved.Should().HaveCount(rampUps.Count);
retrieved[0].Percentage.Should().Be(1);
retrieved[1].Percentage.Should().Be(1);
}
[Test]
public async Task ResetStrategicOpportunityRampUps_ShouldNotResetRampUps()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
// Act
var resetResult = await _strategicOpportunityService.ResetStrategicOpportunityRampUps(opportunityId, CancellationToken.None);
// Assert
resetResult.Should().BeFalse();
}
[Test]
public async Task UpdateStrategicOpportunityRoleAssignment_ShouldUpdateRoleAssignments()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var roleAssignments = new List<OpportunityRoleAssignmentView>()
{
new OpportunityRoleAssignmentView() { LootId = opportunityId, IdentityGuid = Guid.NewGuid(), IdentityName = "Test", CanRead = true, CanWrite = true, CanSecure = false }
};
// Act
var saveResult = await _opportunityRoleAssignmentService.SaveRoleAssignmentsAsync(opportunityId, roleAssignments, CancellationToken.None);
// Assert
saveResult.Should().BeTrue();
// Verify persisted values
var retrieved = await _opportunityRoleAssignmentService.GetRoleAssignmentsAsync(opportunityId, CancellationToken.None);
var userPermission = await _opportunityRoleAssignmentService.GetUserRoleAssignmentsAsync(opportunityId, CancellationToken.None);
retrieved.Should().HaveCount(roleAssignments.Count);
var item = retrieved.FirstOrDefault();
item.LootId.Should().Be(opportunityId);
item.IdentityGuid.Should().Be(roleAssignments[0].IdentityGuid);
item.IdentityName.Should().Be(roleAssignments[0].IdentityName);
item.CanRead.Should().Be(roleAssignments[0].CanRead);
item.CanWrite.Should().Be(roleAssignments[0].CanWrite);
item.CanSecure.Should().Be(roleAssignments[0].CanSecure);
}
[Test]
public async Task GetStrategicOpportunityRampUpsAsync_ShouldReturnEmptyListIfNoneExist()
{
// Arrange
var nonExistentOpportunityId = long.MaxValue;
// Act
var rampUps = await _strategicOpportunityService.GetStrategicOpportunityRampUpsAsync(nonExistentOpportunityId, CancellationToken.None);
// Assert
rampUps.Should().NotBeNull();
rampUps.Should().BeEmpty();
}
[Test]
public async Task GetStrategicOpportunitiesAsync_ShouldReturnOpportunitiesWithRollupsAndPopulation()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
// Act
var results = await _strategicOpportunityService.GetStrategicOpportunitiesAsync(CancellationToken.None);
// Assert
results.Should().NotBeEmpty("At least one opportunity should be returned");
var createdOpportunity = results.FirstOrDefault(o => o.OpportunityId == opportunityId);
createdOpportunity.Should().NotBeNull("Created opportunity should be found in results");
createdOpportunity.OpportunityId.Should().Be(opportunityId);
createdOpportunity.Name.Should().Be(opportunity.Name);
createdOpportunity.OpportunityTypeName.Should().Be("Charge Code");
createdOpportunity.EncounterGroupName.Should().Be("Patient Population", "When PatientPopulationHPath is set, it should be Patient Population");
createdOpportunity.AdditionalFilters.Should().Be("No");
createdOpportunity.BaselinePeriod.Should()
.Be($"{opportunity.BaselineStartDate:MMM d, yyyy} - {opportunity.BaselineEndDate:MMM d, yyyy}");
createdOpportunity.EstimatedTrackDurationMonths.Should().Be(opportunity.EstimatedTrackingDuration);
createdOpportunity.Rollup1.Should().BeEmpty("When rollup1Id is 0, rollup should be empty");
createdOpportunity.Rollup2.Should().BeEmpty("When rollup2Id is 0, rollup should be empty");
createdOpportunity.Rollup3.Should().BeEmpty("When rollup3Id is 0, rollup should be empty");
createdOpportunity.PatientPopulationHPath.Should().Be(opportunity.PatientPopulationHPath);
createdOpportunity.ExplorationPopulationId.Should().Be(opportunity.ExplorationPopulationId);
createdOpportunity.Population.Should().Be(POPULATION_NAME);
}
[Test]
public async Task UpdateStrategicOpportunityStatus_WhenNewInitiative_ShouldUpdateStatusAndDates()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var updateStatusDto = new StrategicOpportunityUpdateStatusDto
{
Status = (byte)StrategicOpportunityStatus.NewInitiative,
TrackingStartDate = new DateOnly(2024, 1, 1),
TrackingEndDate = new DateOnly(2024, 12, 31)
};
// Act
var updateResult = await _strategicOpportunityService.UpdateStrategicOpportunityStatus(opportunityId, updateStatusDto, CancellationToken.None);
// Assert
updateResult.Should().BeTrue("Update should be successful");
var strataId = TestUtilities.GetClaimsPrincipalAccessor()?.GetCurrentClaimsPrincipal().GetStrataId();
var opportunityAfterUpdate = await CentralDbContext.StrategicOpportunities
.FirstOrDefaultAsync(x => x.OpportunityId == opportunityId && x.StrataId == strataId, CancellationToken.None);
opportunityAfterUpdate.Should().NotBeNull("Updated opportunity should exist");
opportunityAfterUpdate.Status.Should().Be(updateStatusDto.Status, "Status should be updated to NewInitiative");
opportunityAfterUpdate.TrackingStartDate.Should().Be(updateStatusDto.TrackingStartDate, "TrackingStartDate should be updated");
opportunityAfterUpdate.TrackingEndDate.Should().Be(updateStatusDto.TrackingEndDate, "TrackingEndDate should be updated");
}
[Test]
public async Task UpdateStrategicOpportunityStatus_WhenNewOpportunity_ShouldResetTrackingData()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var toInitiativeDto = new StrategicOpportunityUpdateStatusDto
{
Status = (byte)StrategicOpportunityStatus.NewInitiative,
TrackingStartDate = new DateOnly(2024, 1, 1),
TrackingEndDate = new DateOnly(2024, 12, 31)
};
await _strategicOpportunityService.UpdateStrategicOpportunityStatus(opportunityId, toInitiativeDto, CancellationToken.None);
var toOpportunityDto = new StrategicOpportunityUpdateStatusDto
{
Status = (byte)StrategicOpportunityStatus.NewOpportunity
};
// Act
var updateResult = await _strategicOpportunityService.UpdateStrategicOpportunityStatus(opportunityId, toOpportunityDto, CancellationToken.None);
// Assert
updateResult.Should().BeTrue("Update should be successful");
var strataId = TestUtilities.GetClaimsPrincipalAccessor()?.GetCurrentClaimsPrincipal().GetStrataId();
var opportunityAfterUpdate = await CentralDbContext.StrategicOpportunityChargeCodes
.FirstOrDefaultAsync(x => x.OpportunityId == opportunityId && x.StrataId == strataId, CancellationToken.None);
opportunityAfterUpdate.Should().NotBeNull("Updated opportunity should exist");
opportunityAfterUpdate.Status.Should().Be((byte)StrategicOpportunityStatus.NewOpportunity, "Status should be reset to NewOpportunity");
opportunityAfterUpdate.TrackingStartDate.Should().Be(default(DateOnly), "TrackingStartDate should be reset to default");
opportunityAfterUpdate.TrackingEndDate.Should().Be(default(DateOnly), "TrackingEndDate should be reset to default");
opportunityAfterUpdate.LastRunTrackingDate.Should().BeNull("LastRunTrackingDate should be cleared");
opportunityAfterUpdate.LastRunTrackingStatus.Should().Be(RunTrackingStatus.None, "LastRunTrackingStatus should be reset to None");
opportunityAfterUpdate.ProgressToDateActual.Should().Be(0, "ProgressToDateActual should be reset to 0");
opportunityAfterUpdate.ProgressToDateCommitted.Should().Be(0, "ProgressToDateCommitted should be reset to 0");
}
[Test]
public async Task UpdateStrategicOpportunityStatus_WhenOtherStatus_ShouldOnlyUpdateStatus()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var toInitiativeDto = new StrategicOpportunityUpdateStatusDto
{
Status = (byte)StrategicOpportunityStatus.NewInitiative,
TrackingStartDate = new DateOnly(2024, 1, 1),
TrackingEndDate = new DateOnly(2024, 12, 31)
};
await _strategicOpportunityService.UpdateStrategicOpportunityStatus(opportunityId, toInitiativeDto, CancellationToken.None);
var toInValidationDto = new StrategicOpportunityUpdateStatusDto
{
Status = (byte)StrategicOpportunityStatus.InValidation
};
// Act
var updateResult = await _strategicOpportunityService.UpdateStrategicOpportunityStatus(opportunityId, toInValidationDto, CancellationToken.None);
// Assert
updateResult.Should().BeTrue("Update should be successful");
var strataId = TestUtilities.GetClaimsPrincipalAccessor()?.GetCurrentClaimsPrincipal().GetStrataId();
var opportunityAfterUpdate = await CentralDbContext.StrategicOpportunityChargeCodes
.FirstOrDefaultAsync(x => x.OpportunityId == opportunityId && x.StrataId == strataId, CancellationToken.None);
opportunityAfterUpdate.Should().NotBeNull("Updated opportunity should exist");
opportunityAfterUpdate.Status.Should().Be((byte)StrategicOpportunityStatus.InValidation, "Status should be updated to InValidation");
opportunityAfterUpdate.TrackingStartDate.Should().Be(toInitiativeDto.TrackingStartDate, "TrackingStartDate should be unchanged");
opportunityAfterUpdate.TrackingEndDate.Should().Be(toInitiativeDto.TrackingEndDate, "TrackingEndDate should be unchanged");
}
[Test]
public async Task UpdateStrategicOpportunityStatus_WhenOpportunityNotFound_ShouldReturnFalse()
{
// Arrange
var nonExistentOpportunityId = long.MaxValue;
var updateStatusDto = new StrategicOpportunityUpdateStatusDto
{
Status = (byte)StrategicOpportunityStatus.NewOpportunity
};
// Act
var result = await _strategicOpportunityService.UpdateStrategicOpportunityStatus(nonExistentOpportunityId, updateStatusDto, CancellationToken.None);
// Assert
result.Should().BeFalse("Update should return false when the opportunity does not exist");
}
[Test]
public async Task CopyStrategicOpportunityRoleAssignments_ShouldReturnEmptyList()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
// Act
var newOpportunityId = opportunityId + 1;
await _opportunityRoleAssignmentService.CopyTo(opportunityId, newOpportunityId, CancellationToken.None);
// Assert
var newRoles = await _opportunityRoleAssignmentService.GetRoleAssignmentsAsync(newOpportunityId, CancellationToken.None);
newRoles.Should().BeEmpty();
}
[Test]
public async Task CopyStrategicOpportunityRoleAssignments_ShouldReturnRoleAssignments()
{
// Arrange
var opportunity = CreateOpportunityDto();
var opportunityId = await _strategicOpportunityService.CreateOpportunityChargeCodeAsync(opportunity, CancellationToken.None);
var roleAssignments = new List<OpportunityRoleAssignmentView>()
{
new() { LootId = opportunityId, IdentityGuid = Guid.NewGuid(), IdentityName = "Test1", CanRead = true, CanWrite = false, CanSecure = true },
new() { LootId = opportunityId, IdentityGuid = Guid.NewGuid(), IdentityName = "Test2", CanRead = false, CanWrite = true, CanSecure = false },
};
await _opportunityRoleAssignmentService.SaveRoleAssignmentsAsync(opportunityId, roleAssignments, CancellationToken.None);
// Act
var newOpportunityId = opportunityId + 1;
await _opportunityRoleAssignmentService.CopyTo(opportunityId, newOpportunityId, CancellationToken.None);
// Assert
var newRoles = await _opportunityRoleAssignmentService.GetRoleAssignmentsAsync(newOpportunityId, CancellationToken.None);
// Verify persisted values
var retrieved = newRoles.ToList();
retrieved.Should().HaveCount(roleAssignments.Count);
CompareRoleAssignments(retrieved[0], roleAssignments[0]);
CompareRoleAssignments(retrieved[1], roleAssignments[1]);
}
private static void CompareRoleAssignments(OpportunityRoleAssignmentView item1, OpportunityRoleAssignmentView item2)
{
item1.IdentityGuid.Should().Be(item2.IdentityGuid);
item1.IdentityName.Should().Be(item2.IdentityName);
item1.CanRead.Should().Be(item2.CanRead);
item1.CanWrite.Should().Be(item2.CanWrite);
item1.CanSecure.Should().Be(item2.CanSecure);
}
public string DataSchemaServiceSelectSql = @"SELECT
""EncounterId"",
""DischargeDateTime"",
""EntityID"",
""PhysicianId"",
""PrimaryServiceLineId"",
""CPTCodeCaseTypeFamilyId"",
""MSDRGCaseTypeFamilyId"",
""APRDRGCaseTypeFamilyId"",
""DepartmentId"",
""PhysicianSpecialtyId"",
""ChargeCodeID"",
""CostDriver"",
""PatientTypeRollup"",
ZEROIFNULL(""Units"") AS ""Units"",
ZEROIFNULL(""Cost"") AS ""Cost""
FROM (
SELECT
""EncounterId"",
""DischargeDateTime"",
""EntityID"",
""PhysicianId"",
""PrimaryServiceLineId"",
""CPTCodeCaseTypeFamilyId"",
""MSDRGCaseTypeFamilyId"",
""APRDRGCaseTypeFamilyId"",
""DepartmentId"",
""PhysicianSpecialtyId"",
""ChargeCodeID"",
""CostDriver"",
""PatientTypeRollup"",
SUM(""Units"") AS ""Units"",
SUM(""Cost"") AS ""Cost""
FROM (
SELECT
DSSPES.encounterid AS ""EncounterId"",
DSSPES.dischargedatetime AS ""DischargeDateTime"",
DSSPES.entityid AS ""EntityID"",
DSSPES.primaryphysicianid AS ""PhysicianId"",
DSSPES.servicelineid AS ""PrimaryServiceLineId"",
DSSPES.cptcodecasetypefamilyid AS ""CPTCodeCaseTypeFamilyId"",
DSSPES.msdrgcasetypefamilyid AS ""MSDRGCaseTypeFamilyId"",
DSSPES.aprdrgcasetypefamilyid AS ""APRDRGCaseTypeFamilyId"",
PBLID.departmentid AS ""DepartmentId"",
PHYSPRIMARY.physicianspecialtyid AS ""PhysicianSpecialtyId"",
CC.chargecodeid AS ""ChargeCodeID"",
IFNULL(CC.costdriver::TEXT, '') AS ""CostDriver"",
IFNULL(EncounterPatientTypeRollup.patienttyperollup::TEXT, '') AS ""PatientTypeRollup"",
NULL AS ""Cost"",
SUM(PBLID.unitsofservice) AS ""Units""
FROM dss.FactPatientBillingLineItemDetail AS PBLID
INNER JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterrecordnumber = PBLID.encounterrecordnumber
LEFT JOIN dss.FactPatientEncounterClinicalIndicator AS DSSPECI ON DSSPECI.encounterid = PBLID.encounterid
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS EncounterPatientTypeRollup ON EncounterPatientTypeRollup.encounterid = PBLID.encounterid
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND CC.chargecodeid IN (:chargecodeid_2))
GROUP BY
""EncounterId"",
""DischargeDateTime"",
""EntityID"",
""PhysicianId"",
""PrimaryServiceLineId"",
""CPTCodeCaseTypeFamilyId"",
""MSDRGCaseTypeFamilyId"",
""APRDRGCaseTypeFamilyId"",
""DepartmentId"",
""PhysicianSpecialtyId"",
""ChargeCodeID"",
""CostDriver"",
""PatientTypeRollup""
UNION ALL
SELECT
DSSPES.encounterid AS ""EncounterId"",
DSSPES.dischargedatetime AS ""DischargeDateTime"",
DSSPES.entityid AS ""EntityID"",
DSSPES.primaryphysicianid AS ""PhysicianId"",
DSSPES.servicelineid AS ""PrimaryServiceLineId"",
DSSPES.cptcodecasetypefamilyid AS ""CPTCodeCaseTypeFamilyId"",
DSSPES.msdrgcasetypefamilyid AS ""MSDRGCaseTypeFamilyId"",
DSSPES.aprdrgcasetypefamilyid AS ""APRDRGCaseTypeFamilyId"",
PBLID.departmentid AS ""DepartmentId"",
PHYSPRIMARY.physicianspecialtyid AS ""PhysicianSpecialtyId"",
CC.chargecodeid AS ""ChargeCodeID"",
IFNULL(CC.costdriver::TEXT, '') AS ""CostDriver"",
IFNULL(EncounterPatientTypeRollup.patienttyperollup::TEXT, '') AS ""PatientTypeRollup"",
SUM(COSTDETAIL.variabledirectcost) AS ""Cost"",
NULL AS ""Units""
FROM dss.FactPatientCostDetail AS COSTDETAIL
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
INNER JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterrecordnumber = PBLID.encounterrecordnumber
LEFT JOIN dss.FactPatientEncounterClinicalIndicator AS DSSPECI ON DSSPECI.encounterid = PBLID.encounterid
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS EncounterPatientTypeRollup ON EncounterPatientTypeRollup.encounterid = PBLID.encounterid
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
WHERE
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND CC.chargecodeid IN (:chargecodeid_2))
GROUP BY
""EncounterId"",
""DischargeDateTime"",
""EntityID"",
""PhysicianId"",
""PrimaryServiceLineId"",
""CPTCodeCaseTypeFamilyId"",
""MSDRGCaseTypeFamilyId"",
""APRDRGCaseTypeFamilyId"",
""DepartmentId"",
""PhysicianSpecialtyId"",
""ChargeCodeID"",
""CostDriver"",
""PatientTypeRollup""
)
GROUP BY
""EncounterId"",
""DischargeDateTime"",
""EntityID"",
""PhysicianId"",
""PrimaryServiceLineId"",
""CPTCodeCaseTypeFamilyId"",
""MSDRGCaseTypeFamilyId"",
""APRDRGCaseTypeFamilyId"",
""DepartmentId"",
""PhysicianSpecialtyId"",
""ChargeCodeID"",
""CostDriver"",
""PatientTypeRollup""
)
ORDER BY
""EncounterId"" ASC NULLS FIRST,
""DischargeDateTime"" ASC NULLS FIRST,
""EntityID"" ASC NULLS FIRST,
""PhysicianId"" ASC NULLS FIRST,
""PrimaryServiceLineId"" ASC NULLS FIRST,
""CPTCodeCaseTypeFamilyId"" ASC NULLS FIRST,
""MSDRGCaseTypeFamilyId"" ASC NULLS FIRST,
""APRDRGCaseTypeFamilyId"" ASC NULLS FIRST,
""DepartmentId"" ASC NULLS FIRST,
""PhysicianSpecialtyId"" ASC NULLS FIRST,
""ChargeCodeID"" ASC NULLS FIRST,
""CostDriver"" ASC NULLS FIRST,
""PatientTypeRollup"" ASC NULLS FIRST
";
}
}
@@ -0,0 +1,46 @@
using Strata.ContinuousImprovement.Biz.Exploration;
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
using System.Diagnostics.CodeAnalysis;
using static Strata.ContinuousImprovement.Biz.Exploration.ExplorationService;
namespace Strata.ContinuousImprovement.Biz.Test.Integration.SnowflakeIntegrationTests
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Integration"), Category("CCI")]
public class TestSnowflakeDatabaseContext : IntegrationTestBase
{
[Test, Ignore("Test database needs to be rolled over to the new year")]
public async Task BuildCaseTypeFamilySummaryQueryTest()
{
var queryParams = new QueryParams
{
CodeType = UtilizationVariation.VariationCaseTypes.CaseTypeCategories.MSDRG
};
int year = DateTime.Now.Year;
var dischargeDate = new FilterChipItem
{
ChipType = ChipType.DateRange,
Filters = [],
DateRange = [new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1).AddTicks(-1)]
};
var filters = new Exploration.Filters.Filter
{
DischargeDateStart = dischargeDate.DateRange.First(),
DischargeDateEnd = dischargeDate.DateRange.Last(),
FilterChipItems = [dischargeDate]
};
var filterStrings = await ExplorationFilterService.GetFilterStringsAsync(filters.FilterChipItems, queryParams, CancellationToken.None);
var ctfSummaryQuery = ExplorationServiceQuery.BuildCaseTypeFamilySummaryQuery(filterStrings.QueryFilters, filterStrings.EncounterSummaryFilters);
var ctfCostDriverInfos = await SnowflakeDatabaseContext
.QueryAsync<CaseTypeFamilyEncounterInfo>(ctfSummaryQuery, "CaseTypeFamily CostDriver Summary", queryParams);
Assert.That(ctfCostDriverInfos, Is.Not.Null.And.Not.Empty
.And.All.InstanceOf<CaseTypeFamilyEncounterInfo>());
}
}
}