Template
chore: deploy initial code base
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
internal class AddressInfo
|
||||
{
|
||||
public string BaseAddress { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class AllOpportunityInfo
|
||||
{
|
||||
public Guid ConfigurationGuid { get; set; }
|
||||
public string CostDriver { get; set; }
|
||||
|
||||
public bool HasInitiative { get; set; }
|
||||
|
||||
public double IdentifiedSavings { get; set; }
|
||||
public string Initiative { get; set; }
|
||||
public Guid InitiativeGuid { get; set; }
|
||||
public bool IsHidden { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Note { get; set; }
|
||||
public Guid OpportunityGuid { get; set; }
|
||||
public long OpportunityId { get; set; }
|
||||
public string OpportunityKey { get; set; }
|
||||
public int OpportunityType { get; set; }
|
||||
public string OpportunityTypeName { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class CaseTypeFamilyInfo
|
||||
{
|
||||
public int CaseTypeFamilyId { get; set; }
|
||||
public Guid CaseTypeFamilyGuid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string CaseTypeFamilyGlobalId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class ConfigurationInfo
|
||||
{
|
||||
public Guid ConfigurationGuid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public byte DisplayOrder { get; set; }
|
||||
public Guid ServiceLineDimensionGuid { get; set; }
|
||||
[JsonPropertyName("entityIdsCSV")]
|
||||
public string EntityIdsCsv { get; set; }
|
||||
public bool IsUsingIndirectCost { get; set; }
|
||||
public string DepartmentRollupGlobalId { get; set; }
|
||||
public bool IsUsingCustomEntityDimension => !string.IsNullOrEmpty(DepartmentRollupGlobalId);
|
||||
[JsonPropertyName("customEntityIdsCSV")]
|
||||
public string CustomEntityIdsCsv { get; set; }
|
||||
[JsonPropertyName("fiscalYearID")]
|
||||
public short FiscalYearId { get; set; }
|
||||
public DateTime OrgDefinedEndDate { get; set; }
|
||||
public DateTime OrgDefinedTrackingCurrentMonth { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class EntityIdInfo
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int EntityId { get; set; }
|
||||
[JsonPropertyName("name")]
|
||||
public string EntityName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class EntityInfo
|
||||
{
|
||||
public string EntityTitle { get; set; }
|
||||
public string EntityLabel { get; set; }
|
||||
public string EntityText { get; set; }
|
||||
public string DepartmentRollupGlobalId { get; set; }
|
||||
public string HierarchicalGuid { get; set; }
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class ServiceLineInfo
|
||||
{
|
||||
public string ServiceLineGlobalId { get; set; }
|
||||
public string ServiceLineTitle { get; set; }
|
||||
public string ServiceLineName { get; set; }
|
||||
public string ServiceLineDimensionGuid { get; set; }
|
||||
public string ServiceLineHierarchicalGuid { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class CostDriver
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class InitiativeDefaultColumnInfo
|
||||
{
|
||||
[JsonPropertyName("index")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("heading")]
|
||||
public string Heading { get; set; }
|
||||
|
||||
[JsonPropertyName("isLocked")]
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
[JsonPropertyName("isHidden")]
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
[JsonPropertyName("initiativeRollupColumnId")]
|
||||
public int? InitiativeRollupColumnId { get; set; }
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class InitiativeDetailColumnInfo
|
||||
{
|
||||
[JsonPropertyName("index")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("heading")]
|
||||
public string Heading { get; set; }
|
||||
|
||||
[JsonPropertyName("isLocked")]
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
[JsonPropertyName("isHidden")]
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
[JsonPropertyName("initiativeRollupColumnId")]
|
||||
public int? InitiativeRollupColumnId { get; set; }
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class InitiativeFilterOptionsInfo
|
||||
{
|
||||
[JsonPropertyName("costDrivers")]
|
||||
public List<CostDriver> CostDrivers { get; set; }
|
||||
|
||||
[JsonPropertyName("costLeaders")]
|
||||
public List<CostLeader> CostLeaders { get; set; }
|
||||
|
||||
[JsonPropertyName("owners")]
|
||||
public List<Owner> Owners { get; set; }
|
||||
|
||||
[JsonPropertyName("executiveSponsors")]
|
||||
public List<ExecutiveSponsor> ExecutiveSponsors { get; set; }
|
||||
|
||||
[JsonPropertyName("workstreams")]
|
||||
public List<Workstream> Workstreams { get; set; }
|
||||
|
||||
[JsonPropertyName("rollupColumn1Values")]
|
||||
public object RollupColumn1Values { get; set; }
|
||||
|
||||
[JsonPropertyName("rollupColumn2Values")]
|
||||
public object RollupColumn2Values { get; set; }
|
||||
|
||||
[JsonPropertyName("rollupColumn3Values")]
|
||||
public object RollupColumn3Values { get; set; }
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class InitiativeFilterSettingsInfo
|
||||
{
|
||||
[JsonPropertyName("configurationGuid")]
|
||||
public string ConfigurationGuid { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public int Status { get; set; }
|
||||
|
||||
[JsonPropertyName("costDrivers")]
|
||||
public List<string> CostDrivers { get; set; }
|
||||
|
||||
[JsonPropertyName("costLeaders")]
|
||||
public List<string> CostLeaders { get; set; }
|
||||
|
||||
[JsonPropertyName("owners")]
|
||||
public List<string> Owners { get; set; }
|
||||
|
||||
[JsonPropertyName("executiveSponsors")]
|
||||
public List<string> ExecutiveSponsors { get; set; }
|
||||
|
||||
[JsonPropertyName("workstreams")]
|
||||
public List<string> Workstreams { get; set; }
|
||||
|
||||
[JsonPropertyName("rollupColumn1Values")]
|
||||
public List<string> RollupColumn1Values { get; set; }
|
||||
|
||||
[JsonPropertyName("rollupColumn2Values")]
|
||||
public List<string> RollupColumn2Values { get; set; }
|
||||
|
||||
[JsonPropertyName("rollupColumn3Values")]
|
||||
public List<string> RollupColumn3Values { get; set; }
|
||||
|
||||
[JsonPropertyName("groupByCategory")]
|
||||
public string GroupByCategory { get; set; }
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class InitiativeRollupColumnInfo
|
||||
{
|
||||
[JsonPropertyName("initiativeRollupColumnId")]
|
||||
public int InitiativeRollupColumnId { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("initiativeRollupValues")]
|
||||
public List<InitiativeRollupValue> InitiativeRollupValues { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class InitiativeRollupValue
|
||||
{
|
||||
[JsonPropertyName("initiativeRollupValueId")]
|
||||
public int InitiativeRollupValueId { get; set; }
|
||||
|
||||
[JsonPropertyName("initiativeRollupColumnId")]
|
||||
public int InitiativeRollupColumnId { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class SecurityInfo()
|
||||
{
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class User
|
||||
{
|
||||
[JsonPropertyName("userGuid")]
|
||||
public string UserGuid { get; set; }
|
||||
|
||||
[JsonPropertyName("nameFull")]
|
||||
public string NameFull { get; set; }
|
||||
}
|
||||
|
||||
public class CostLeader : User
|
||||
{
|
||||
}
|
||||
|
||||
public class ExecutiveSponsor : User
|
||||
{
|
||||
}
|
||||
|
||||
public class Owner : User
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Info
|
||||
{
|
||||
public class Workstream
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.OrgDefined.Queries;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Mapperly
|
||||
{
|
||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, EnumMappingIgnoreCase = true)]
|
||||
public static partial class OpportunityMetricBaseMap
|
||||
{
|
||||
public static OpportunityMetricBase Map(this ENOpportunityDataModel dataModel)
|
||||
{
|
||||
var enOpportunityBase = JsonSerializer.Deserialize<ENOpportunityBaseFilters>(dataModel.EncounterOpportunityJSON);
|
||||
OpportunityMetricBase target = new OpportunityMetricBase();
|
||||
if (enOpportunityBase != null)
|
||||
{
|
||||
target = enOpportunityBase.Map();
|
||||
target.CaseTypeIds = enOpportunityBase
|
||||
.CaseTypes.Select(fh => int.TryParse(fh.Id, out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
switch (enOpportunityBase.EncounterOpportunityLevel)
|
||||
{
|
||||
case OpportunityLevel.Entity:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.Entities.Select(fh => int.TryParse(fh.Id, out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
case OpportunityLevel.ServiceLine:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.ServiceLines.Select(fh => int.TryParse(fh.Id, out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
case OpportunityLevel.Specialty:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.Specialties.Select(fh => int.TryParse(fh.Id, out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
case OpportunityLevel.Provider:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.Providers.Select(fh => int.TryParse(fh.Id, out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public static OpportunityMetricBase Map(this ENOpportunityDto source)
|
||||
{
|
||||
var target = source.MapTo();
|
||||
var enOpportunityBase = JsonSerializer.Deserialize<ENOpportunityBaseFilters>(source.EncounterOpportunityJSON);
|
||||
var regexp = new Regex(@"[^\d]*\|([\d]+)", RegexOptions.Compiled);
|
||||
if (enOpportunityBase != null)
|
||||
{
|
||||
target = enOpportunityBase.Map();
|
||||
target.CaseTypeIds = enOpportunityBase
|
||||
.CaseTypes.Select(fh => int.TryParse(regexp.Replace(fh.Id, "$1"), out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
switch (enOpportunityBase.EncounterOpportunityLevel)
|
||||
{
|
||||
case OpportunityLevel.Entity:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.Entities.Select(fh => int.TryParse(regexp.Replace(fh.Id, "$1"), out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
case OpportunityLevel.ServiceLine:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.ServiceLines.Select(fh => int.TryParse(regexp.Replace(fh.Id, "$1"), out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
case OpportunityLevel.Specialty:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.Specialties.Select(fh => int.TryParse(regexp.Replace(fh.Id, "$1"), out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
case OpportunityLevel.Provider:
|
||||
target.OpportunityLevelIds = enOpportunityBase
|
||||
.Providers.Select(fh => int.TryParse(regexp.Replace(fh.Id, "$1"), out var id) ? id : -1).Where(id => id >= 0).ToList();
|
||||
break;
|
||||
}
|
||||
target.OpportunityLevelIdAsCSV = string.Join(",", target.OpportunityLevelIds);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public static partial OpportunityMetricBase Map(this ENOpportunityBaseFilters enOpportunityBase);
|
||||
|
||||
[MapperIgnoreSource(nameof(ENOpportunityDto.OpportunityLevelIds))]
|
||||
private static partial OpportunityMetricBase MapTo(this ENOpportunityDto source);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Setup;
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Tests;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
[SetUpFixture]
|
||||
public class ClientIntegrationTestFixture
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public virtual async Task SetDbContextAndServices()
|
||||
{
|
||||
#if DEBUG
|
||||
//When running locally sets the profile to the CI profile so you have access to resources
|
||||
Environment.SetEnvironmentVariable("AWS_Profile", "sdt-continuous-improvement-service-role");
|
||||
#endif
|
||||
var clientService = new ApiTestBase();
|
||||
await clientService.ClientValidation();
|
||||
var services = ServiceCollectionFactory.Create();
|
||||
await services.SetupServiceProperties(clientService.StrataId);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task TearDown()
|
||||
{
|
||||
try
|
||||
{
|
||||
var centralDbContext = ServiceProperties.CentralDbContext as CentralDbContext;
|
||||
if (centralDbContext == null) { return; }
|
||||
_ = await centralDbContext.Database.EnsureDeletedAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Strata.ApiLib.Core.Cors.Extensions;
|
||||
using Strata.ApiLib.Core.StrataAuthentication.Bootstrappers;
|
||||
using Strata.Configuration.Client.DependencyInjection.ConsoleApps;
|
||||
using Strata.ContinuousImprovement.Biz.Configurations;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Setup
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static class ServiceCollectionFactory
|
||||
{
|
||||
// dev master
|
||||
private static readonly Guid DatabaseGuid = new("54409f55-5ddf-4748-8222-9ccfb04fb4f5");
|
||||
private static readonly string IntegrationTesting = $"ci_api_{DateTime.Now:yyyyMMdd}_{Guid.NewGuid():N}";
|
||||
|
||||
public static IServiceCollection Create()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
try
|
||||
{
|
||||
var configuration = StrataConfigurationFactory.GetConfiguration();
|
||||
services.AddSingleton(configuration);
|
||||
services.AddStrataAuthentication(configuration);
|
||||
services.AddStrataCors(configuration);
|
||||
services.AddContinuousImprovementServices(configuration, IntegrationTesting);
|
||||
services.AddAsyncDbContextFactory<JazzDbContext>(options =>
|
||||
{
|
||||
options
|
||||
.WithConnectionString((provider, token) => provider.GetConnectionStringFromSmc(DatabaseGuid, token))
|
||||
.WithDbContextOptions((connectionString, provider, builder) =>
|
||||
builder.UseSqlServer(connectionString, options => { options.CommandTimeout(300); }));
|
||||
});
|
||||
services.AddLogging();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Strata.Analytics.Client;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Info;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityTypeWorkbooks;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityWorkbooks;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Setup
|
||||
{
|
||||
public static class ServiceProperties
|
||||
{
|
||||
// dev master
|
||||
internal static readonly Guid DatabaseGuid = new("54409f55-5ddf-4748-8222-9ccfb04fb4f5");
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
public static ICentralDbContext CentralDbContext { get; set; }
|
||||
public static JazzEntityFrameworkFactory JazzConnBuilderFactory { get; set; }
|
||||
public static IJazzDbContext JazzDbContext { get; set; }
|
||||
public static Biz.DbContexts.Snowflake.ISnowflakeDatabaseContext SnowflakeDatabaseContext { get; set; }
|
||||
public static IServiceCollection Services { get; set; }
|
||||
public static IServiceProvider ServiceProvider { get; set; }
|
||||
public static IAnalyticsService AnalyticsService { get; set; }
|
||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
public static string BaseAddress { get; set; } = string.Empty;
|
||||
|
||||
private static int WorkbookId = 9;
|
||||
|
||||
public static async Task SetupServiceProperties(this IServiceCollection services, int strataId = 0)
|
||||
{
|
||||
#if DEBUG
|
||||
Environment.SetEnvironmentVariable("AWS_Profile", "sdt-continuous-improvement-service-role");
|
||||
#endif
|
||||
services.AddOptions<AddressInfo>().BindConfiguration("address").ValidateDataAnnotations(); //bind the options for aws and validates for missing values
|
||||
|
||||
Services = services;
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
ServiceProvider = serviceProvider;
|
||||
|
||||
try
|
||||
{
|
||||
var sfConnBuilderFactory = serviceProvider.GetRequiredService<Biz.DbContexts.Snowflake.ISnowflakeDatabaseContextFactory>();
|
||||
SnowflakeDatabaseContext = sfConnBuilderFactory.Create($"{DatabaseGuid:N}");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
var sectionAccessor = serviceProvider.GetRequiredService<IOptions<AddressInfo>>();
|
||||
var addressInfo = sectionAccessor.Value;
|
||||
if (addressInfo != null && !string.IsNullOrEmpty(addressInfo.BaseAddress))
|
||||
{
|
||||
BaseAddress = addressInfo.BaseAddress;
|
||||
}
|
||||
try
|
||||
{
|
||||
var jazzConnBuilderFactory = serviceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
JazzDbContext = await jazzConnBuilderFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
|
||||
await BuildPostgresDb(strataId);
|
||||
|
||||
BuildAnalyticsService(strataId);
|
||||
}
|
||||
|
||||
private static async Task BuildPostgresDb(int strataId)
|
||||
{
|
||||
try
|
||||
{
|
||||
CentralDbContext = ServiceProvider.GetService<CentralDbContext>() as CentralDbContext
|
||||
?? throw new NullReferenceException(nameof(CentralDbContext));
|
||||
var centralDbContext = CentralDbContext as CentralDbContext;
|
||||
if (centralDbContext == null) { throw new NullReferenceException(nameof(centralDbContext)); }
|
||||
|
||||
await centralDbContext.Database.MigrateAsync(CancellationToken.None);
|
||||
Assert.That(centralDbContext, Is.Not.Null);
|
||||
|
||||
JazzConnBuilderFactory = new JazzEntityFrameworkFactory();
|
||||
await JazzConnBuilderFactory.EnsureCreatedAsync(centralDbContext, JazzDbContext, CancellationToken.None);
|
||||
var workbook = new OpportunityWorkbook
|
||||
{
|
||||
StrataId = strataId,
|
||||
WorkbookId = WorkbookId,
|
||||
OpportunityGuid = Guid.NewGuid(),
|
||||
Name = "Test",
|
||||
};
|
||||
if (!centralDbContext.OpportunityWorkbooks.Any(ow => ow.StrataId == workbook.StrataId
|
||||
&& ow.WorkbookId == workbook.WorkbookId))
|
||||
{
|
||||
await centralDbContext.OpportunityWorkbooks.AddAsync(workbook, CancellationToken.None);
|
||||
}
|
||||
var oppTypeWorkbook = new OpportunityTypeWorkbook
|
||||
{
|
||||
StrataId = strataId,
|
||||
WorkbookId = WorkbookId,
|
||||
OpportunityType = (int)OpportunityTypes.ChargeCode,
|
||||
Name = "Test",
|
||||
};
|
||||
if (!centralDbContext.OpportunityTypeWorkbooks.Any(otw => otw.StrataId == oppTypeWorkbook.StrataId
|
||||
&& otw.WorkbookId == oppTypeWorkbook.WorkbookId))
|
||||
{
|
||||
await centralDbContext.OpportunityTypeWorkbooks.AddAsync(oppTypeWorkbook, CancellationToken.None);
|
||||
}
|
||||
|
||||
await centralDbContext.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void BuildAnalyticsService(int strataId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var analyticsService = Mock.Of<IAnalyticsService>();
|
||||
var workbook = new Workbook
|
||||
{
|
||||
Name = "Test",
|
||||
WorkbookId = WorkbookId,
|
||||
IsHidden = false,
|
||||
StrataId = strataId,
|
||||
};
|
||||
var workbooks = new List<Workbook> { workbook };
|
||||
workbooks.AddRange(Enumerable.Range(1, 1000).Where(i => i != WorkbookId).Select(i => new Workbook
|
||||
{
|
||||
Name = $"Test {i}",
|
||||
WorkbookId = i,
|
||||
IsHidden = (i % 2 == 0),
|
||||
StrataId = (i % 3 == 0) ? 9 : strataId,
|
||||
}));
|
||||
//GetWorkbooksAsync
|
||||
Mock.Get(analyticsService)
|
||||
.Setup(s => s.GetWorkbookAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<int, CancellationToken>((id, token)
|
||||
=> Task.FromResult(id == WorkbookId ? workbook : null));
|
||||
Mock.Get(analyticsService)
|
||||
.Setup(s => s.GetWorkbooksAsync(It.IsAny<bool>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<bool, CancellationToken>((isHidden, token)
|
||||
=> Task.FromResult(workbooks.Where(w => w.IsHidden == isHidden)));
|
||||
AnalyticsService = analyticsService;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Setup
|
||||
{
|
||||
public class VaultResponse
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="NUnit" Version="4.3.2" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
|
||||
<PackageReference Include="Strata.ApiLib.Core" Version="8.4.1" />
|
||||
<PackageReference Include="Strata.Configuration.Client" Version="9.15.1" />
|
||||
<PackageReference Include="Strata.SnowflakeLib" Version="4.3.0" />
|
||||
<PackageReference Include="Strata.SqlTools.EntityFramework" Version="6.0.3" />
|
||||
<PackageReference Include="Verify.NUnit" Version="29.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Strata.ContinuousImprovement.Api\Strata.ContinuousImprovement.Api.csproj" />
|
||||
<ProjectReference Include="..\..\src\Strata.ContinuousImprovement.Biz\Strata.ContinuousImprovement.Biz.csproj" />
|
||||
<ProjectReference Include="..\Strata.ContinuousImprovement.JazzEntityFrameworkStub\Strata.ContinuousImprovement.JazzEntityFrameworkStub.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="NUnit.Framework" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,222 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Setup;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityTypeWorkbooks;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityWorkbooks;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
public class ApiTestBase
|
||||
{
|
||||
protected StrataTokenInfo StrataTokenInfo { get; set; }
|
||||
protected string AccessToken { get; set; }
|
||||
protected IEnumerable<Claim> Claims { get; set; }
|
||||
protected long Expires_in { get; set; }
|
||||
protected ClaimsIdentity ClaimsIdentity { get; set; }
|
||||
protected ClaimsPrincipal ClaimsPrincipal { get; set; }
|
||||
protected Dictionary<string, ClaimsPrincipal> ClaimsPrincipals { get; set; } = new Dictionary<string, ClaimsPrincipal>();
|
||||
internal IClaimsPrincipalAccessor ClaimsPrincipalAccessor { get; set; }
|
||||
internal int StrataId { get; set; }
|
||||
internal Guid DatabaseGuid { get; set; }
|
||||
|
||||
protected readonly string BaseAddress = "https://continuousimprovement-api.dev.stratanetwork.net";
|
||||
public IConfigurationService ConfigurationService { get; set; }
|
||||
public CentralDbContext CentralDbContext { get; set; }
|
||||
public IOpportunityWorkbookService OpportunityWorkbookService { get; set; }
|
||||
#pragma warning disable NUnit1032 // An IDisposable field/property should be Disposed in a TearDown method
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
#pragma warning restore NUnit1032 // An IDisposable field/property should be Disposed in a TearDown method
|
||||
|
||||
public OpportunityWorkbook OpportunityWorkbook { get; set; }
|
||||
public OpportunityTypeWorkbook OpportunityTypeWorkbook { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task TestSetup()
|
||||
{
|
||||
#if DEBUG
|
||||
//When running locally sets the profile to the CI profile so you have access to resources
|
||||
Environment.SetEnvironmentVariable("AWS_Profile", "sdt-continuous-improvement-service-role");
|
||||
#endif
|
||||
var strataBearer = await StrataValidation();
|
||||
await ClientValidation();
|
||||
var services = ServiceCollectionFactory.Create();
|
||||
await services.SetupServiceProperties(StrataId);
|
||||
ServiceProvider = ServiceProperties.ServiceProvider;
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(BaseAddress)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
|
||||
CentralDbContext = (CentralDbContext)ServiceProperties.CentralDbContext;
|
||||
}
|
||||
|
||||
protected HttpClient HttpClient;
|
||||
|
||||
[SetUp]
|
||||
public void SetupHttpClient()
|
||||
{
|
||||
HttpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(BaseAddress)
|
||||
};
|
||||
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDownHttpClient()
|
||||
{
|
||||
HttpClient.Dispose();
|
||||
}
|
||||
|
||||
#region Client Claims Validation
|
||||
|
||||
public async Task<string> ClientValidation()
|
||||
{
|
||||
var userInfo = new VaultUserInfo
|
||||
{
|
||||
vaultTitle = "ccicostleader", // vault ccicostleader is same as jazz ccicostleader
|
||||
vaultUserName = "ccicostleader",
|
||||
userName = "ccicostleader",
|
||||
fullName = "Administrator, CCI"
|
||||
};
|
||||
return await ClientValidation(userInfo);
|
||||
}
|
||||
|
||||
public async Task<string> StrataValidation()
|
||||
{
|
||||
var userInfo = new VaultUserInfo
|
||||
{
|
||||
vaultTitle = "IDTestUser", // vault ccicostleader is same as jazz ccicostleader
|
||||
vaultUserName = "sdt\\IDTestUser", // vault ccicostleader is same as jazz ccicostleader
|
||||
userName = "sdt\\IDTestUser",
|
||||
fullName = "Administrator, CCI"
|
||||
};
|
||||
return await ClientValidation(userInfo);
|
||||
}
|
||||
|
||||
public async Task<string> ClientValidation(VaultUserInfo userInfo)
|
||||
{
|
||||
var vaultInfo = await GetUserPassword(userInfo.vaultTitle);
|
||||
var bearerToken = await GetBearerToken(vaultInfo.Item1, vaultInfo.Item2);
|
||||
ClaimsIdentity = new ClaimsIdentity(Claims);
|
||||
ClaimsPrincipal = new ClaimsPrincipal(ClaimsIdentity);
|
||||
ClaimsPrincipals.Add(userInfo.vaultTitle, ClaimsPrincipal);
|
||||
StrataId = ClaimsPrincipal.GetStrataId();
|
||||
DatabaseGuid = ClaimsPrincipal.GetDatabaseGuid();
|
||||
BuildClaimsPrincipalAccessor();
|
||||
return bearerToken;
|
||||
}
|
||||
|
||||
internal async Task<Tuple<string, string>> GetUserPassword(string vaultTitle)
|
||||
{
|
||||
var client = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri("https://vault.sdt.local/")
|
||||
};
|
||||
client.DefaultRequestHeaders.Add("apiKey", "a69d2e478c3f5b395b687ecdf34a3239");
|
||||
client.DefaultRequestHeaders.Add("ignoreHTTPSErrors", "true");
|
||||
var vaultResponse = await client.GetAsync($"api/searchpasswords/122?title={vaultTitle}");
|
||||
vaultResponse.EnsureSuccessStatusCode();
|
||||
string responseString = await vaultResponse.Content.ReadAsStringAsync();
|
||||
if (string.IsNullOrEmpty(responseString)) { return new Tuple<string, string>(string.Empty, string.Empty); }
|
||||
var jsonResponse = JsonSerializer.Deserialize<IEnumerable<VaultResponse>>(responseString);
|
||||
if (jsonResponse == null) { return new Tuple<string, string>(string.Empty, string.Empty); }
|
||||
var vaultUserInfo = jsonResponse.First();
|
||||
client.Dispose();
|
||||
return new Tuple<string, string>(vaultUserInfo.UserName, vaultUserInfo.Password);
|
||||
}
|
||||
|
||||
internal async Task<string> GetBearerToken(string userName, string password)
|
||||
{
|
||||
var form = BuildTokenRequest(userName, password);
|
||||
var client = new HttpClient();
|
||||
var response = await client.PostAsync("https://identity.dev.stratanetwork.net/connect/token", form);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseString = await response.Content.ReadAsStringAsync();
|
||||
if (string.IsNullOrEmpty(responseString)) { return string.Empty; }
|
||||
var strataTokenInfo = JsonSerializer.Deserialize<StrataTokenInfo>(responseString);
|
||||
if (strataTokenInfo == null) { return string.Empty; }
|
||||
StrataTokenInfo = strataTokenInfo;
|
||||
AccessToken = strataTokenInfo.AccessToken;
|
||||
Expires_in = strataTokenInfo.ExpiresIn;
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(AccessToken);
|
||||
Claims = jwtToken.Claims;
|
||||
client.Dispose();
|
||||
return AccessToken;
|
||||
}
|
||||
|
||||
internal FormUrlEncodedContent BuildTokenRequest(string userName, string password)
|
||||
{
|
||||
var form = new FormUrlEncodedContent(
|
||||
[
|
||||
new KeyValuePair<string, string>("database_guid", ServiceProperties.DatabaseGuid.ToString()),
|
||||
new KeyValuePair<string, string>("grant_type", "password"),
|
||||
new KeyValuePair<string, string>("username", userName),
|
||||
new KeyValuePair<string, string>("password", password),
|
||||
new KeyValuePair<string, string>("scope", "strata.api.external offline_access"),
|
||||
new KeyValuePair<string, string>("client_id", "IntegrationTest"),
|
||||
new KeyValuePair<string, string>("client_secret", "UhAEQsGByTCrwPdHPLmaet_DmLEfcJVbZqGBxArdeRup_TPLrYKNpUNRHejjdnhd")
|
||||
]);
|
||||
return form;
|
||||
}
|
||||
|
||||
internal void BuildClaimsPrincipalAccessor()
|
||||
{
|
||||
try
|
||||
{
|
||||
var claimsAccessor = Mock.Of<IClaimsPrincipalAccessor>();
|
||||
Mock.Get(claimsAccessor)
|
||||
.Setup(s => s.GetCurrentClaimsPrincipal())
|
||||
.Returns(ClaimsPrincipal);
|
||||
ClaimsPrincipalAccessor = claimsAccessor;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
protected async Task<IEnumerable<Info.ConfigurationInfo>> GetConfigurations()
|
||||
{
|
||||
string ConfigurationsEndpoint = "api/v1/configurations";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ConfigurationsEndpoint);
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var configurations = await response.Content.ReadFromJsonAsync<IEnumerable<Info.ConfigurationInfo>>();
|
||||
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
|
||||
configurations = configurations?.OrderBy(x => x.DisplayOrder)
|
||||
?? throw new ArgumentNullException(nameof(configurations));
|
||||
#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one
|
||||
return configurations;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
public class VaultUserInfo
|
||||
{
|
||||
public string vaultTitle { get; set; }
|
||||
public string vaultUserName { get; set; }
|
||||
public string userName { get; set; }
|
||||
public string fullName { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
public static class HttpMessageResponseExtensions
|
||||
{
|
||||
public static void AssertIsValid(this HttpResponseMessage response, bool exportable = false, string version = "1.0")
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(response.IsSuccessStatusCode, Is.True);
|
||||
var headers = response.Headers.Where(x => x.Value != null)
|
||||
.ToDictionary(k => k.Key, v => v.Value);
|
||||
Assert.That(headers, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(headers.ContainsKey("Api-Supported-Versions"), Is.True);
|
||||
var apiVersion = headers["Api-Supported-Versions"].ToList();
|
||||
Assert.That(apiVersion.Contains(version), Is.True);
|
||||
if (exportable)
|
||||
{
|
||||
var content = response.Content;
|
||||
Assert.That(content, Is.Not.Null);
|
||||
headers = response.Content.Headers.Where(x => x.Value != null)
|
||||
.ToDictionary(k => k.Key, v => v.Value);
|
||||
Assert.That(headers.ContainsKey("Content-Type"), Is.True);
|
||||
var contentType = headers["Content-Type"].ToList();
|
||||
Assert.That(contentType.Contains("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), Is.True);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
public class StrataTokenInfo
|
||||
{
|
||||
[JsonPropertyName("access_token")]
|
||||
public required string AccessToken { get; set; }
|
||||
[JsonPropertyName("token_type")]
|
||||
public required string TokenType { get; set; }
|
||||
[JsonPropertyName("expires_in")]
|
||||
public required long ExpiresIn { get; set; }
|
||||
[JsonPropertyName("refresh_token")]
|
||||
public required string RefreshToken { get; set; }
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using Strata.ApiCommunication.Http;
|
||||
using Strata.ApiLib.Standard.Models;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture(Category = "Integration")]
|
||||
|
||||
public class TestAllOpportunitiesController : ApiTestBase
|
||||
{
|
||||
protected const string AllOpportunitiesEndpoint = "api/v1/all-opportunities";
|
||||
|
||||
[Test]
|
||||
public async Task TestGetAllOportunitiesFromApi()
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.First();
|
||||
var loadOptions = new Controllers.AllOpportunityLoadOptions
|
||||
{
|
||||
Filters = new Biz.All.Opportunities.Filters.AllFilters
|
||||
{
|
||||
ConfigurationGuid = configuration.ConfigurationGuid,
|
||||
OpportunityTypes =
|
||||
[
|
||||
$"{(int)OpportunityTypes.Encounter}",
|
||||
$"{(int) OpportunityTypes.QualityVariation}",
|
||||
$"{(int) OpportunityTypes.Variation}",
|
||||
],
|
||||
ViewVisible = true
|
||||
},
|
||||
PagingOptions = new Biz.Pagination.PagingOptions
|
||||
{
|
||||
First = 0,
|
||||
Rows = 100,
|
||||
SortOrder = Biz.Pagination.SortOrder.Desc,
|
||||
SortField = "IdentifiedSavings"
|
||||
}
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, AllOpportunitiesEndpoint);
|
||||
|
||||
// act
|
||||
await request.SetContentAsJsonAsync(loadOptions);
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
var opportunitiesPage = new PagedApiResponse<Info.AllOpportunityInfo>();
|
||||
Assert.DoesNotThrowAsync(async () =>
|
||||
opportunitiesPage = await response.Content.ReadFromJsonAsync<PagedApiResponse<Info.AllOpportunityInfo>>());
|
||||
response.AssertIsValid();
|
||||
Assert.That(opportunitiesPage.Data, Is.Not.Empty);
|
||||
Assert.That(opportunitiesPage.Data.Count, Is.LessThanOrEqualTo(loadOptions.PagingOptions.Rows));
|
||||
Assert.That(opportunitiesPage.Data.Any(opp =>
|
||||
opp.OpportunityType == (int)OpportunityTypes.Encounter
|
||||
|| opp.OpportunityType == (int)OpportunityTypes.QualityVariation
|
||||
|| opp.OpportunityType == (int)OpportunityTypes.Variation), Is.True);
|
||||
Assert.That(opportunitiesPage.PagingInfo.ResultsPerPage, Is.LessThanOrEqualTo(loadOptions.PagingOptions.Rows));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using Strata.DecisionSupport.Models.Jazz;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture(Category = "Integration")]
|
||||
|
||||
public class TestClientOpportunityWorkbooksController : ApiTestBase
|
||||
{
|
||||
protected const string ClientOpportunityWorkbooksEndpoint = "api/v1/client-opportunity-workbooks";
|
||||
|
||||
[Test]
|
||||
public async Task TestGetAllWorkbooksFromApi()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, $"{ClientOpportunityWorkbooksEndpoint}/workbooks");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var workbooks = await response.Content.ReadFromJsonAsync<IEnumerable<WorkbookClientInfo>>(CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(workbooks, Is.AssignableTo<IEnumerable<WorkbookClientInfo>>());
|
||||
Assert.That(workbooks, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("API")]
|
||||
|
||||
public partial class TestConfigurationsController : ApiTestBase
|
||||
{
|
||||
protected const string ConfigurationsEndpoint = "api/v1/configurations";
|
||||
|
||||
[Test]
|
||||
public async Task TestConfigurations()
|
||||
{
|
||||
//arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ConfigurationsEndpoint);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
response.AssertIsValid();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Test database needs to be rolled over to the new year")]
|
||||
public async Task TestDfltConfiguration()
|
||||
{
|
||||
//arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ConfigurationsEndpoint);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var configurations = await response.Content.ReadFromJsonAsync<IEnumerable<Info.ConfigurationInfo>>();
|
||||
configurations = configurations?.OrderBy(x => x.DisplayOrder);
|
||||
var configuration = configurations?.FirstOrDefault();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
|
||||
Assert.That(configurations, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configuration, Is.Not.Null);
|
||||
var expectedFiscalYearId = DateTime.Now.Year;
|
||||
Assert.That(configuration.FiscalYearId, Is.InRange(expectedFiscalYearId - 1, expectedFiscalYearId), "Test database fiscal year configuration must be updated for the current year.");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestEntityConfiguration()
|
||||
{
|
||||
//arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ConfigurationsEndpoint);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var configurations = await response.Content.ReadFromJsonAsync<IEnumerable<Info.ConfigurationInfo>>();
|
||||
configurations = configurations?.OrderBy(x => x.DisplayOrder);
|
||||
var configuration = configurations?.FirstOrDefault(c => c.EntityIdsCsv != "" && !c.IsUsingCustomEntityDimension);
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
|
||||
Assert.That(configurations, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configuration, Is.Not.Null);
|
||||
Assert.That(configuration.IsUsingCustomEntityDimension, Is.False);
|
||||
Assert.That(configuration.EntityIdsCsv, Is.Not.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestConfigurationEntities()
|
||||
{
|
||||
//arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configurationGuid = configurations?.FirstOrDefault(c => c.EntityIdsCsv != "" && !c.IsUsingCustomEntityDimension)?.ConfigurationGuid ?? Guid.Empty;
|
||||
var configurationsEndpoint = ConfigurationsEndpoint + $"/{configurationGuid}/configuration-entities";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, configurationsEndpoint);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var configurationEntities = await response.Content.ReadFromJsonAsync<IEnumerable<Info.EntityIdInfo>>();
|
||||
configurationEntities = configurationEntities?.OrderBy(x => x.EntityName);
|
||||
var configurationEntity = configurationEntities?.FirstOrDefault();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
|
||||
Assert.That(configurationEntities, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configurationEntity, Is.Not.Null);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestConfigurationServiceLineInfo()
|
||||
{
|
||||
//arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configurationGuid = configurations?.FirstOrDefault()?.ConfigurationGuid ?? Guid.Empty;
|
||||
var configurationsEndpoint = ConfigurationsEndpoint + $"/{configurationGuid}/configuration-service-line-info";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, configurationsEndpoint);
|
||||
var guidRegex = GuidRegex();
|
||||
var serviceLineRegex = new Regex("w*Service Lines?");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var configurationServiceLine = await response.Content.ReadFromJsonAsync<Info.ServiceLineInfo>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
|
||||
Assert.That(configurationServiceLine, Is.Not.Null);
|
||||
Assert.That(configurationServiceLine.ServiceLineGlobalId, Is.Not.Null);
|
||||
Assert.That(configurationServiceLine.ServiceLineTitle, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configurationServiceLine.ServiceLineName, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(guidRegex.IsMatch(configurationServiceLine.ServiceLineDimensionGuid), Is.True);
|
||||
Assert.That(guidRegex.IsMatch(configurationServiceLine.ServiceLineHierarchicalGuid), Is.True);
|
||||
Assert.That(serviceLineRegex.IsMatch(configurationServiceLine.ServiceLineTitle), Is.True);
|
||||
Assert.That(serviceLineRegex.IsMatch(configurationServiceLine.ServiceLineName), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestConfigurationEntityInfo()
|
||||
{
|
||||
//arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configurationGuid = configurations?.FirstOrDefault()?.ConfigurationGuid ?? Guid.Empty;
|
||||
var configurationsEndpoint = ConfigurationsEndpoint + $"/{configurationGuid}/configuration-entity-info";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, configurationsEndpoint);
|
||||
var guidRegex = GuidRegex();
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var configurationServiceLine = await response.Content.ReadFromJsonAsync<Info.EntityInfo>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
|
||||
Assert.That(configurationServiceLine, Is.Not.Null);
|
||||
Assert.That(configurationServiceLine.DepartmentRollupGlobalId, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configurationServiceLine.EntityTitle, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configurationServiceLine.EntityLabel, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(configurationServiceLine.EntityText, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(guidRegex.IsMatch(configurationServiceLine.HierarchicalGuid), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
[GeneratedRegex("[({]?[a-fA-F0-9]{8}[\\-]?([a-fA-F0-9]{4}[\\-]?){3}[a-fA-F0-9]{12}[})]?")]
|
||||
private static partial Regex GuidRegex();
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
using Strata.ApiCommunication.Http;
|
||||
using Strata.ApiLib.Standard.Models;
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Mapperly;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.OrgDefined.Queries;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[TestFixture, Category("Integration"), Category("API")]
|
||||
|
||||
public class TestEncounterOpportunitiesController : ApiTestBase
|
||||
{
|
||||
private const string EncounterOpportunitiesEndpoint = "api/v1/encounter-opportunities";
|
||||
|
||||
[Test]
|
||||
public async Task OpportunityMetricCohortDataTest()
|
||||
{
|
||||
// Arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "api/v1/configurations");
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
var configurations = await response.Content
|
||||
.ReadFromJsonAsync<List<ConfigurationInfo>>(options, CancellationToken.None);
|
||||
var configuration = configurations.SingleOrDefault(c => c.DisplayOrder == 0);
|
||||
if (configuration == null) Assert.Inconclusive("Unable to obtain configuration");
|
||||
request = new HttpRequestMessage(HttpMethod.Post, EncounterOpportunitiesEndpoint);
|
||||
await request.SetContentAsJsonAsync(
|
||||
new
|
||||
{
|
||||
pagingOptions = new
|
||||
{
|
||||
sortField = "IdentifiedSavings",
|
||||
sortOrder = "Asc",
|
||||
first = 0,
|
||||
rows = 1
|
||||
},
|
||||
filters = new
|
||||
{
|
||||
configurationGuid = configuration.ConfigurationGuid,
|
||||
identifiedSavings = 0,
|
||||
viewVisible = true,
|
||||
excludeInitiaves = false,
|
||||
opportunitySearch = "",
|
||||
cases = 0,
|
||||
opportunityLevels = Array.Empty<string>(),
|
||||
encounterGroups = "CaseType",
|
||||
seasonality = "Without"
|
||||
}
|
||||
});
|
||||
response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var opportunityPage =
|
||||
await response.Content.ReadFromJsonAsync<PagedApiResponse<ENOpportunity>>(options, CancellationToken.None);
|
||||
var opportunity = opportunityPage?.Data.FirstOrDefault(opp => opp.OpportunityLevel == nameof(OpportunityLevel.Entity));
|
||||
if (opportunity == null) Assert.Ignore("No Encounter Opportunity found to work with");
|
||||
request = new HttpRequestMessage(HttpMethod.Get, EncounterOpportunitiesEndpoint + $"/{opportunity.OpportunityGuid}");
|
||||
response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var opportunityDto = await response.Content.ReadFromJsonAsync<ENOpportunityDto>(options, CancellationToken.None);
|
||||
if (opportunityDto == null) Assert.Ignore("Unable to retrieve opportunity data");
|
||||
var data = opportunityDto.Map();
|
||||
var caseTypeCategorySql = string.Empty;
|
||||
/*
|
||||
var CaseTypeCategories = await jazzDbContext.DimCaseTypes
|
||||
.GroupBy(ct => ct.CaseTypeCategory, (g, d) => new
|
||||
{
|
||||
CaseTypeCategory = g,
|
||||
CaseTypeIds = d.Select(x => x.CaseTypeId).ToList()
|
||||
}).ToListAsync(CancellationToken.None);
|
||||
var CaseTypeCategorySql = CaseTypeCategories
|
||||
.Select(ct => string.Format(CaseTypeFamilyIdExists.Exists[ct.CaseTypeCategory], string.Join(",", ct.CaseTypeIds)))
|
||||
.Aggregate("", (x, y) => $"{x}\r\nOR\r\n{y}");
|
||||
*/
|
||||
var requestInfo = new OpportunityMetricCohortOptions
|
||||
{
|
||||
ConfigurationGuid = configuration.ConfigurationGuid,
|
||||
Data = data,
|
||||
InnerJoin = caseTypeCategorySql
|
||||
};
|
||||
request = new HttpRequestMessage(HttpMethod.Post, $"{EncounterOpportunitiesEndpoint}/opportunity-metric-cohort-data");
|
||||
await request.SetContentAsJsonAsync(requestInfo, CancellationToken.None);
|
||||
|
||||
// Act
|
||||
response = await HttpClient.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigurationInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Guid ConfigurationGuid { get; set; }
|
||||
public int DisplayOrder { get; set; }
|
||||
public int FiscalYearId { get; set; }
|
||||
}
|
||||
|
||||
public class ENOpportunityInfo
|
||||
{
|
||||
public Guid OpportunityGuid { get; set; }
|
||||
public Guid ConfigurationGuid { get; set; }
|
||||
public string OpportunityKey { get; set; }
|
||||
public double IdentifiedSavings { get; set; }
|
||||
public Guid InitiativeGuid { get; set; }
|
||||
public bool HasInitiative => InitiativeGuid != Guid.Empty;
|
||||
public string Initiative { get; set; }
|
||||
public bool IsHidden { get; set; }
|
||||
public string Note { get; set; } = String.Empty;
|
||||
public string Name { get; set; }
|
||||
public double Cases { get; set; }
|
||||
public string EncounterGroup { get; set; }
|
||||
public string EncounterGroupValue { get; set; }
|
||||
public string OpportunityLevel { get; set; }
|
||||
public bool IsSeasonal { get; set; }
|
||||
public long OpportunityId { get; set; }
|
||||
}
|
||||
|
||||
public static class CaseTypeFamilyIdExists
|
||||
{
|
||||
public static string[] Exists => new[]
|
||||
{
|
||||
@" EXISTS(SELECT 1 FROM dss.CaseTypeFamilyMappingICD10 msdrg
|
||||
INNER JOIN dss.DimCaseType dct ON dct.CaseTypeFamilyID = msdrg.CaseTypeFamilyID AND dct.CaseTypeId in ({0}) AND dct.CaseTypeCategory = 0
|
||||
WHERE msdrg.CaseTypeFamilyID = fpes.MSDRGCaseTypeFamilyID)",
|
||||
@" EXISTS(SELECT 1 FROM dss.CaseTypeFamilyMappingAPRDRG aprdrg
|
||||
INNER JOIN dss.DimCaseType dct ON dct.CaseTypeFamilyID = aprdrg.CaseTypeFamilyID AND dct.CaseTypeId in ({0}) AND dct.CaseTypeCategory = 1
|
||||
WHERE aprdrg.CaseTypeFamilyID = fpes.APRDRGCaseTypeFamilyID)",
|
||||
@"AND EXISTS(SELECT 1 FROM dss.FactPatientEncounterClinicalIndicator ci
|
||||
WHERE ci.EncounterID = fpes.EncounterID AND ci.ClinicalIndicatorID in ({0}) and ci.IsDeleted = 0)",
|
||||
@" EXISTS(SELECT 1 FROM dss.CaseTypeFamilyMappingCPT cpt
|
||||
INNER JOIN dss.DimCaseType dct ON dct.CaseTypeFamilyID = cpt.CaseTypeFamilyID AND dct.CaseTypeId in ({0}) AND dct.CaseTypeCategory = 3
|
||||
WHERE cpt.CaseTypeFamilyID = fpes.CPTCodeCaseTypeFamilyID)"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Setup;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture(Category = "Integration")]
|
||||
public class TestFixtureSetup : ApiTestBase
|
||||
{
|
||||
[Test]
|
||||
public async Task TestBearerToken()
|
||||
{
|
||||
var bearerToken = AccessToken;
|
||||
Assert.That(bearerToken, Is.Not.Null.And.Not.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestCentralDbContext()
|
||||
{
|
||||
var centralDbContext = ServiceProperties.CentralDbContext as CentralDbContext;
|
||||
Assert.That(centralDbContext, Is.Not.Null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
using Strata.ApiCommunication.Http;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[TestFixture(Category = "Integration")]
|
||||
public class TestInitiativesController : ApiTestBase
|
||||
{
|
||||
protected const string InitiativesEndpoint = "api/v1/initiatives";
|
||||
protected const string InitiativesDetailColumnsEndpoint = "api/v1/initiative-detail-columns";
|
||||
|
||||
[Test]
|
||||
public async Task TestInitiatives()
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
var data = new
|
||||
{
|
||||
configurationGuid,
|
||||
name = "",
|
||||
status = 0,
|
||||
costDrivers = Array.Empty<string>(),
|
||||
costLeaders = Array.Empty<string>(),
|
||||
owners = Array.Empty<string>(),
|
||||
executiveSponsors = Array.Empty<string>(),
|
||||
workstreams = Array.Empty<string>(),
|
||||
rollupColumn1Values = Array.Empty<string>(),
|
||||
rollupColumn2Values = Array.Empty<string>(),
|
||||
rollupColumn3Values = Array.Empty<string>(),
|
||||
groupByCategory = "CostDriver"
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, InitiativesEndpoint);
|
||||
await request.SetContentAsJsonAsync(data);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var initiatives = await response.Content.ReadFromJsonAsync<IEnumerable<dynamic>>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(initiatives, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestInitiativesCriteria()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, InitiativesEndpoint + "/criteria");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var initiativesCriteria = await response.Content.ReadFromJsonAsync<IEnumerable<dynamic>>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(initiativesCriteria, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestInitiativesDetailColumnList()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "api/v1/initiative-detail-columns/column-list");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var detailColumns = await response.Content.ReadFromJsonAsync<IEnumerable<dynamic>>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(detailColumns, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestInitiativesDefaultColumnList()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "api/v1/initiative-detail-columns/default-column-list");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var detailColumns = await response.Content.ReadFromJsonAsync<IEnumerable<dynamic>>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(detailColumns, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestInitiativesFilterOptions()
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, InitiativesEndpoint + $"/{configurationGuid}/filter-options");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var filterOptions = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(filterOptions, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
|
||||
}
|
||||
[Test]
|
||||
public async Task TestInitiativesFilterSettings()
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, InitiativesEndpoint + $"/filter-settings");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var filterSettings = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(filterSettings, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestInitiativesExport()
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
var data = new
|
||||
{
|
||||
sortOptions = new
|
||||
{
|
||||
sortField = "name",
|
||||
sortOrder = "Asc"
|
||||
},
|
||||
filters = new
|
||||
{
|
||||
configurationGuid,
|
||||
groupByCategory = "CostDriver"
|
||||
}
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, InitiativesEndpoint + "/export?exportAll=true");
|
||||
await request.SetContentAsJsonAsync(data);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// assert
|
||||
response.AssertIsValid(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
using Strata.ApiCommunication.Http;
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Info;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[TestFixture(), Category("Integration"), Category("API")]
|
||||
public class TestInitiativesController_Export : ApiTestBase
|
||||
{
|
||||
protected const string InitiativesEndpoint = "api/v1/initiatives";
|
||||
private Guid ConfigurationGuid { get; set; } = Guid.Empty;
|
||||
private string Workstream { get; set; } = string.Empty;
|
||||
private object FilterSettings { get; set; }
|
||||
|
||||
[Test, Order(1)]
|
||||
public async Task TestInitiativesExport_Step1()
|
||||
{
|
||||
// arrange
|
||||
|
||||
// act
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
ConfigurationGuid = configurationGuid;
|
||||
|
||||
// assert
|
||||
Assert.That(ConfigurationGuid, Is.Not.EqualTo(Guid.Empty));
|
||||
}
|
||||
|
||||
[Test, Order(2)]
|
||||
public async Task TestInitiativesExport_Step2()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, InitiativesEndpoint + $"/{ConfigurationGuid}/filter-options");
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var filterOptions = await response.Content.ReadFromJsonAsync<InitiativeFilterOptionsInfo>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(filterOptions, Is.Not.Null);
|
||||
Assert.That(filterOptions.Workstreams, Is.Not.Null.And.Not.Empty);
|
||||
});
|
||||
var workstream = filterOptions.Workstreams.First().Name;
|
||||
Workstream = workstream;
|
||||
}
|
||||
|
||||
[Test, Order(3)]
|
||||
public async Task TestInitiativesExport_Step3()
|
||||
{
|
||||
// arrange
|
||||
FilterSettings = new
|
||||
{
|
||||
ConfigurationGuid,
|
||||
name = "",
|
||||
status = 0,
|
||||
costDrivers = Array.Empty<string>(),
|
||||
costLeaders = Array.Empty<string>(),
|
||||
owners = Array.Empty<string>(),
|
||||
executiveSponsors = Array.Empty<string>(),
|
||||
workstreams = new string[] { Workstream },
|
||||
rollupColumn1Values = Array.Empty<string>(),
|
||||
rollupColumn2Values = Array.Empty<string>(),
|
||||
rollupColumn3Values = Array.Empty<string>(),
|
||||
groupByCategory = "CostDriver"
|
||||
};
|
||||
|
||||
// act
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, InitiativesEndpoint + "/set-filter-settings");
|
||||
await request.SetContentAsJsonAsync(FilterSettings);
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
|
||||
// assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
[Test, Order(4)]
|
||||
public async Task TestInitiativesExport_Step4()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, InitiativesEndpoint);
|
||||
await request.SetContentAsJsonAsync(FilterSettings);
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
|
||||
// act
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
[Test, Order(5)]
|
||||
public async Task TestInitiativesExport_Step5()
|
||||
{
|
||||
// arrange
|
||||
var data = new
|
||||
{
|
||||
sortOptions = new
|
||||
{
|
||||
sortField = "name",
|
||||
sortOrder = "Asc"
|
||||
},
|
||||
filters = new
|
||||
{
|
||||
ConfigurationGuid,
|
||||
name = "",
|
||||
status = 0,
|
||||
costDrivers = Array.Empty<string>(),
|
||||
costLeaders = Array.Empty<string>(),
|
||||
owners = Array.Empty<string>(),
|
||||
executiveSponsors = Array.Empty<string>(),
|
||||
workstreams = new string[] { Workstream },
|
||||
rollupColumn1Values = Array.Empty<string>(),
|
||||
rollupColumn2Values = Array.Empty<string>(),
|
||||
rollupColumn3Values = Array.Empty<string>(),
|
||||
groupByCategory = "CostDriver"
|
||||
}
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, InitiativesEndpoint + "/export");
|
||||
await request.SetContentAsJsonAsync(data);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// assert
|
||||
var headers = response.Content.Headers.Where(x => x.Value != null)
|
||||
.ToDictionary(k => k.Key, v => v.Value);
|
||||
var disposition = headers["Content-Disposition"].First();
|
||||
var regex = new Regex("attachment; filename=Initiatives_[\\d]{2}-[\\d]{2}-[\\d]{4}\\.xlsx; filename\\*=UTF-8''Initiatives_[\\d]{2}-[\\d]{2}-[\\d]{4}\\.xlsx",
|
||||
RegexOptions.IgnoreCase);
|
||||
Assert.That(regex.IsMatch(disposition));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
using Strata.ApiCommunication.Http;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[TestFixture(Category = "Integration")]
|
||||
public class TestOpportunityController : ApiTestBase
|
||||
{
|
||||
[TestCaseSource(nameof(OpportunityControllerTestCases))]
|
||||
public async Task OpportunityControllerTest(string testName, string url, string sortField, object? filters, bool exportable)
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
sortField = sortField ?? "identifiedSavings";
|
||||
filters = filters ?? new
|
||||
{
|
||||
configurationGuid,
|
||||
opportunityTypes = Array.Empty<string>(),
|
||||
identifiedSavings = null as object,
|
||||
viewVisible = true,
|
||||
excludeInitiatives = false,
|
||||
opportunitySearch = ""
|
||||
};
|
||||
|
||||
var data = new
|
||||
{
|
||||
pagingOptions = new
|
||||
{
|
||||
sortField,
|
||||
sortOrder = "Desc",
|
||||
first = 0,
|
||||
rows = 25
|
||||
},
|
||||
filters,
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
await request.SetContentAsJsonAsync(data);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// asert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
});
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OpportunityControllerTestCases))]
|
||||
public async Task OpportunityControllerFilterTest(string testName, string url, string sortField, object? filters, bool exportable)
|
||||
{
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url + $"/{configurationGuid}/filter-options");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// asert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
});
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OpportunityControllerTestCases))]
|
||||
public async Task OpportunityControllerFilteExportTest(string testName, string url, string sortField, object? filters, bool exportable)
|
||||
{
|
||||
if (!exportable) { Assert.Ignore(); return; }
|
||||
// arrange
|
||||
var configurations = await GetConfigurations();
|
||||
var configuration = configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
var configurationGuid = configuration.ConfigurationGuid;
|
||||
sortField = sortField ?? "identifiedSavings";
|
||||
filters = filters ?? new
|
||||
{
|
||||
configurationGuid,
|
||||
opportunityTypes = Array.Empty<string>(),
|
||||
identifiedSavings = null as object,
|
||||
viewVisible = true,
|
||||
excludeInitiatives = false,
|
||||
opportunitySearch = ""
|
||||
};
|
||||
|
||||
var data = new
|
||||
{
|
||||
pagingOptions = new
|
||||
{
|
||||
sortField,
|
||||
sortOrder = "Desc",
|
||||
first = 0,
|
||||
rows = 25
|
||||
},
|
||||
filters,
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url + $"/export");
|
||||
await request.SetContentAsJsonAsync(data);
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request, CancellationToken.None);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// asert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid(exportable);
|
||||
});
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> OpportunityControllerTestCases()
|
||||
{
|
||||
yield return new TestCaseData("all opportunities", "/api/v1/all-opportunities", null, new
|
||||
{
|
||||
configurationGuid = "aa4cba4f-2506-462c-8e34-c45b21653988"
|
||||
}, true).SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("charge code opportunities", "/api/v1/charge-code-opps", null, new
|
||||
{
|
||||
configurationGuid = "aa4cba4f-2506-462c-8e34-c45b21653988",
|
||||
opportunityTypes = Array.Empty<string>(),
|
||||
identifiedSavings = null as object,
|
||||
viewVisible = true,
|
||||
excludeInitiatives = false,
|
||||
opportunitySearch = ""
|
||||
}, true).SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("encounter opportunities", "/api/v1/encounter-opportunities", null, null, true)
|
||||
.SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("free form opportunities", "/api/v1/free-form-opportunities", null, null, true)
|
||||
.SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("general ledger opportunities", "/api/v1/general-ledger-opportunities", null, null, true)
|
||||
.SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("payroll opportunities", "/api/v1/payroll-opportunities", null, null, true)
|
||||
.SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("qvi opportunities", "/api/v1/quality-variation-item-opportunities", null, null, true)
|
||||
.SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("qvi breakdowns", "/api/v1/quality-variation-item-breakdowns", "identifiedSavingsTotal", new
|
||||
{
|
||||
exportFiltered = false,
|
||||
configurationGuid = "aa4cba4f-2506-462c-8e34-c45b21653988",
|
||||
identifiedSavings = 0,
|
||||
breakdownIds = Array.Empty<string>(),
|
||||
qualityVariationCases = 0
|
||||
}, false).SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("qvi events", "/api/v1/quality-variation-item-events", "identifiedSavingsTotal", new
|
||||
{
|
||||
configurationGuid = "aa4cba4f-2506-462c-8e34-c45b21653988"
|
||||
}, false).SetName("{m} {0}");
|
||||
|
||||
yield return new TestCaseData("variation opportunities", "/api/v1/variation-opportunities", null, new
|
||||
{
|
||||
configurationGuid = "aa4cba4f-2506-462c-8e34-c45b21653988"
|
||||
}, true).SetName("{m} {0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Strata.ContinuousImprovement.Api.Test.Integration.Info;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture(Category = "Integration")]
|
||||
public class TestSecuirtyController : ApiTestBase
|
||||
{
|
||||
[Test]
|
||||
public async Task TestSecurityIdenties()
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/security/identities");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
var securityInfos = await response.Content.ReadFromJsonAsync<IEnumerable<SecurityInfo>>();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
Assert.That(securityInfos, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(securityInfos.Any(s => s.Title == "Administrator, CCI"), Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Integration.Tests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture(Category = "Integration")]
|
||||
public class TestUsersController : ApiTestBase
|
||||
{
|
||||
protected const string UsersEndpoint = "api/v1/users";
|
||||
|
||||
[TestCaseSource(nameof(TestUserCases))]
|
||||
public async Task TestUsers(string testName, string url, bool hasCostLeader, bool hasAaronHooper)
|
||||
{
|
||||
// arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, UsersEndpoint + $"/{url}");
|
||||
|
||||
// act
|
||||
var response = await HttpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
response.AssertIsValid();
|
||||
});
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> TestUserCases()
|
||||
{
|
||||
yield return new TestCaseData("administrators", "administrators", true, false)
|
||||
.SetName("{m} - {0}");
|
||||
yield return new TestCaseData("cost leaders", "cost-leaders", true, false)
|
||||
.SetName("{m} - {0}");
|
||||
yield return new TestCaseData("executive sponsors", "executive-sponsors", true, false)
|
||||
.SetName("{m} - {0}");
|
||||
yield return new TestCaseData("owners", "owners", true, true)
|
||||
.SetName("{m} - {0}");
|
||||
yield return new TestCaseData("users", "users", true, true)
|
||||
.SetName("{m} - {0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using ClosedXML.Excel;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NUnitSetup = NUnit.Framework.SetUpAttribute;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.ActionResults
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("ContentResult")]
|
||||
public class ExcelContentResultTests
|
||||
{
|
||||
public ExcelContentResult ExcelContentResult { get; set; }
|
||||
public IXLWorkbook Workbook { get; set; }
|
||||
|
||||
[NUnitSetup]
|
||||
public void Setup()
|
||||
{
|
||||
Workbook = new XLWorkbook();
|
||||
Workbook.AddWorksheet();
|
||||
ExcelContentResult = new ExcelContentResult(Workbook, "Export");
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void ExcelContentResultTest()
|
||||
{
|
||||
// Assert
|
||||
ExcelContentResult.Should().NotBeNull();
|
||||
ExcelContentResult.Workbook.Should().NotBeNull();
|
||||
ExcelContentResult.FileName.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void ExecuteResultTest()
|
||||
{
|
||||
// Arange
|
||||
var actionConext = new ActionContext();
|
||||
Action executeResult = () => ExcelContentResult.ExecuteResult(actionConext);
|
||||
|
||||
executeResult.Should().NotThrow();
|
||||
}
|
||||
|
||||
[Test(), Ignore("We don't currently use this implementation method")]
|
||||
public async Task ExecuteResultAsyncTest()
|
||||
{
|
||||
// Arange
|
||||
var actionConext = new ActionContext();
|
||||
Assert.DoesNotThrowAsync(async () => await ExcelContentResult.ExecuteResultAsync(actionConext));
|
||||
}
|
||||
}
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
using ClosedXML.Excel;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using MoreLinq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ApiLib.Standard.Models;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.All;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Export;
|
||||
using Strata.ContinuousImprovement.Biz.Filter;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.Pagination;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.FeatureFlags.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class AllOpportunityControllerTests
|
||||
{
|
||||
public const string ConfigurationGuid = "aa4cba4f-2506-462c-8e34-c45b21653988";
|
||||
|
||||
[TestCaseSource(nameof(GetOpportunityTestCases))]
|
||||
public async Task GetOpportunityTest(bool featureFlagSet, bool featureFlagIsOn, bool isSdtEmployee = true)
|
||||
{
|
||||
var result = await GetPagedApiResponse(featureFlagSet);
|
||||
var service = await GetAllOpportunityService(result);
|
||||
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor(isSdtEmployee: isSdtEmployee);
|
||||
var featureFlagServiceClient = featureFlagSet
|
||||
? TestUtilities.GetFeatureFlagServiceClient("ciflexibleexplorationenabled", featureFlagIsOn)
|
||||
: Mock.Of<IFeatureFlagServiceClient>();
|
||||
var controller = new AllOpportunitiesController(service, claimsPrincipalAccessor,
|
||||
featureFlagServiceClient,
|
||||
Mock.Of<ILogger<AllOpportunitiesController>>());
|
||||
var pagedResponse = await controller.GetOpportunities(new AllOpportunityLoadOptions
|
||||
{
|
||||
PagingOptions = new PagingOptions { First = 0, Rows = 20, SortField = "IdentifiedSavings", SortOrder = SortOrder.Desc },
|
||||
Filters = new AllFilters
|
||||
{
|
||||
ConfigurationGuid = new Guid(ConfigurationGuid)
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
Assert.That(result, Is.SameAs(pagedResponse));
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> GetOpportunityTestCases()
|
||||
{
|
||||
yield return new TestCaseData(true, false, true)
|
||||
.SetName(nameof(GetOpportunityTest) + " FeatureFlag Off SdtEmployee");
|
||||
yield return new TestCaseData(true, true, true)
|
||||
.SetName(nameof(GetOpportunityTest) + " FeatureFlag On SdtEmployee");
|
||||
yield return new TestCaseData(true, false, false)
|
||||
.SetName(nameof(GetOpportunityTest) + " FeatureFlag Off Not SdtEmployee");
|
||||
yield return new TestCaseData(true, true, false)
|
||||
.SetName(nameof(GetOpportunityTest) + " FeatureFlag On Not SdtEmployee");
|
||||
yield return new TestCaseData(false, false, true)
|
||||
.SetName(nameof(GetOpportunityTest) + " No FeatureFlag Off SdtEmployee");
|
||||
yield return new TestCaseData(false, true, true)
|
||||
.SetName(nameof(GetOpportunityTest) + " No FeatureFlag On SdtEmployee");
|
||||
yield return new TestCaseData(false, false, false)
|
||||
.SetName(nameof(GetOpportunityTest) + " No FeatureFlag Off Not SdtEmployee");
|
||||
yield return new TestCaseData(false, true, false)
|
||||
.SetName(nameof(GetOpportunityTest) + " No FeatureFlag On Not SdtEmployee");
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public async Task GetNameIsValidTest(bool isValid)
|
||||
{
|
||||
var result = await GetPagedApiResponse();
|
||||
var service = await GetAllOpportunityService(result);
|
||||
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var firstRow = result.Data.First();
|
||||
var validateName = new ValidateName
|
||||
{
|
||||
ConfigurationGuid = firstRow.ConfigurationGuid,
|
||||
OpportunityGuid = isValid ? Guid.NewGuid() : firstRow.OpportunityGuid,
|
||||
Name = firstRow.Name,
|
||||
OpportunityType = firstRow.OpportunityType
|
||||
};
|
||||
var controller = new AllOpportunitiesController(service, claimsPrincipalAccessor,
|
||||
Mock.Of<IFeatureFlagServiceClient>(),
|
||||
Mock.Of<ILogger<AllOpportunitiesController>>());
|
||||
var isNameValid = await controller.IsNameValid(validateName, CancellationToken.None);
|
||||
Assert.That(isValid, Is.EqualTo(isNameValid));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public async Task GetFilterOptionsTest(bool isSdtEmployee)
|
||||
{
|
||||
var result = await GetPagedApiResponse();
|
||||
var service = await GetAllOpportunityService(result);
|
||||
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor(isSdtEmployee: isSdtEmployee);
|
||||
var controller = new AllOpportunitiesController(service, claimsPrincipalAccessor,
|
||||
Mock.Of<IFeatureFlagServiceClient>(),
|
||||
Mock.Of<ILogger<AllOpportunitiesController>>());
|
||||
var filterOptions = new AllFilterOptions
|
||||
{
|
||||
OpportunityTypes = Enum.GetValues<OpportunityTypes>()
|
||||
.Where(ot => isSdtEmployee || ot != OpportunityTypes.Blank)
|
||||
.Select(ot => new FilterMember { Id = $"{(int)ot}", Text = ot.OpportunityTypeName() })
|
||||
};
|
||||
var options = await controller.GetFilterOptionsAsync(result.Data.First().ConfigurationGuid, CancellationToken.None);
|
||||
filterOptions.OpportunityTypes
|
||||
.ForEach(ot => Assert.That(options.OpportunityTypes.Any(x => x.Id == ot.Id && x.Text == ot.Text), Is.True));
|
||||
}
|
||||
|
||||
[Test, Ignore("Ignoring because we don't have generic GetById endpoint to use here.")]
|
||||
public async Task SetHiddenAsyncTest()
|
||||
{
|
||||
var result = await GetPagedApiResponse();
|
||||
var service = await GetAllOpportunityService(result);
|
||||
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var controller = new AllOpportunitiesController(service, claimsPrincipalAccessor,
|
||||
Mock.Of<IFeatureFlagServiceClient>(),
|
||||
Mock.Of<ILogger<AllOpportunitiesController>>());
|
||||
var firstRow = result.Data.First(row => row.OpportunityType == (int)OpportunityTypes.Variation);
|
||||
await controller.SetHiddenAsync(firstRow.OpportunityGuid, !firstRow.IsHidden, CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task ExcelExportText()
|
||||
{
|
||||
var result = await GetPagedApiResponse();
|
||||
var service = await GetAllOpportunityService(result);
|
||||
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var controller = new AllOpportunitiesController(service, claimsPrincipalAccessor,
|
||||
Mock.Of<IFeatureFlagServiceClient>(),
|
||||
Mock.Of<ILogger<AllOpportunitiesController>>());
|
||||
var export = await controller.Export(new AllOpportunityExportOptions
|
||||
{
|
||||
SortOptions = new SortOptions(),
|
||||
Filters = new AllFilters()
|
||||
}, CancellationToken.None);
|
||||
Assert.That(export, Is.AssignableFrom<ExcelContentResult>());
|
||||
var workbook = ((ExcelContentResult)export).Workbook;
|
||||
var filename = ((ExcelContentResult)export).FileName;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(workbook.Worksheets.First().Rows().Count(), Is.GreaterThan(result.Data.Count()));
|
||||
Assert.That(filename, Is.Not.Null);
|
||||
Assert.That(filename, Is.Not.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<PagedApiResponse<AllOpportunity>> GetPagedApiResponse(bool withExplorationOpportunities = true)
|
||||
{
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
var dbContext = await jeff.CreateDbContextAsync(nameof(AllOpportunityControllerTests), CancellationToken.None);
|
||||
var result = new PagedApiResponse<AllOpportunity>
|
||||
{
|
||||
Data = withExplorationOpportunities
|
||||
? dbContext.AllOpportunities
|
||||
: dbContext.AllOpportunities.Where(opp => opp.OpportunityType != (int)OpportunityTypes.Blank),
|
||||
PagingInfo = new PagingInfo
|
||||
{
|
||||
ResultsPerPage = 20,
|
||||
TotalNumberOfResults = dbContext.AllOpportunities.Count(),
|
||||
TotalPages = dbContext.AllOpportunities.Count() / 20,
|
||||
PageNumber = 1
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<IAllOpportunityService> GetAllOpportunityService(PagedApiResponse<AllOpportunity> pagedApiResponse = null)
|
||||
{
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
var dbContext = await jeff.CreateDbContextAsync(nameof(AllOpportunityControllerTests), CancellationToken.None);
|
||||
var service = Mock.Of<IAllOpportunityService>();
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.GetPagedAsync(It.IsAny<PagingOptions>(), It.IsAny<IFilters<AllOpportunity>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(pagedApiResponse ?? await GetPagedApiResponse()));
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.NameIsValid(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<Guid?>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, int, string, Guid?, CancellationToken>((config, oppType, name, oppGuid, token)
|
||||
=> Task.FromResult(!dbContext.AllOpportunities.Any(opp
|
||||
=> opp.ConfigurationGuid == config
|
||||
&& opp.OpportunityType == oppType
|
||||
&& opp.OpportunityGuid == oppGuid
|
||||
&& opp.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))));
|
||||
var filterOptions = new AllFilterOptions
|
||||
{
|
||||
OpportunityTypes = Enum.GetValues<OpportunityTypes>()
|
||||
.Select(ot => new FilterMember { Id = $"{(int)ot}", Text = ot.OpportunityTypeName() })
|
||||
};
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.GetFilterOptionsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>((config, token) => Task.FromResult(filterOptions));
|
||||
|
||||
var exportOptions = new ExportOptions() { EmptyMessage = "No Opportunities" };
|
||||
// configure column formats
|
||||
exportOptions.AddColumnOptions("Identified Savings", "Currency2Dec");
|
||||
exportOptions.AddColumnOptions("Is Hidden", "\"Yes\";-;\"No\"");
|
||||
var xlWorkbook = (IXLWorkbook)ExportUtils.CreateExcelWorkbook(dbContext.AllOpportunities, exportOptions);
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.ExportAsync(It.IsAny<SortOptions>(), It.IsAny<AllFilters>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(xlWorkbook));
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.SetHiddenAsync(It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.SetNoteAsync(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>((oppGuid, token) => Task.FromResult(dbContext.AllOpportunities.FirstOrDefault(opp => opp.OpportunityGuid == oppGuid)));
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Mappers;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ChargeCodeInitiativesControllerTests
|
||||
{
|
||||
|
||||
[TestCaseSource(nameof(DevDbTestCases1))]
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
public async Task Test(ChargeCodeInitiativesController controller, Guid opportunityGuid, Guid expected)
|
||||
{
|
||||
var dto = new InitiativeDto
|
||||
{
|
||||
OpportunityGuid = opportunityGuid,
|
||||
TrackingStart = DateTime.Now.ToString("yyyy/MM/dd")
|
||||
};
|
||||
var result = await controller.Create(dto, CancellationToken.None);
|
||||
var actual = result.Value;
|
||||
actual.Should().Be(expected);
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<TestCaseData> DevDbTestCases1()
|
||||
{
|
||||
var foundOpportunityGuid = Guid.NewGuid();
|
||||
var notFoundOpportunityGuid = Guid.NewGuid();
|
||||
var newInitiative = Guid.NewGuid();
|
||||
var stakeholderUsers = Mock.Of<IStakeholderUser>();
|
||||
var clientClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var service = Mock.Of<ICCInitiativeService>();
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.CreateChargeCodeInitiative(It.IsAny<Guid>(), It.IsAny<Initiative>(),
|
||||
It.IsIn(new[] { foundOpportunityGuid }.AsEnumerable()), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(newInitiative));
|
||||
var controller = new ChargeCodeInitiativesController(clientClaimsPrincipalAccessor, service, stakeholderUsers);
|
||||
|
||||
var notFoundService = Mock.Of<ICCInitiativeService>();
|
||||
Mock.Get(notFoundService)
|
||||
.Setup(s => s.CreateChargeCodeInitiative(It.IsAny<Guid>(), It.IsAny<Initiative>(),
|
||||
It.IsNotIn(new[] { foundOpportunityGuid }.AsEnumerable()), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(Guid.Empty));
|
||||
var notFoundController = new ChargeCodeInitiativesController(clientClaimsPrincipalAccessor, notFoundService, stakeholderUsers);
|
||||
|
||||
yield return new TestCaseData(controller, foundOpportunityGuid, newInitiative)
|
||||
.SetName("Dev Db Opportunity Found");
|
||||
yield return new TestCaseData(controller, notFoundOpportunityGuid, Guid.Empty)
|
||||
.SetName("Dev Db Opportunity Not Found");
|
||||
yield return new TestCaseData(notFoundController, notFoundOpportunityGuid, Guid.Empty)
|
||||
.SetName("Dev Db Opportunity Not Found 1");
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> TestCases()
|
||||
{
|
||||
var foundOpportunityGuid = Guid.NewGuid();
|
||||
var notFoundOpportunityGuid = Guid.NewGuid();
|
||||
var stakeholderUsers = Mock.Of<IStakeholderUser>();
|
||||
var service = Mock.Of<ICCInitiativeService>();
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.CreateChargeCodeInitiative(It.IsIn(new[] { new Guid(TestUtilities.ClientDbGuid) }.AsEnumerable()),
|
||||
It.IsAny<Initiative>(), It.IsIn(new[] { foundOpportunityGuid }.AsEnumerable()), It.IsAny<CancellationToken>()))
|
||||
.Throws<Exception>();
|
||||
var clientClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor(isClientDatabase: true);
|
||||
var controller = new ChargeCodeInitiativesController(clientClaimsPrincipalAccessor, service, stakeholderUsers);
|
||||
|
||||
yield return new TestCaseData(controller, foundOpportunityGuid, Guid.Empty)
|
||||
.SetName("Client Db Opportunity Found");
|
||||
yield return new TestCaseData(controller, notFoundOpportunityGuid, Guid.Empty)
|
||||
.SetName("Client Db Opportunity Not Found");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
using ClosedXML.Excel;
|
||||
using DocumentFormat.OpenXml.Drawing.Diagrams;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.OrgDefined;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub.Generics;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.FeatureFlags.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnitSetup = NUnit.Framework.SetUpAttribute;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ChargeCodeOpportunitiesControllerTests
|
||||
{
|
||||
public JazzDbContext DbContext { get; set; }
|
||||
public ChargeCodeOpportunitiesController Controller { get; set; }
|
||||
public IEnumerable<Biz.Configuration.Configuration> Configurations { get; set; }
|
||||
public IClaimsPrincipalAccessor ClaimsPrincipalAccessor { get; set; }
|
||||
public IConfigurationService ConfigurationService { get; set; }
|
||||
public ICCOpportunityService OpportunityService;
|
||||
public ICCInitiativeService InitiativeService;
|
||||
public IOpportunityMetricCohortService OpportunityMetricCohortService;
|
||||
public IFeatureFlagServiceClient FeatureFlagServiceClient;
|
||||
public IAllOpportunityService AllOpportunityService;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task ConfigurationsControllerTestSetup()
|
||||
{
|
||||
// Arrange
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
DbContext = await jeff.CreateDbContextAsync(nameof(ConfigurationsControllerTests), CancellationToken.None);
|
||||
Configurations = jeff.Configurations.ToList();
|
||||
var configuration = jeff.Configurations.First(c => c.ConfigurationGuid == JazzEntityFrameworkFactory.CurrentConfigurationGuid);
|
||||
var ccOpportunityDataModel = jeff.CCOpportunities[0].MapToDataModel(configuration);
|
||||
var workbook = new XLWorkbook();
|
||||
|
||||
// Act
|
||||
ClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
FeatureFlagServiceClient = TestUtilities.GetFeatureFlagServiceClient("chargecodebeta", true);
|
||||
ConfigurationService = Mock.Of<IConfigurationService>();
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetConfigurationsAsync(It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(Configurations.AsEnumerable()));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetConfigurationAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(configuration));
|
||||
AllOpportunityService = Mock.Of<IAllOpportunityService>();
|
||||
Mock.Get(AllOpportunityService)
|
||||
.Setup(s => s.NameIsValid(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<Guid?>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
OpportunityService = Mock.Of<ICCOpportunityService>();
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.UpdateAsync(It.IsAny<Guid>(), It.IsAny<CCOpportunityDataModel>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(Guid.NewGuid().ToString()));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.ValidateChargeCodesAsync(It.IsAny<List<string>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.ValidateOpportunityLevelsAsync(It.IsAny<OpportunityLevel>(), It.IsAny<List<string>>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.DeleteAsync<CCOpportunityDataModel>(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>((oppGuid, token)
|
||||
=> oppGuid == ccOpportunityDataModel.OpportunityGuid
|
||||
? Task.FromResult(ccOpportunityDataModel)
|
||||
: Task.FromResult((CCOpportunityDataModel)null));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.ExportAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, IEnumerable<string>, string, CancellationToken>((oppGuid, chargeCodes, userName, token)
|
||||
=> oppGuid == ccOpportunityDataModel.OpportunityGuid
|
||||
? Task.FromResult(((IXLWorkbook)workbook))
|
||||
: Task.FromResult(((IXLWorkbook)null)));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.UpdateGoalTotalUnitsOfServiceAsync(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<double>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, string, double, CancellationToken>((oppGuid, key, value, token)
|
||||
=> oppGuid == ccOpportunityDataModel.OpportunityGuid
|
||||
? Task.FromResult(true)
|
||||
: Task.FromResult(false));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.UpdateGoalCostPerUnitAsync(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<double>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, string, double, CancellationToken>((oppGuid, key, goalValue, token)
|
||||
=> oppGuid == ccOpportunityDataModel.OpportunityGuid
|
||||
? Task.FromResult(true)
|
||||
: Task.FromResult(false));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.GetInitiativeInfoAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(new InitiativeInfo()));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.RefreshOpportunityTrackingData(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, Guid, CancellationToken>((dbGuid, oppGuid, token)
|
||||
=> dbGuid.ToString() == TestUtilities.DbGuid && oppGuid == ccOpportunityDataModel.OpportunityGuid
|
||||
? Task.FromResult("Good")
|
||||
: Task.FromResult(""));
|
||||
InitiativeService = Mock.Of<ICCInitiativeService>();
|
||||
Mock.Get(InitiativeService)
|
||||
.Setup(s => s.UpdateSavings(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
OpportunityMetricCohortService = Mock.Of<IOpportunityMetricCohortService>();
|
||||
}
|
||||
|
||||
[NUnitSetup]
|
||||
public void TestSetup()
|
||||
{
|
||||
// Arrange
|
||||
Controller = new ChargeCodeOpportunitiesController(ClaimsPrincipalAccessor, ConfigurationService, OpportunityService, InitiativeService, OpportunityMetricCohortService, FeatureFlagServiceClient, AllOpportunityService);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void ChargeCodeOpportunitiesControllerTest()
|
||||
{
|
||||
// Act
|
||||
var controller = new ChargeCodeOpportunitiesController(ClaimsPrincipalAccessor, ConfigurationService, OpportunityService, InitiativeService, OpportunityMetricCohortService, FeatureFlagServiceClient, AllOpportunityService);
|
||||
|
||||
// Assert
|
||||
controller.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task UpdateOpportunityTest()
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstOrDefaultAsync(CancellationToken.None);
|
||||
var opportunity = opp.MapToSaveModel();
|
||||
|
||||
// Act
|
||||
var result = await Controller.UpdateOpportunity(opportunity.OpportunityGuid, opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var actual = result.Result;
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)result.Result;
|
||||
okResult.Value.Should().BeOfType<string>();
|
||||
var value = new Guid(okResult.Value as string);
|
||||
value.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ValidateChargeCodesAsyncTest()
|
||||
{
|
||||
// Arrange
|
||||
var chargeCodes = new List<string>();
|
||||
|
||||
// Act
|
||||
var result = await Controller.ValidateChargeCodesAsync(new ChargeCodeOpportunitiesController.ValidateChargeCodeDto() { ChargeCodes = chargeCodes }, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)result.Result;
|
||||
okResult.Value.Should().BeOfType<bool>();
|
||||
var value = (bool)okResult.Value;
|
||||
value.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetCustomPickerChargeCodesAsyncTest()
|
||||
{
|
||||
// Arrange
|
||||
var chargeCodes = new List<string>();
|
||||
|
||||
// Act
|
||||
var result = await Controller.GetChargeCodeMembers(chargeCodes, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)result.Result;
|
||||
okResult.Value.Should().BeOfType<ChargeCodePickerModel[]>();
|
||||
var value = (ChargeCodePickerModel[])okResult.Value;
|
||||
value.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ValidateOpportunityLevelsAsyncTest()
|
||||
{
|
||||
// Arrange
|
||||
var info = new ChargeCodeOpportunitiesController.OpportunityLevelSelectionInfo
|
||||
{
|
||||
Configuration = Configurations.First().ConfigurationGuid,
|
||||
OpportunityLevel = OpportunityLevel.Entity,
|
||||
OpportunityLevels = new List<string>()
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await Controller.ValidateOpportunityLevelsAsync(info, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)result.Result;
|
||||
okResult.Value.Should().BeOfType<bool>();
|
||||
var value = (bool)okResult.Value;
|
||||
value.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task DeleteTest()
|
||||
{
|
||||
// Act
|
||||
var result = await Controller.Delete(Guid.NewGuid(), CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "GetByIdTest - true")]
|
||||
[TestCase(false, TestName = "GetByIdTest - false")]
|
||||
public async Task GetByIdTest(bool withOpp)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstAsync(CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var result = await Controller.GetById(withOpp ? opp.OpportunityGuid : Guid.NewGuid(), CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
if (withOpp)
|
||||
{
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)result.Result;
|
||||
okResult.Value.Should().BeOfType<CCOpportunityDto>()
|
||||
.And.NotBeOfType<CCOpportunityDataModel>();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "GetEditModelTest - true")]
|
||||
[TestCase(false, TestName = "GetEditModelTest - false")]
|
||||
public async Task GetEditModelTest(bool withOpp)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstAsync(CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var result = await Controller.GetEditModel(withOpp ? opp.OpportunityGuid : Guid.NewGuid(), CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
if (withOpp)
|
||||
{
|
||||
result.Should().BeOfType<CCOpportunitySaveModel>();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "UpdateGoalTotalUnitsOfServiceTest - true")]
|
||||
[TestCase(false, TestName = "UpdateGoalTotalUnitsOfServiceTest - false")]
|
||||
public async Task UpdateGoalTotalUnitsOfServiceTest(bool withOpp)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstAsync(CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var result = await Controller.UpdateGoalTotalUnitsOfService(withOpp ? opp.OpportunityGuid : Guid.NewGuid(), new ChargeCodeOpportunitiesController.GoalSetDto(), CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().Be(withOpp);
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "UpdateGoalCostPerUnitTest - true")]
|
||||
[TestCase(false, TestName = "UpdateGoalCostPerUnitTest - false")]
|
||||
public async Task UpdateGoalCostPerUnitTest(bool withOpp)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstAsync(CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var result = await Controller.UpdateGoalCostPerUnit(withOpp ? opp.OpportunityGuid : Guid.NewGuid(), new ChargeCodeOpportunitiesController.GoalSetDto(), CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().Be(withOpp);
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "ExportTest - true")]
|
||||
[TestCase(false, TestName = "ExportTest - false")]
|
||||
public async Task ExportTest(bool withOpp)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstAsync(CancellationToken.None);
|
||||
var chargeCodeItems = new ChargeCodeOpportunitiesController.ChargeCodeItems
|
||||
{
|
||||
ChargeCodes = new List<string>()
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await Controller.Export(withOpp ? opp.OpportunityGuid : Guid.NewGuid(), chargeCodeItems, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeOfType<ExcelContentResult>();
|
||||
var excelContentResult = (ExcelContentResult)result;
|
||||
if (withOpp)
|
||||
{
|
||||
excelContentResult.Workbook.Should().NotBeNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
excelContentResult.Workbook.Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task OpportunityCreateEnabledTest()
|
||||
{
|
||||
// Act
|
||||
var result = await Controller.OpportunityCreateEnabled(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetInitiativeInfoTest()
|
||||
{
|
||||
// Act
|
||||
var result = await Controller.GetInitiativeInfo(Guid.NewGuid(), CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeOfType<InitiativeInfo>()
|
||||
.And.NotBeNull();
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "RefreshTest - true")]
|
||||
[TestCase(false, TestName = "RefreshTest - false")]
|
||||
public async Task RefreshTest(bool withOpp)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpportunities.FirstAsync(CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var result = await Controller.Refresh(new ChargeCodeOpportunitiesController.RefreshData
|
||||
{
|
||||
DatabaseGuid = new Guid(TestUtilities.DbGuid),
|
||||
Guid = withOpp ? opp.OpportunityGuid : Guid.NewGuid()
|
||||
}, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeOfType<string>();
|
||||
if (withOpp)
|
||||
{
|
||||
result.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().BeNullOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Api.Test.Unit.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Jobs;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ChargeCodeOpportunityDetailsControllerTests
|
||||
{
|
||||
public ChargeCodeOpportunityDetailsController Controller { get; set; }
|
||||
public FakeBackgroundJobClient BackgroundJobClient { get; set; }
|
||||
private Mock<ICCOpportunityClientJob> _mockClientJob;
|
||||
private ILogger<ChargeCodeOpportunityDetailsController> _logger;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void TestSetup()
|
||||
{
|
||||
// Arrange
|
||||
BackgroundJobClient ??= new FakeBackgroundJobClient();
|
||||
_mockClientJob = new Mock<ICCOpportunityClientJob>();
|
||||
_logger = Mock.Of<ILogger<ChargeCodeOpportunityDetailsController>>();
|
||||
|
||||
_mockClientJob
|
||||
.Setup(j => j.RefreshClientOpportunityBaselineDetails(It.IsAny<int>(), It.Is<Guid>(g => g != Guid.Empty), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync("baseline-job-123");
|
||||
_mockClientJob
|
||||
.Setup(j => j.RefreshClientOpportunityTrackingDetails(It.IsAny<int>(), It.Is<Guid>(g => g != Guid.Empty), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync("tracking-job-456");
|
||||
_mockClientJob
|
||||
.Setup(j => j.RefreshClientOpportunityBaselineDetails(It.IsAny<int>(), Guid.Empty, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new InvalidOperationException("Opportunity missing"));
|
||||
_mockClientJob
|
||||
.Setup(j => j.RefreshClientOpportunityTrackingDetails(It.IsAny<int>(), Guid.Empty, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new InvalidOperationException("Initiative missing"));
|
||||
|
||||
Controller = new ChargeCodeOpportunityDetailsController(BackgroundJobClient, _mockClientJob.Object, _logger);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChargeCodeOpportunityDetailsControllerTest()
|
||||
{
|
||||
// Arrange
|
||||
var controller = new ChargeCodeOpportunityDetailsController(BackgroundJobClient, _mockClientJob.Object, _logger);
|
||||
|
||||
// Assert
|
||||
controller.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateTest()
|
||||
{
|
||||
// Arrange
|
||||
Action create = () => Controller.Create(new ChargeCodeOpportunityDetailsController.ChargeCodeDetailInfo
|
||||
{
|
||||
StrataId = 9,
|
||||
OpportunityGuid = Guid.NewGuid(),
|
||||
IsBaseline = true,
|
||||
IsUpdate = true
|
||||
}, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
create.Should().NotThrow();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RefreshBaselineDetails_ReturnsOk_OnSuccess()
|
||||
{
|
||||
var request = new ChargeCodeOpportunityDetailsController.ChargeCodeOpportunityJobRequestInfo
|
||||
{
|
||||
StrataId = 9,
|
||||
OpportunityGuid = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var result = await Controller.RefreshBaselineDetails(request, CancellationToken.None);
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
ok.Value.Should().Be("baseline-job-123");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RefreshTrackingDetails_ReturnsOk_OnSuccess()
|
||||
{
|
||||
var request = new ChargeCodeOpportunityDetailsController.ChargeCodeOpportunityJobRequestInfo
|
||||
{
|
||||
StrataId = 9,
|
||||
OpportunityGuid = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var result = await Controller.RefreshTrackingDetails(request, CancellationToken.None);
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
ok.Value.Should().Be("tracking-job-456");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RefreshBaselineDetails_ReturnsBadRequest_OnException()
|
||||
{
|
||||
var request = new ChargeCodeOpportunityDetailsController.ChargeCodeOpportunityJobRequestInfo
|
||||
{
|
||||
StrataId = 9,
|
||||
OpportunityGuid = Guid.Empty
|
||||
};
|
||||
|
||||
var result = await Controller.RefreshBaselineDetails(request, CancellationToken.None);
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RefreshTrackingDetails_ReturnsBadRequest_OnException()
|
||||
{
|
||||
var request = new ChargeCodeOpportunityDetailsController.ChargeCodeOpportunityJobRequestInfo
|
||||
{
|
||||
StrataId = 9,
|
||||
OpportunityGuid = Guid.Empty
|
||||
};
|
||||
|
||||
var result = await Controller.RefreshTrackingDetails(request, CancellationToken.None);
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
using ClosedXML.Excel;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ApiLib.Standard.Models;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Filter;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityComments;
|
||||
using Strata.ContinuousImprovement.Biz.Pagination;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnitSetup = NUnit.Framework.SetUpAttribute;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ChargeCodeOppsControllerTests
|
||||
{
|
||||
public JazzDbContext DbContext { get; set; }
|
||||
public ChargeCodeOppsController Controller { get; set; }
|
||||
public ICommentService CommentService { get; set; }
|
||||
public ICCOpportunityViewService OpportunityService { get; set; }
|
||||
public ICCInitiativeService InitiativeService { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task ConfigurationsControllerTestSetup()
|
||||
{
|
||||
// Arrange
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
DbContext = await jeff.CreateDbContextAsync(nameof(ChargeCodeOppsControllerTests), CancellationToken.None);
|
||||
var expectedData = jeff.CCOpportunities;
|
||||
var expected = expectedData[0];
|
||||
var expectedage = new PagedApiResponse<CCOpportunity>
|
||||
{
|
||||
Data = expectedData,
|
||||
PagingInfo = new PagingInfo
|
||||
{
|
||||
TotalNumberOfResults = expectedData.Count,
|
||||
TotalPages = 1,
|
||||
ResultsPerPage = expectedData.Count,
|
||||
PageNumber = 1
|
||||
}
|
||||
};
|
||||
var workbook = new XLWorkbook();
|
||||
workbook.AddWorksheet("ChargeCodeOpps");
|
||||
CommentService = Mock.Of<ICommentService>();
|
||||
OpportunityService = Mock.Of<ICCOpportunityViewService>();
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.GetPagedAsync(It.IsAny<PagingOptions>(), It.IsAny<IFilters<CCOpportunity>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(expectedage));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.ExportAsync(It.IsAny<SortOptions>(), It.IsAny<CCFilters>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult((IXLWorkbook)workbook));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.GetFilterOptionsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(new CCFilterOptions()));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>((oppGuid, token)
|
||||
=> oppGuid == expected.OpportunityGuid
|
||||
? Task.FromResult(expected)
|
||||
: Task.FromResult((CCOpportunity)null));
|
||||
var hiddenExpected = expected.IsHidden = !expectedData[0].IsHidden;
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.SetHiddenAsync(It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, bool, CancellationToken>((oppGuid, isHidden, token)
|
||||
=> oppGuid == expected.OpportunityGuid
|
||||
? Task.CompletedTask
|
||||
: Task.FromResult((CCOpportunity)null));
|
||||
Mock.Get(OpportunityService)
|
||||
.Setup(s => s.SetNoteAsync(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, string, CancellationToken>((oppGuid, note, token)
|
||||
=> oppGuid == expected.OpportunityGuid
|
||||
? Task.CompletedTask
|
||||
: Task.FromResult((CCOpportunity)null));
|
||||
InitiativeService = Mock.Of<ICCInitiativeService>();
|
||||
}
|
||||
|
||||
[NUnitSetup]
|
||||
public void TestSetup()
|
||||
{
|
||||
// Arrange
|
||||
Controller = new ChargeCodeOppsController(OpportunityService);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void ChargeCodeOppsControllerTest()
|
||||
{
|
||||
Controller.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetOpportunitiesTest()
|
||||
{
|
||||
// Arrange
|
||||
var loadOptions = new ChargeCodeOppsController.CCOpportunityLoadOptions
|
||||
{
|
||||
PagingOptions = new PagingOptions
|
||||
{
|
||||
First = 1,
|
||||
Rows = int.MaxValue,
|
||||
SortField = "IdentifiedSavings",
|
||||
SortOrder = SortOrder.Desc
|
||||
},
|
||||
Filters = new CCFilters
|
||||
{
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await Controller.GetOpportunities(loadOptions, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeOfType<PagedApiResponse<CCOpportunity>>();
|
||||
result.PagingInfo.Should().NotBeNull();
|
||||
result.Data.Should().NotBeNull();
|
||||
result.Data.Count().Should().Be(result.PagingInfo.TotalNumberOfResults);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ExportTest()
|
||||
{
|
||||
// Arrange
|
||||
var exportOptions = new ChargeCodeOppsController.CCOpportunityExportOptions
|
||||
{
|
||||
Filters = new CCFilters
|
||||
{
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await Controller.Export(exportOptions, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ExcelContentResult>();
|
||||
var excelContentResult = result as ExcelContentResult;
|
||||
excelContentResult.Should().NotBeNull();
|
||||
excelContentResult.FileName.Should().NotBeNullOrEmpty();
|
||||
excelContentResult.Workbook.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetFilterOptionsAsyncTest()
|
||||
{
|
||||
// Act
|
||||
var result = await Controller.GetFilterOptionsAsync(JazzEntityFrameworkFactory.CurrentConfigurationGuid, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<CCFilterOptions>();
|
||||
}
|
||||
|
||||
// commenting out because we don't have getById endpoint test should be redone
|
||||
[TestCase(true, TestName = "GetByIdTest - true", Ignore = "When I debug it passes, otherwise it doesn't")]
|
||||
[TestCase(false, TestName = "GetByIdTest - false")]
|
||||
public async Task GetByIdTest(bool withOpp)
|
||||
{
|
||||
//// Arrange
|
||||
//var opp = await DbContext.ChargeCodeOpps.FirstOrDefaultAsync(CancellationToken.None);
|
||||
|
||||
//// Act
|
||||
|
||||
////var result = await Controller.GetById(withOpp ? opp.OpportunityGuid : Guid.NewGuid(), CancellationToken.None);
|
||||
|
||||
//// Assert
|
||||
//if (withOpp)
|
||||
//{
|
||||
// result.Should().NotBeNull();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// result.Should().BeNull();
|
||||
//}
|
||||
}
|
||||
|
||||
[TestCase(true, true, TestName = "SetHiddenAsyncTest - true - isHidden")]
|
||||
[TestCase(true, false, TestName = "SetHiddenAsyncTest - true - not isHidden")]
|
||||
[TestCase(false, true, TestName = "SetHiddenAsyncTest - false - isHidden")]
|
||||
[TestCase(false, false, TestName = "SetHiddenAsyncTest - false - not isHidden")]
|
||||
public async Task SetHiddenAsyncTest(bool withOpp, bool isHidden)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpps.FirstOrDefaultAsync(CancellationToken.None);
|
||||
var opportunityGuid = withOpp ? opp.OpportunityGuid : Guid.NewGuid();
|
||||
|
||||
// Assert
|
||||
Assert.DoesNotThrowAsync(async () => // Act
|
||||
await Controller.SetHiddenAsync(opportunityGuid, isHidden, CancellationToken.None));
|
||||
}
|
||||
|
||||
[TestCase(true, "", TestName = "SetNoteAsyncTest - true - note")]
|
||||
[TestCase(true, null, TestName = "SetNoteAsyncTest - true - null")]
|
||||
[TestCase(false, "", TestName = "SetNoteAsyncTest - false - not")]
|
||||
[TestCase(false, null, TestName = "SetNoteAsyncTest - false - null")]
|
||||
public async Task SetNoteAsyncTest(bool withOpp, string note)
|
||||
{
|
||||
// Arrange
|
||||
var opp = await DbContext.ChargeCodeOpps.FirstOrDefaultAsync(CancellationToken.None);
|
||||
var opportunityGuid = withOpp ? opp.OpportunityGuid : Guid.NewGuid();
|
||||
|
||||
// Assert
|
||||
Assert.DoesNotThrowAsync(async () => // Act
|
||||
await Controller.SetNoteAsync(opportunityGuid, note, CancellationToken.None));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
using DocumentFormat.OpenXml.Drawing.Diagrams;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ApiLib.Standard.Models;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Api.Test.Unit.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Network;
|
||||
using Strata.ContinuousImprovement.Biz.Pagination;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.CoreLib.Claims;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnitSetup = NUnit.Framework.SetUpAttribute;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ClientNetworkOpportunitiesControllerTests
|
||||
{
|
||||
public CentralDbContext DbContext { get; set; }
|
||||
public ClientNetworkOpportunitiesController Controller { get; set; }
|
||||
public ClientNetworkOpportunitiesController EmptyDbController { get; set; }
|
||||
public ClientNetworkOpportunitiesController ControllerExceptions { get; set; }
|
||||
public IClaimsPrincipalAccessor ClaimsPrincipalAccessor { get; set; }
|
||||
public IClaimsPrincipalAccessor EnptyDbClaimsPrincipalAccessor { get; set; }
|
||||
public IClientNetworkOpportunityService ClientNetworkOpportunityService { get; set; }
|
||||
public IClientNetworkOpportunityService ClientNetworkOpportunityServiceException { get; set; }
|
||||
public ILogger<ClientNetworkOpportunitiesController> Logger { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task ClientNetworkOpportunitiesControllerTestsSetup()
|
||||
{
|
||||
// Arrange
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
DbContext = await jeff.CreateCentralDbContextAsync(nameof(ClientNetworkOpportunitiesControllerTests), CancellationToken.None);
|
||||
var expectedData = jeff.ClientNetworkOpportunities;
|
||||
var opportunity = expectedData[0];
|
||||
var expected = new PagedApiResponse<NetworkOpportunitySummary>
|
||||
{
|
||||
Data = expectedData.Select(d => new NetworkOpportunitySummary
|
||||
{
|
||||
NetworkOpportunityId = d.NetworkOpportunityId,
|
||||
Name = d.NetworkOpportunity.Name,
|
||||
AnalyticsWorkbookId = d.AnalyticsWorkbookId,
|
||||
CostDriver = d.NetworkOpportunity.CostDriver,
|
||||
Description = d.NetworkOpportunity.Description,
|
||||
IsPublished = d.IsPublished
|
||||
}),
|
||||
PagingInfo = new PagingInfo
|
||||
{
|
||||
PageNumber = 1,
|
||||
TotalNumberOfResults = expectedData.Count,
|
||||
TotalPages = 1,
|
||||
ResultsPerPage = expectedData.Count
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
ClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
EnptyDbClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor(isEmptyDatabase: true);
|
||||
ClientNetworkOpportunityService = Mock.Of<IClientNetworkOpportunityService>();
|
||||
Mock.Get(ClientNetworkOpportunityService)
|
||||
.Setup(s => s.GetOpportunitiesAsync(It.IsAny<Guid>(), It.IsAny<PagingOptions>(), It.IsAny<NetworkOpportunityFilters>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(expected));
|
||||
Mock.Get(ClientNetworkOpportunityService)
|
||||
.Setup(s => s.GetByIdAsync(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, int, CancellationToken>((dbGuid, networkOpportunityId, token) =>
|
||||
networkOpportunityId == opportunity.NetworkOpportunityId
|
||||
? Task.FromResult(opportunity)
|
||||
: Task.FromResult((ClientNetworkOpportunity)null));
|
||||
Mock.Get(ClientNetworkOpportunityService)
|
||||
.Setup(s => s.ConfigureAsync(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<ClientNetworkOpportunity>(), It.IsAny<NetworkOpportunitySaveModel>(), It.IsAny<CancellationToken>()));
|
||||
ClientNetworkOpportunityServiceException = Mock.Of<IClientNetworkOpportunityService>();
|
||||
Mock.Get(ClientNetworkOpportunityServiceException)
|
||||
.Setup(s => s.GetByIdAsync(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Throws(new CreateWorkbookFromTemplateException());
|
||||
Logger = Mock.Of<ILogger<ClientNetworkOpportunitiesController>>();
|
||||
}
|
||||
|
||||
[NUnitSetup]
|
||||
public void TestSetup()
|
||||
{
|
||||
Controller = new ClientNetworkOpportunitiesController(ClaimsPrincipalAccessor, ClientNetworkOpportunityService, Logger);
|
||||
EmptyDbController = new ClientNetworkOpportunitiesController(EnptyDbClaimsPrincipalAccessor, ClientNetworkOpportunityService, Logger);
|
||||
ControllerExceptions = new ClientNetworkOpportunitiesController(ClaimsPrincipalAccessor, ClientNetworkOpportunityServiceException, Logger);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ClientNetworkOpportunitiesControllerTest()
|
||||
{
|
||||
// Assert
|
||||
Controller.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetOpportunitiesTest()
|
||||
{
|
||||
// Arrange
|
||||
var loadOptions = new NetworkOpportunityLoadOptions
|
||||
{
|
||||
Filters = new NetworkOpportunityFilters
|
||||
{
|
||||
|
||||
},
|
||||
PagingOptions = new Biz.Pagination.PagingOptions
|
||||
{
|
||||
First = 0,
|
||||
Rows = 24
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await Controller.GetOpportunities(loadOptions, CancellationToken.None);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result.Value.Should().NotBeNull()
|
||||
.And.BeOfType<PagedApiResponse<NetworkOpportunitySummary>>();
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "GetByIdTest w/ Exception")]
|
||||
[TestCase(false, TestName = "GetByIdTest w/o Exception")]
|
||||
public async Task GetByIdTest(bool withException)
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = DbContext.NetworkOpportunities.FirstOrDefault();
|
||||
var clientNetworkOpportunity = DbContext.ClientNetworkOpportunities.FirstOrDefault(cno => cno.NetworkOpportunityId == opportunity.NetworkOpportunityId);
|
||||
var dto = clientNetworkOpportunity.MapToDto();
|
||||
ActionResult<ClientNetworkOpportunityDto> result;
|
||||
|
||||
// Act
|
||||
if (withException)
|
||||
{
|
||||
result = await ControllerExceptions.GetById(opportunity.NetworkOpportunityId, CancellationToken.None);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Controller.GetById(opportunity.NetworkOpportunityId, CancellationToken.None);
|
||||
}
|
||||
|
||||
// Assert
|
||||
if (withException)
|
||||
{
|
||||
result.Value.Should().BeNull();
|
||||
result.Result.Should().BeOfType<ObjectResult>();
|
||||
var objectResult = (ObjectResult)result.Result;
|
||||
objectResult.StatusCode.Should().Be(500);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Value.Should().NotBeNull()
|
||||
.And.BeOfType<ClientNetworkOpportunityDto>()
|
||||
.And.BeEquivalentTo(dto);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(true, TestName = "SaveTest w/ Exception")]
|
||||
[TestCase(false, TestName = "SaveTest w/o Exception")]
|
||||
public async Task SaveTest(bool withException)
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = DbContext.NetworkOpportunities.FirstOrDefault();
|
||||
var clientNetworkOpportunity = DbContext.ClientNetworkOpportunities.FirstOrDefault(cno => cno.NetworkOpportunityId == opportunity.NetworkOpportunityId);
|
||||
var dto = clientNetworkOpportunity.MapToDto();
|
||||
|
||||
// Act
|
||||
IActionResult result;
|
||||
if (withException)
|
||||
{
|
||||
result = await ControllerExceptions.Save(-1, dto, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Controller.Save(opportunity.NetworkOpportunityId, dto, CancellationToken.None);
|
||||
}
|
||||
|
||||
// Assert
|
||||
if (withException)
|
||||
{
|
||||
result.Should().NotBeNull()
|
||||
.And.BeOfType<BadRequestResult>();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().NotBeNull()
|
||||
.And.BeOfType<NoContentResult>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task DeleteTest()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = DbContext.NetworkOpportunities.FirstOrDefault();
|
||||
|
||||
// Act
|
||||
IActionResult result;
|
||||
result = await Controller.Delete(opportunity.NetworkOpportunityId, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull()
|
||||
.And.BeOfType<NoContentResult>();
|
||||
}
|
||||
|
||||
[TestCase(true, true, TestName = "PublishTest w/ Exception on EmptyDb")]
|
||||
[TestCase(false, true, TestName = "PublishTest w/o Exception on EmptyDb")]
|
||||
[TestCase(true, false, TestName = "PublishTest w/ Exception")]
|
||||
[TestCase(false, false, TestName = "PublishTest w/o Exception")]
|
||||
public async Task PublishTest(bool withException, bool withEmptyDb)
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = DbContext.NetworkOpportunities.FirstOrDefault();
|
||||
var clientNetworkOpportunity = DbContext.ClientNetworkOpportunities.FirstOrDefault(cno => cno.NetworkOpportunityId == opportunity.NetworkOpportunityId);
|
||||
var dto = clientNetworkOpportunity.MapToDto();
|
||||
ActionResult<bool> result;
|
||||
|
||||
// Act
|
||||
if (withEmptyDb)
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result = await EmptyDbController.Publish(-1, true, CancellationToken.None);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await EmptyDbController.Publish(opportunity.NetworkOpportunityId, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result = await Controller.Publish(-1, true, CancellationToken.None);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Controller.Publish(opportunity.NetworkOpportunityId, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
if (withEmptyDb)
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
result.Value.Should().BeFalse();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
result.Value.Should().BeFalse();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task UpdateInitiativeStatusTest()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = DbContext.NetworkOpportunities.FirstOrDefault();
|
||||
var clientNetworkOpportunity = DbContext.ClientNetworkOpportunities.FirstOrDefault(cno => cno.NetworkOpportunityId == opportunity.NetworkOpportunityId);
|
||||
var dto = clientNetworkOpportunity.MapToDto();
|
||||
|
||||
// Act
|
||||
var result = await Controller.UpdateInitiativeStatus(opportunity.NetworkOpportunityId, Biz.Shared.InitiativeStatuses.Completed, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull()
|
||||
.And.BeOfType<NoContentResult>();
|
||||
}
|
||||
|
||||
[TestCase(true, true, TestName = "RevertWorkbookToTemplateTest w/ Exception on EmptyDb")]
|
||||
[TestCase(false, true, TestName = "RevertWorkbookToTemplateTest w/o Exception on EmptyDb")]
|
||||
[TestCase(true, false, TestName = "RevertWorkbookToTemplateTest w/ Exception")]
|
||||
[TestCase(false, false, TestName = "RevertWorkbookToTemplateTest w/o Exception")]
|
||||
public async Task RevertWorkbookToTemplateTest(bool withException, bool withEmptyDb)
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = DbContext.NetworkOpportunities.FirstOrDefault();
|
||||
var clientNetworkOpportunity = DbContext.ClientNetworkOpportunities.FirstOrDefault(cno => cno.NetworkOpportunityId == opportunity.NetworkOpportunityId);
|
||||
var dto = clientNetworkOpportunity.MapToDto();
|
||||
ActionResult<bool> result;
|
||||
|
||||
// Act
|
||||
if (withEmptyDb)
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result = await EmptyDbController.RevertWorkbookToTemplate(-1, CancellationToken.None);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await EmptyDbController.RevertWorkbookToTemplate(opportunity.NetworkOpportunityId, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result = await Controller.RevertWorkbookToTemplate(-1, CancellationToken.None);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Controller.RevertWorkbookToTemplate(opportunity.NetworkOpportunityId, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
if (withEmptyDb)
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
result.Value.Should().BeFalse();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (withException)
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
result.Value.Should().BeFalse();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<bool>>();
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Network;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.CoreLib.Claims;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnitSetup = NUnit.Framework.SetUpAttribute;
|
||||
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ClientNetworkOpportunityLinkControllerTests
|
||||
{
|
||||
public ClientNetworkOpportunityLinkController Controller { get; set; }
|
||||
public ClientNetworkOpportunityLinkController EmptyDbController { get; set; }
|
||||
public IClaimsPrincipalAccessor ClaimsPrincipalAccessor { get; set; }
|
||||
public IClaimsPrincipalAccessor EmptyDbClaimsPrincipalAccessor { get; set; }
|
||||
public IClientNetworkOpportunityService clientNetworkOpportunityService;
|
||||
public IClientNetworkOpportunityLinkService clientNetworkOpportunityLinkService;
|
||||
public IAllOpportunityService allOpportunityService;
|
||||
public IInitiativeService initiativeService;
|
||||
public ILogger<ClientNetworkOpportunityLinkController> logger;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void ClientNetworkOpportunityLinkControllerTestsSetup()
|
||||
{
|
||||
// Arrange
|
||||
clientNetworkOpportunityService = Mock.Of<IClientNetworkOpportunityService>();
|
||||
clientNetworkOpportunityLinkService = Mock.Of<IClientNetworkOpportunityLinkService>();
|
||||
allOpportunityService = Mock.Of<IAllOpportunityService>();
|
||||
initiativeService = Mock.Of<IInitiativeService>();
|
||||
logger = Mock.Of<ILogger<ClientNetworkOpportunityLinkController>>();
|
||||
|
||||
// Act
|
||||
ClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
EmptyDbClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor(isEmptyDatabase: true);
|
||||
var expected = new ClientNetworkOpportunityLink
|
||||
{
|
||||
OpportunityGuid = Guid.NewGuid(),
|
||||
ConfigurationGuid = Guid.NewGuid(),
|
||||
ClientNetworkOpportunityLinkId = 1,
|
||||
InitiativeGuid = Guid.NewGuid(),
|
||||
ClientNetworkOpportunity = new ClientNetworkOpportunity
|
||||
{
|
||||
NetworkOpportunityId = 1,
|
||||
}
|
||||
};
|
||||
Mock.Get(clientNetworkOpportunityLinkService)
|
||||
.Setup(s => s.RolloverOpportunityLinkAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<Guid>(),
|
||||
It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, Guid, Guid, Guid, Guid, Guid, CancellationToken>(
|
||||
(dbGuid, SrcConfigGuid, SrcOppGuid, RollConfigGuid, RollOppGuid, RollInitGuid, token) =>
|
||||
dbGuid == Guid.Empty || SrcOppGuid == Guid.Empty
|
||||
? Task.FromResult(default(ClientNetworkOpportunityLink))
|
||||
: Task.FromResult(expected));
|
||||
Mock.Get(clientNetworkOpportunityLinkService)
|
||||
.Setup(s => s.DeleteOpportunityLinkAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<Guid>(),
|
||||
It.IsAny<CancellationToken>()));
|
||||
}
|
||||
|
||||
[NUnitSetup]
|
||||
public void TestSetup()
|
||||
{
|
||||
// Act
|
||||
Controller = new ClientNetworkOpportunityLinkController(
|
||||
ClaimsPrincipalAccessor,
|
||||
clientNetworkOpportunityService,
|
||||
clientNetworkOpportunityLinkService,
|
||||
allOpportunityService,
|
||||
initiativeService,
|
||||
logger);
|
||||
|
||||
EmptyDbController = new ClientNetworkOpportunityLinkController(
|
||||
EmptyDbClaimsPrincipalAccessor,
|
||||
clientNetworkOpportunityService,
|
||||
clientNetworkOpportunityLinkService,
|
||||
allOpportunityService,
|
||||
initiativeService,
|
||||
logger);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ClientNetworkOpportunityLinkControllerTest()
|
||||
{
|
||||
// Assert
|
||||
Controller.Should().NotBeNull();
|
||||
EmptyDbController.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(AddOpportunityLinkTestCases))]
|
||||
public async Task AddOpportunityLinkTest(Guid opportunityGuid)
|
||||
{
|
||||
var result = await Controller.AddOpportunityLink(new ClientNetworkOpportunityLinkController.CreateNetworkOpportunityLinkDto
|
||||
{
|
||||
OpportunityGuid = opportunityGuid
|
||||
}, CancellationToken.None);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<ActionResult<ClientNetworkOpportunityLinkController.ClientNetworkOpportunityResultDto>>();
|
||||
result.Result.Should().NotBeNull();
|
||||
if (opportunityGuid == Guid.Empty)
|
||||
{
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().NotBeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> AddOpportunityLinkTestCases()
|
||||
{
|
||||
yield return new TestCaseData(Guid.Empty)
|
||||
.SetName(nameof(AddOpportunityLinkTestCases) + " w/Empty Opp Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid())
|
||||
.SetName(nameof(AddOpportunityLinkTestCases) + " w/Opp Guid");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(RolloverOpportunityLinkTestCases))]
|
||||
public async Task RolloverOpportunityLinkTest(Guid srcOppGuid, bool useEmptyController)
|
||||
{
|
||||
// Arrange
|
||||
var rolloverOpportunityInfo = new ClientNetworkOpportunityLinkController.RolloverOpportunityDto
|
||||
{
|
||||
SourceOpportunityGuid = srcOppGuid,
|
||||
SourceConfigurationGuid = Guid.Empty,
|
||||
DatabaseGuid = useEmptyController ? Guid.Empty : new Guid(TestUtilities.DbGuid),
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await Controller.RolloverOpportunityLink(rolloverOpportunityInfo, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
if (useEmptyController)
|
||||
{
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> RolloverOpportunityLinkTestCases()
|
||||
{
|
||||
yield return new TestCaseData(Guid.Empty, true)
|
||||
.SetName(nameof(RolloverOpportunityLinkTest) + " w/Empty Opp Guid, Empty Db Guid");
|
||||
yield return new TestCaseData(Guid.Empty, false)
|
||||
.SetName(nameof(RolloverOpportunityLinkTest) + " w/Empty Opp Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid(), true)
|
||||
.SetName(nameof(RolloverOpportunityLinkTest) + " w/Opp Guid, Empty Db Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid(), false)
|
||||
.SetName(nameof(RolloverOpportunityLinkTest) + " w/Opp Guid");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(DeleteOpportunityLinkTestCases))]
|
||||
public async Task DeleteOpportunityLinkTest(Guid srcOppGuid, Guid configurationGuid, Guid databaseGuid, bool useEmptyController)
|
||||
{
|
||||
// Arrange
|
||||
Mock.Get(clientNetworkOpportunityLinkService)
|
||||
.Setup(s => s.ContainsOpportunityLinkAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<Guid>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, Guid, Guid, CancellationToken>(
|
||||
(dbGuid, configurationGuid, oppGuid, token) =>
|
||||
oppGuid == Guid.Empty || dbGuid == Guid.Empty
|
||||
? Task.FromResult(false)
|
||||
: Task.FromResult(true));
|
||||
var opportunity = new ClientNetworkOpportunityLinkController.ClientNetworkOpportunityLinkDto
|
||||
{
|
||||
ConfigurationGuid = configurationGuid,
|
||||
DatabaseGuid = databaseGuid,
|
||||
Id = srcOppGuid
|
||||
};
|
||||
|
||||
// Act
|
||||
ActionResult<bool> result;
|
||||
if (useEmptyController)
|
||||
{
|
||||
result = await EmptyDbController.DeleteOpportunityLink(opportunity, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Controller.DeleteOpportunityLink(opportunity, CancellationToken.None);
|
||||
}
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
if (useEmptyController)
|
||||
{
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> DeleteOpportunityLinkTestCases()
|
||||
{
|
||||
var configurationGuid = JazzEntityFrameworkStub.JazzEntityFrameworkFactory.CurrentConfigurationGuid;
|
||||
var databaseGuid = JazzEntityFrameworkStub.JazzEntityFrameworkFactory.DatabaseGuid;
|
||||
|
||||
yield return new TestCaseData(Guid.Empty, configurationGuid, Guid.Empty, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkTest) + " w/Empty Opp Guid, Empty Db Guid");
|
||||
yield return new TestCaseData(Guid.Empty, configurationGuid, null, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkTest) + " w/Empty Opp Guid, Null Db Guid");
|
||||
yield return new TestCaseData(Guid.Empty, configurationGuid, databaseGuid, false)
|
||||
.SetName(nameof(DeleteOpportunityLinkTest) + " w/Empty Opp Guid");
|
||||
|
||||
yield return new TestCaseData(Guid.NewGuid(), configurationGuid, Guid.Empty, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkTest) + " w/Opp Guid, Empty Db Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid(), configurationGuid, null, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkTest) + " w/Opp Guid, Null Db Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid(), configurationGuid, databaseGuid, false)
|
||||
.SetName(nameof(DeleteOpportunityLinkTest) + " w/Opp Guid");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(DeleteOpportunityLinkIfExistsTestCases))]
|
||||
public async Task DeleteOpportunityLinkIfExistsTest(Guid opportunityGuid, Guid configurationGuid, bool useEmptyController)
|
||||
{
|
||||
// Arrange
|
||||
Mock.Get(clientNetworkOpportunityLinkService)
|
||||
.Setup(s => s.ContainsOpportunityLinkAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<Guid>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, Guid, Guid, CancellationToken>(
|
||||
(dbGuid, configurationGuid, oppGuid, token) =>
|
||||
oppGuid == Guid.Empty || dbGuid == Guid.Empty
|
||||
? Task.FromResult(false)
|
||||
: Task.FromResult(true));
|
||||
|
||||
// Act
|
||||
ActionResult<bool> result;
|
||||
if (useEmptyController)
|
||||
{
|
||||
result = await EmptyDbController.DeleteOpportunityLinkIfExists(opportunityGuid, configurationGuid, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Controller.DeleteOpportunityLinkIfExists(opportunityGuid, configurationGuid, CancellationToken.None);
|
||||
}
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
if (useEmptyController)
|
||||
{
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> DeleteOpportunityLinkIfExistsTestCases()
|
||||
{
|
||||
var configurationGuid = JazzEntityFrameworkStub.JazzEntityFrameworkFactory.CurrentConfigurationGuid;
|
||||
var databaseGuid = JazzEntityFrameworkStub.JazzEntityFrameworkFactory.DatabaseGuid;
|
||||
|
||||
yield return new TestCaseData(Guid.Empty, configurationGuid, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkIfExistsTest) + " w/Empty Opp Guid, Empty Db Guid");
|
||||
yield return new TestCaseData(Guid.Empty, configurationGuid, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkIfExistsTest) + " w/Empty Opp Guid, Null Db Guid");
|
||||
yield return new TestCaseData(Guid.Empty, configurationGuid, false)
|
||||
.SetName(nameof(DeleteOpportunityLinkIfExistsTest) + " w/Empty Opp Guid");
|
||||
|
||||
yield return new TestCaseData(Guid.NewGuid(), configurationGuid, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkIfExistsTest) + " w/Opp Guid, Empty Db Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid(), configurationGuid, true)
|
||||
.SetName(nameof(DeleteOpportunityLinkIfExistsTest) + " w/Opp Guid, Null Db Guid");
|
||||
yield return new TestCaseData(Guid.NewGuid(), configurationGuid, false)
|
||||
.SetName(nameof(DeleteOpportunityLinkIfExistsTest) + " w/Opp Guid");
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task DeleteInitiativeLinkTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task DeleteInitiativeLinkTest1()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.ClientWarehouses;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ClientWarehousesControllerTests
|
||||
{
|
||||
private readonly ClientWarehousesController _clientWarehouseController;
|
||||
private readonly Mock<IClientWarehouseService> _mockClientWarehouseService;
|
||||
|
||||
public ClientWarehousesControllerTests()
|
||||
{
|
||||
_mockClientWarehouseService = new Mock<IClientWarehouseService>();
|
||||
_clientWarehouseController = new ClientWarehousesController(_mockClientWarehouseService.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetAll_ReturnsAllClientWarehouses()
|
||||
{
|
||||
// Arrange
|
||||
var warehouses = new List<ClientWarehouse>
|
||||
{
|
||||
new ClientWarehouse { DatabaseGuid = Guid.NewGuid() },
|
||||
new ClientWarehouse { DatabaseGuid = Guid.NewGuid() }
|
||||
};
|
||||
|
||||
_mockClientWarehouseService
|
||||
.Setup(x => x.GetAll(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(warehouses);
|
||||
|
||||
// Act
|
||||
var result = await _clientWarehouseController.GetClientWarehouses(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
okResult.Value.Should().BeEquivalentTo(warehouses);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetAll_ReturnsNoContent_IfNoneExists()
|
||||
{
|
||||
// Arrange
|
||||
var warehouses = new List<ClientWarehouse>();
|
||||
|
||||
_mockClientWarehouseService
|
||||
.Setup(x => x.GetAll(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(warehouses);
|
||||
|
||||
// Act
|
||||
var result = await _clientWarehouseController.GetClientWarehouses(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var okResult = result.Result.Should().BeOfType<NoContentResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Add_ShouldReturnNewId()
|
||||
{
|
||||
// Arrange
|
||||
var clientWarehouse = new ClientWarehouse();
|
||||
_mockClientWarehouseService
|
||||
.Setup(s => s.Add(clientWarehouse, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(123);
|
||||
|
||||
// Act
|
||||
var result = await _clientWarehouseController.AddClientWarehouse(clientWarehouse, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var okResult = result.Result.Should().BeOfType<CreatedAtActionResult>().Subject;
|
||||
okResult.Value.Should().Be(123);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Delete_ShouldReturn_NoContent()
|
||||
{
|
||||
// Arrange
|
||||
var clientId = Guid.NewGuid();
|
||||
|
||||
_mockClientWarehouseService
|
||||
.Setup(s => s.Delete(clientId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
// Act
|
||||
var result = await _clientWarehouseController.DeleteClientWarehouse(clientId, CancellationToken.None);
|
||||
// Assert
|
||||
result.Should().BeOfType<NoContentResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Api.Test.Unit.Api.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityComments;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Strata.ContinuousImprovement.Api.Controllers.CommentsController;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class CommentsControllerTests
|
||||
{
|
||||
[TestCase(true, TestName = "Get Comments Global")]
|
||||
[TestCase(false, TestName = "Get Comments Not Global")]
|
||||
public async Task TestGetComments(bool isGlobal)
|
||||
{
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
var dbContext = await jeff.CreateCentralDbContextAsync(nameof(CommentsControllerTests), CancellationToken.None);
|
||||
var comments = await dbContext.Comments.ToListAsync(CancellationToken.None);
|
||||
var clientClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var service = Mock.Of<ICommentService>();
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.GetCommentsAsync(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(comments.Where(c => c.OpportunityId == 1).AsEnumerable()));
|
||||
var controller = new CommentsController(service, clientClaimsPrincipalAccessor);
|
||||
|
||||
var options = new CommentOptions
|
||||
{
|
||||
IsGlobal = isGlobal,
|
||||
OpportunityId = 1L,
|
||||
OpportunityType = $"{(int)OpportunityTypes.ChargeCode}"
|
||||
};
|
||||
var result = await controller.GetComments(options, CancellationToken.None);
|
||||
result.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[TestCase(true, true, TestName = "Add Comment Global")]
|
||||
[TestCase(false, true, TestName = "Add Comment Not Global")]
|
||||
[TestCase(true, false, TestName = "Add No Comment Global")]
|
||||
[TestCase(false, false, TestName = "Add No Comment Not Global")]
|
||||
public async Task TestAddComments(bool isGlobal, bool hasComment)
|
||||
{
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
var dbContext = await jeff.CreateCentralDbContextAsync(nameof(CommentsControllerTests), CancellationToken.None);
|
||||
var comments = await dbContext.Comments.ToListAsync(CancellationToken.None);
|
||||
var newComment = new NewComment
|
||||
{
|
||||
IsGlobal = isGlobal,
|
||||
OpportunityId = 1L,
|
||||
OpportunityType = $"{(int)OpportunityTypes.ChargeCode}",
|
||||
CommentDate = DateTimeOffset.Now,
|
||||
Comment = hasComment ? "This is a new comment" : ""
|
||||
};
|
||||
var expected = newComment.Map();
|
||||
var clientClaimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var user = clientClaimsPrincipalAccessor.GetCurrentClaimsPrincipal();
|
||||
var name = user.GetUserFirstName();
|
||||
var service = Mock.Of<ICommentService>();
|
||||
Mock.Get(service)
|
||||
.Setup(s => s.AddCommentAsync(It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<long>(),
|
||||
It.IsAny<string>(), It.IsAny<DateTimeOffset?>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(expected));
|
||||
var controller = new CommentsController(service, clientClaimsPrincipalAccessor);
|
||||
|
||||
var result = await controller.Create(newComment, CancellationToken.None);
|
||||
if (!hasComment)
|
||||
{
|
||||
result.Should().Be(default);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.Schema.Client;
|
||||
using Strata.Schema.Client.Dtos.Info;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture(), Category("Unit"), Category("Controller")]
|
||||
public class ConfigurationsControllerTests
|
||||
{
|
||||
public JazzDbContext DbContext { get; set; }
|
||||
public IEnumerable<Biz.Configuration.Configuration> Configurations { get; set; }
|
||||
public ISchemaServiceClient SchemaService { get; set; }
|
||||
public IConfigurationService ConfigurationService { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task ConfigurationsControllerTestSetup()
|
||||
{
|
||||
var jeff = new JazzEntityFrameworkFactory();
|
||||
DbContext = await jeff.CreateDbContextAsync(nameof(ConfigurationsControllerTests), CancellationToken.None);
|
||||
Configurations = await DbContext.Configurations.ToListAsync();
|
||||
var configuration = Configurations.First();
|
||||
var entities = new HashSet<int>(configuration.ConfiguredEntityIds);
|
||||
var expected = entities.Select(e => new Member
|
||||
{
|
||||
Id = e,
|
||||
Name = $"Entity {e}"
|
||||
});
|
||||
var dimensionInfo = new ScoreDimensionInfoDto
|
||||
{
|
||||
FriendlyName = "Entity",
|
||||
DefaultHierarchyGuid = Guid.NewGuid(),
|
||||
DimensionGuid = Guid.NewGuid(),
|
||||
ParentDimensionGuid = Guid.NewGuid()
|
||||
};
|
||||
|
||||
ConfigurationService = Mock.Of<IConfigurationService>();
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetConfigurationsAsync(It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(Configurations.AsEnumerable()));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetConfigurationAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(configuration));
|
||||
SchemaService = Mock.Of<ISchemaServiceClient>();
|
||||
Mock.Get(SchemaService)
|
||||
.Setup(s => s.GetDimensionMembersByGlobalIdAsync<Member>(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(expected));
|
||||
Mock.Get(SchemaService)
|
||||
.Setup(s => s.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(dimensionInfo));
|
||||
Mock.Get(SchemaService)
|
||||
.Setup(s => s.GetDimensionByIdAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(dimensionInfo));
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void ConfigurationsControllerTest()
|
||||
{
|
||||
var controller = new ConfigurationsController(ConfigurationService, SchemaService);
|
||||
controller.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetAllConfigurationsTest()
|
||||
{
|
||||
var controller = new ConfigurationsController(ConfigurationService, SchemaService);
|
||||
controller.Should().NotBeNull();
|
||||
var result = await controller.GetAllConfigurations(CancellationToken.None);
|
||||
result.Should().BeEquivalentTo(Configurations);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetConfigurationEntitiesTest()
|
||||
{
|
||||
var configuration = Configurations.First();
|
||||
var entities = new HashSet<int>(configuration.ConfiguredEntityIds);
|
||||
var expected = entities.Select(e => new Member
|
||||
{
|
||||
Id = e,
|
||||
Name = $"Entity {e}"
|
||||
});
|
||||
var controller = new ConfigurationsController(ConfigurationService, SchemaService);
|
||||
var result = await controller.GetConfigurationEntities(configuration.ConfigurationGuid, CancellationToken.None);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetConfigurationEntityInfoTest()
|
||||
{
|
||||
var configuration = Configurations.First();
|
||||
var entities = new HashSet<int>(configuration.ConfiguredEntityIds);
|
||||
var controller = new ConfigurationsController(ConfigurationService, SchemaService);
|
||||
var result = await controller.GetConfigurationEntityInfo(configuration.ConfigurationGuid, CancellationToken.None);
|
||||
result.Should().NotBeNull()
|
||||
.And.BeOfType<EntityInfo>();
|
||||
result.EntityTitle.Should().NotBeNullOrEmpty();
|
||||
result.EntityTitle.Should().Be("Entities");
|
||||
result.EntityText.Should().Be("Entity");
|
||||
result.DepartmentRollupGlobalId.Should().Be("Entity");
|
||||
result.HierarchicalGuid.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetConfigurationServiceLineInfoTest()
|
||||
{
|
||||
var configuration = Configurations.First();
|
||||
var controller = new ConfigurationsController(ConfigurationService, SchemaService);
|
||||
var result = await controller.GetConfigurationServiceLineInfo(configuration.ConfigurationGuid, CancellationToken.None);
|
||||
result.Should().NotBeNull()
|
||||
.And.BeOfType<ServiceLineInfo>();
|
||||
result.ServiceLineName.Should().NotBeNullOrEmpty();
|
||||
result.ServiceLineName.Should().Be("Entity");
|
||||
result.ServiceLineTitle.Should().Be("Entities");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.Databases;
|
||||
using Strata.ContinuousImprovement.Biz.Replication;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class DatabaseControllerTests
|
||||
{
|
||||
private readonly DatabasesController _databaseController;
|
||||
private readonly Mock<IDatabaseService> _mockDatabaseService;
|
||||
|
||||
public DatabaseControllerTests()
|
||||
{
|
||||
_mockDatabaseService = new Mock<IDatabaseService>();
|
||||
_databaseController = new DatabasesController(_mockDatabaseService.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Get_ReturnsAllDatabases()
|
||||
{
|
||||
// Arrange
|
||||
var databases = new List<Database>
|
||||
{
|
||||
new Database { DatabaseGuid = Guid.NewGuid() },
|
||||
new Database { DatabaseGuid = Guid.NewGuid() }
|
||||
};
|
||||
|
||||
_mockDatabaseService
|
||||
.Setup(x => x.GetDatabasesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(databases);
|
||||
|
||||
// Act
|
||||
var result = await _databaseController.Get(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
okResult.Value.Should().BeEquivalentTo(databases);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Get_ReturnsNoContent_IfNoneExists()
|
||||
{
|
||||
// Arrange
|
||||
var databases = new List<Database>();
|
||||
|
||||
_mockDatabaseService
|
||||
.Setup(x => x.GetDatabasesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(databases);
|
||||
|
||||
// Act
|
||||
var result = await _databaseController.Get(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var okResult = result.Result.Should().BeOfType<NoContentResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.FeatureFlags.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ExplorationControllerTests
|
||||
{
|
||||
public JazzEntityFrameworkFactory JazzEntityFrameworkFactory { get; set; }
|
||||
public JazzDbContext JazzDbContext { get; set; }
|
||||
public ExplorationController ExplorationController { get; set; }
|
||||
public IConfigurationService ConfigurationService { get; set; }
|
||||
public IExplorationService ExplorationService { get; set; }
|
||||
public IFeatureFlagServiceClient FeatureFlagServiceClient { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task ClientNetworkOpportunityLinkControllerTestsSetup()
|
||||
{
|
||||
// Arrange
|
||||
JazzEntityFrameworkFactory = new JazzEntityFrameworkFactory();
|
||||
JazzDbContext = await JazzEntityFrameworkFactory.CreateDbContextAsync(nameof(ConfigurationsControllerTests), CancellationToken.None);
|
||||
ConfigurationService = Mock.Of<IConfigurationService>();
|
||||
ExplorationService = Mock.Of<IExplorationService>();
|
||||
FeatureFlagServiceClient = Mock.Of<IFeatureFlagServiceClient>();
|
||||
|
||||
// Act
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
// Arrange
|
||||
ExplorationController = new ExplorationController(ExplorationService, ConfigurationService, FeatureFlagServiceClient, Mock.Of<ILogger<ExplorationController>>());
|
||||
|
||||
// Act
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExplorationControllerTest()
|
||||
{
|
||||
// Assert
|
||||
ExplorationController.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetCaseTypeFamilySummariesTestCases))]
|
||||
public async Task GetCaseTypeFamilySummariesTest(Guid configurationGuid)
|
||||
{
|
||||
// Arrange
|
||||
var configuration = JazzDbContext.Configurations.First();
|
||||
var explorationInfo = new ExplorationInfo
|
||||
{
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid,
|
||||
ExclusionCriteria = new Biz.Exploration.Filters.ExclusionCriteria(),
|
||||
Filters = new Biz.Exploration.Filters.Filter
|
||||
{
|
||||
DischargeDateStart = new System.DateTime(2024, 1, 1),
|
||||
DischargeDateEnd = new System.DateTime(2024, 12, 31),
|
||||
FilterChipItems = new List<FilterChipItem>()
|
||||
}
|
||||
};
|
||||
var result = new List<CaseTypeFamilySummaryData>();
|
||||
if (configuration.ConfigurationGuid == configurationGuid)
|
||||
{
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(JazzDbContext.Configurations.First()));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.Contains(It.IsAny<Biz.Configuration.Configuration>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Biz.Configuration.Configuration, CancellationToken>(
|
||||
(configuration, token) => Task.FromResult(true));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.IsConfigurationValid(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(default(Strata.ContinuousImprovement.Biz.Configuration.Configuration)));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.Contains(It.IsAny<Biz.Configuration.Configuration>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Biz.Configuration.Configuration, CancellationToken>(
|
||||
(configuration, token) => Task.FromResult(false));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.IsConfigurationValid(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(false));
|
||||
}
|
||||
|
||||
Mock.Get(ExplorationService)
|
||||
.Setup(s => s.GetCaseTypeFamilySummariesAsync(It.IsAny<ExplorationInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<ExplorationInfo, CancellationToken>(
|
||||
(explorationInfo, token) => Task.FromResult(result.AsEnumerable()));
|
||||
|
||||
// Act
|
||||
var controllerResponse = await ExplorationController.GetCaseTypeFamilySummaries(explorationInfo, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
controllerResponse.Should().NotBeNull();
|
||||
if (controllerResponse.Result is OkObjectResult okResult)
|
||||
{
|
||||
okResult.Should().NotBeNull();
|
||||
var value = okResult.Value as IEnumerable<CaseTypeFamilySummaryData>;
|
||||
value.Should().NotBeNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
controllerResponse.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetCaseTypeFamilyDetailsTestCases))]
|
||||
public async Task GetCaseTypeFamilyDetailsTest(Guid configurationGuid)
|
||||
{
|
||||
// Arrange
|
||||
var configuration = JazzDbContext.Configurations.First();
|
||||
var explorationInfo = new ExplorationDetailInfo
|
||||
{
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid,
|
||||
CaseTypeFamilyId = 0,
|
||||
ExclusionCriteria = new Biz.Exploration.Filters.ExclusionCriteria(),
|
||||
Filters = new Biz.Exploration.Filters.Filter
|
||||
{
|
||||
DischargeDateStart = new System.DateTime(2024, 1, 1),
|
||||
DischargeDateEnd = new System.DateTime(2024, 12, 31),
|
||||
FilterChipItems = new List<FilterChipItem>()
|
||||
}
|
||||
};
|
||||
var result = new CaseTypeFamilyDetailInfo
|
||||
{
|
||||
Metrics = new CaseTypeFamilyMetric(),
|
||||
CaseTypeFamilyDetails = Enumerable.Empty<CaseTypeFamilyDetailData>()
|
||||
};
|
||||
if (configuration.ConfigurationGuid == configurationGuid)
|
||||
{
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(JazzDbContext.Configurations.First()));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.Contains(It.IsAny<Biz.Configuration.Configuration>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Biz.Configuration.Configuration, CancellationToken>(
|
||||
(configuration, token) => Task.FromResult(true));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.IsConfigurationValid(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.GetById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(default(Strata.ContinuousImprovement.Biz.Configuration.Configuration)));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.Contains(It.IsAny<Biz.Configuration.Configuration>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Biz.Configuration.Configuration, CancellationToken>(
|
||||
(configuration, token) => Task.FromResult(false));
|
||||
Mock.Get(ConfigurationService)
|
||||
.Setup(s => s.IsConfigurationValid(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(configurationGuid, token) => Task.FromResult(false));
|
||||
}
|
||||
Mock.Get(ExplorationService)
|
||||
.Setup(s => s.GetCaseTypeFamilyDetailsAsync(It.IsAny<ExplorationDetailInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<ExplorationDetailInfo, CancellationToken>(
|
||||
(exploration, token) => Task.FromResult(result));
|
||||
|
||||
// Act
|
||||
var controllerResponse = await ExplorationController.GetCaseTypeFamilyDetails(explorationInfo, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
controllerResponse.Should().NotBeNull();
|
||||
if (controllerResponse.Result is OkObjectResult okResult)
|
||||
{
|
||||
okResult.Should().NotBeNull();
|
||||
okResult.Value.Should().BeOfType<CaseTypeFamilyDetailInfo>();
|
||||
configuration.ConfigurationGuid.Should().Be(configurationGuid);
|
||||
}
|
||||
else if (controllerResponse.Result is BadRequestResult badRequestResult)
|
||||
{
|
||||
badRequestResult.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> GetCaseTypeFamilySummariesTestCases()
|
||||
{
|
||||
var configurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid;
|
||||
|
||||
yield return new TestCaseData(configurationGuid)
|
||||
.SetName("summaries valid configuration");
|
||||
yield return new TestCaseData(Guid.NewGuid())
|
||||
.SetName("summaries invalid configuration");
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> GetCaseTypeFamilyDetailsTestCases()
|
||||
{
|
||||
var configurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid;
|
||||
|
||||
yield return new TestCaseData(configurationGuid)
|
||||
.SetName("details valid configuration");
|
||||
yield return new TestCaseData(Guid.NewGuid())
|
||||
.SetName("details invalid configuration");
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration;
|
||||
using Strata.ContinuousImprovement.Biz.Population;
|
||||
using Strata.ContinuousImprovement.Biz.UserSetting;
|
||||
using Strata.DecisionSupport.Models.Encounters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class ExplorationPopulationsControllerTests
|
||||
{
|
||||
public ExplorationPopulationsController Controller { get; set; }
|
||||
public IUserSettingService UserSettingService { get; set; }
|
||||
public IExplorationPopulationService PopulationService { get; set; }
|
||||
public IExplorationService ExplorationService { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task ExplorationPopulationsControllerTests_OneTimeSetUp()
|
||||
{
|
||||
UserSettingService = Mock.Of<IUserSettingService>();
|
||||
PopulationService = Mock.Of<IExplorationPopulationService>();
|
||||
ExplorationService = Mock.Of<IExplorationService>();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ExplorationPopulationsControllerTests_Setup()
|
||||
{
|
||||
Controller = new ExplorationPopulationsController(UserSettingService, PopulationService, ExplorationService, Mock.Of<ILogger<ExplorationPopulationsController>>());
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetEncountersTestCases))]
|
||||
public async Task GetEncountersTest(ExplorationPopulationDto population, IEnumerable<EncounterPhysicianInfo> encounterPhysicians)
|
||||
{
|
||||
// Arrange
|
||||
long populationId = 0;
|
||||
Guid configurationGuid = new Guid();
|
||||
|
||||
Mock.Get(PopulationService)
|
||||
.Setup(s => s.GetPopulationDataAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<long, CancellationToken>((populationId, token) => Task.FromResult(population));
|
||||
Mock.Get(ExplorationService)
|
||||
.Setup(s => s.GetEncountersAsync(It.IsAny<ExplorationDetailInfo>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<ExplorationDetailInfo, Guid, CancellationToken>((exploration, configurationGuid, token) => Task.FromResult(encounterPhysicians));
|
||||
|
||||
// Act
|
||||
var response = await Controller.GetEncounters(populationId, configurationGuid, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
response.Should().NotBeNull();
|
||||
if (population == null || encounterPhysicians == null)
|
||||
{
|
||||
response.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)response.Result;
|
||||
okResult.Value.Should().BeAssignableTo<IEnumerable<IJazzExplorationEncounterInfo>>();
|
||||
okResult.Value.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> GetEncountersTestCases()
|
||||
{
|
||||
var population = new ExplorationPopulationDto()
|
||||
{
|
||||
FiltersJSON = string.Empty
|
||||
};
|
||||
var encounterPhysicians = Enumerable.Empty<EncounterPhysicianInfo>();
|
||||
|
||||
yield return new TestCaseData(population, encounterPhysicians)
|
||||
.SetName("Population and Encounters are not null");
|
||||
yield return new TestCaseData(population, null)
|
||||
.SetName("Encounters is null");
|
||||
yield return new TestCaseData(null, encounterPhysicians)
|
||||
.SetName("Population is null");
|
||||
yield return new TestCaseData(null, null)
|
||||
.SetName("Population and Encounters are null");
|
||||
}
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ApiLib.Standard.Models;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.FreeForm.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.FreeForm.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.FreeForm.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Pagination;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class FreeFormOpportunitiesControllerTests
|
||||
{
|
||||
public JazzEntityFrameworkFactory jazzEntityFrameworkFactory { get; set; }
|
||||
public JazzDbContext DbContext { get; set; }
|
||||
public IFFOpportunityService FFOpportunityService { get; set; }
|
||||
public IFFInitiativeService FFInitiativeService { get; set; }
|
||||
public IInitiativeService InitiativeService { get; set; }
|
||||
public FreeFormOpportunitiesController Controller { get; set; }
|
||||
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task FreeFormOpportunitiesControllerTestSetup()
|
||||
{
|
||||
jazzEntityFrameworkFactory = new JazzEntityFrameworkFactory();
|
||||
DbContext = await jazzEntityFrameworkFactory.CreateDbContextAsync(nameof(ConfigurationsControllerTests), CancellationToken.None);
|
||||
FFOpportunityService = Mock.Of<IFFOpportunityService>();
|
||||
InitiativeService = Mock.Of<IInitiativeService>();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
// Arrange
|
||||
Controller = new FreeFormOpportunitiesController(FFOpportunityService, InitiativeService);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task FreeFormOpportunitiesControllerTest()
|
||||
{
|
||||
// Act
|
||||
|
||||
// Assert
|
||||
Controller.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetOpportunitiesTest()
|
||||
{
|
||||
// Arrange
|
||||
var opportunities = DbContext.FFOpportunities.Take(25).ToList();
|
||||
var pagingOptions = new PagingOptions
|
||||
{
|
||||
First = 1,
|
||||
Rows = 25,
|
||||
SortField = nameof(FFOpportunity.IdentifiedSavings),
|
||||
SortOrder = SortOrder.Asc
|
||||
};
|
||||
var loadOptions = new FFOpportunityLoadOptions
|
||||
{
|
||||
Filters = new FFFilters { ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid },
|
||||
PagingOptions = pagingOptions,
|
||||
};
|
||||
var response = new PagedApiResponse<FFOpportunity>
|
||||
{
|
||||
PagingInfo = new PagingInfo
|
||||
{
|
||||
PageNumber = 1,
|
||||
TotalNumberOfResults = opportunities.Count,
|
||||
TotalPages = opportunities.Count / pagingOptions.Rows,
|
||||
ResultsPerPage = pagingOptions.Rows
|
||||
},
|
||||
Data = opportunities
|
||||
};
|
||||
Mock.Get(FFOpportunityService)
|
||||
.Setup(x => x.GetPagedAsync(It.IsAny<PagingOptions>(), It.IsAny<FFFilters>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<PagingOptions, FFFilters, CancellationToken>(
|
||||
(pagingOptions, filters, token) =>
|
||||
Task.FromResult(response)
|
||||
);
|
||||
|
||||
// Act
|
||||
var controllerResponse = await Controller.GetOpportunities(loadOptions, CancellationToken.None);
|
||||
|
||||
|
||||
// Assert
|
||||
controllerResponse.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetByIdTest()
|
||||
{
|
||||
// Arrange
|
||||
var opportunities = DbContext.FreeFormOpportunities.ToList();
|
||||
var firstOpportunity = opportunities.First();
|
||||
if (firstOpportunity.Configuration == null)
|
||||
{
|
||||
var configuration = DbContext.Configurations.First();
|
||||
if (configuration.FiscalMonthResolver == null)
|
||||
{
|
||||
var fmr = jazzEntityFrameworkFactory.FiscalMonthResolver;
|
||||
configuration.FiscalMonthResolver = fmr;
|
||||
}
|
||||
firstOpportunity.Configuration = configuration;
|
||||
}
|
||||
var dfltOpportunity = default(FFOpportunityDataModel);
|
||||
var fmResolver = jazzEntityFrameworkFactory.FiscalMonthResolver;
|
||||
Mock.Get(FFOpportunityService)
|
||||
.Setup(x => x.GetDataModelById(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, CancellationToken>(
|
||||
(opportunityGuid, token) =>
|
||||
opportunityGuid != firstOpportunity.OpportunityGuid
|
||||
? Task.FromResult(dfltOpportunity)
|
||||
: Task.FromResult(firstOpportunity)
|
||||
);
|
||||
var detailModel = firstOpportunity.MapToDetailModel(fmResolver);
|
||||
|
||||
// Act
|
||||
var controllerResponse = await Controller.GetById(firstOpportunity.OpportunityGuid, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
controllerResponse.Should().NotBeNull();
|
||||
controllerResponse.Result.Should().NotBeNull();
|
||||
controllerResponse.Result.Should().BeOfType<OkObjectResult>();
|
||||
var result = (OkObjectResult)controllerResponse.Result;
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeOfType<OkObjectResult>();
|
||||
result.Value.Should().NotBeNull();
|
||||
result.Value.Should().BeOfType<FFOpportunityDetailModel>();
|
||||
var detail = (FFOpportunityDetailModel)result.Value;
|
||||
detail.Should().BeEquivalentTo(detailModel);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task CreateTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task DeleteTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetInitiativeTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task SetGoalsAsyncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ExportTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetFilterOptionsAsyncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers.JazzQvi;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.QVI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class NonQviSavingsControllerTests
|
||||
{
|
||||
private Mock<INonQviSavingsService> _serviceMock;
|
||||
private NonQviSavingsController _controller;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_serviceMock = new Mock<INonQviSavingsService>();
|
||||
_controller = new NonQviSavingsController(_serviceMock.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilies_WhenConfigurationGuidIsEmpty_ReturnsBadRequest()
|
||||
{
|
||||
var result = await _controller.GetCaseTypeFamilies(Guid.Empty, false, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestObjectResult>();
|
||||
_serviceMock.Verify(s => s.GetCaseTypeFamilyInfoAsync(It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilies_WhenConfigurationGuidIsValid_ReturnsOk()
|
||||
{
|
||||
var configurationGuid = Guid.NewGuid();
|
||||
var expected = new[] { new NonQviCaseTypeFamilyInfo { CaseTypeFamilyId = 1, CaseCount = 5 } };
|
||||
_serviceMock
|
||||
.Setup(s => s.GetCaseTypeFamilyInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(expected);
|
||||
|
||||
var result = await _controller.GetCaseTypeFamilies(configurationGuid, true, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = result.Result as OkObjectResult;
|
||||
var value = okResult?.Value as System.Collections.Generic.IEnumerable<NonQviCaseTypeFamilyInfo>;
|
||||
value.Should().NotBeNull();
|
||||
value!.Single().CaseTypeFamilyId.Should().Be(1);
|
||||
_serviceMock.Verify(s => s.GetCaseTypeFamilyInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetDrgTypes_WhenConfigurationGuidIsEmpty_ReturnsBadRequest()
|
||||
{
|
||||
var result = await _controller.GetDrgTypes(Guid.Empty, false, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestObjectResult>();
|
||||
_serviceMock.Verify(s => s.GetDrgTypeInfoAsync(It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetDrgTypes_WhenConfigurationGuidIsValid_ReturnsOk()
|
||||
{
|
||||
var configurationGuid = Guid.NewGuid();
|
||||
var expected = new[] { new NonQviDrgTypeInfo { DrgType = "MS" } };
|
||||
_serviceMock
|
||||
.Setup(s => s.GetDrgTypeInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(expected);
|
||||
|
||||
var result = await _controller.GetDrgTypes(configurationGuid, true, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = result.Result as OkObjectResult;
|
||||
var value = okResult?.Value as System.Collections.Generic.IEnumerable<NonQviDrgTypeInfo>;
|
||||
value.Should().NotBeNull();
|
||||
value!.Single().DrgType.Should().Be("MS");
|
||||
_serviceMock.Verify(s => s.GetDrgTypeInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers.JazzQvi;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.QVI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class QviEncountersControllerTests
|
||||
{
|
||||
private Mock<IQviEncounterService> _serviceMock;
|
||||
private QviEncountersController _controller;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_serviceMock = new Mock<IQviEncounterService>();
|
||||
_controller = new QviEncountersController(_serviceMock.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Get_WhenConfigurationGuidIsEmpty_ReturnsBadRequest()
|
||||
{
|
||||
var result = await _controller.Get(Guid.Empty, DateTime.UtcNow, DateTime.UtcNow, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestObjectResult>();
|
||||
_serviceMock.Verify(s => s.GetEncounterInfoAsync(It.IsAny<Guid>(), It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Get_WhenConfigurationGuidIsValid_ReturnsOk()
|
||||
{
|
||||
var configurationGuid = Guid.NewGuid();
|
||||
var startDate = new DateTime(2024, 1, 1);
|
||||
var endDate = new DateTime(2024, 12, 31);
|
||||
var expected = new[] { new EncounterInfo { EncounterId = 11, EncounterRecordNumber = "enc-11" } };
|
||||
_serviceMock
|
||||
.Setup(s => s.GetEncounterInfoAsync(configurationGuid, startDate, endDate, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(expected);
|
||||
|
||||
var result = await _controller.Get(configurationGuid, startDate, endDate, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = result.Result as OkObjectResult;
|
||||
var value = okResult?.Value as System.Collections.Generic.IEnumerable<EncounterInfo>;
|
||||
value.Should().NotBeNull();
|
||||
value!.Single().EncounterId.Should().Be(11);
|
||||
_serviceMock.Verify(s => s.GetEncounterInfoAsync(configurationGuid, startDate, endDate, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers.JazzQvi;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.QVI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class QviLineItemSavingsControllerTests
|
||||
{
|
||||
private Mock<IQviLineItemSavingsService> _serviceMock;
|
||||
private QviLineItemSavingsController _controller;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_serviceMock = new Mock<IQviLineItemSavingsService>();
|
||||
_controller = new QviLineItemSavingsController(_serviceMock.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilies_WhenConfigurationGuidIsEmpty_ReturnsBadRequest()
|
||||
{
|
||||
var result = await _controller.GetCaseTypeFamilies(Guid.Empty, false, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestObjectResult>();
|
||||
_serviceMock.Verify(s => s.GetCaseTypeFamilyInfoAsync(It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilies_WhenConfigurationGuidIsValid_ReturnsOk()
|
||||
{
|
||||
var configurationGuid = Guid.NewGuid();
|
||||
var expected = new[] { new QviLineItemCaseTypeFamilyInfo { QualityVariationEventId = 1, CaseTypeFamilyId = 2 } };
|
||||
_serviceMock
|
||||
.Setup(s => s.GetCaseTypeFamilyInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(expected);
|
||||
|
||||
var result = await _controller.GetCaseTypeFamilies(configurationGuid, true, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = result.Result as OkObjectResult;
|
||||
var value = okResult?.Value as System.Collections.Generic.IEnumerable<QviLineItemCaseTypeFamilyInfo>;
|
||||
value.Should().NotBeNull();
|
||||
value!.Single().CaseTypeFamilyId.Should().Be(2);
|
||||
_serviceMock.Verify(s => s.GetCaseTypeFamilyInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetDrgTypes_WhenConfigurationGuidIsEmpty_ReturnsBadRequest()
|
||||
{
|
||||
var result = await _controller.GetDrgTypes(Guid.Empty, false, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestObjectResult>();
|
||||
_serviceMock.Verify(s => s.GetDrgTypeInfoAsync(It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetDrgTypes_WhenConfigurationGuidIsValid_ReturnsOk()
|
||||
{
|
||||
var configurationGuid = Guid.NewGuid();
|
||||
var expected = new[] { new QviLineItemDrgTypeInfo { DrgType = "APR" } };
|
||||
_serviceMock
|
||||
.Setup(s => s.GetDrgTypeInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(expected);
|
||||
|
||||
var result = await _controller.GetDrgTypes(configurationGuid, true, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = result.Result as OkObjectResult;
|
||||
var value = okResult?.Value as System.Collections.Generic.IEnumerable<QviLineItemDrgTypeInfo>;
|
||||
value.Should().NotBeNull();
|
||||
value!.Single().DrgType.Should().Be("APR");
|
||||
_serviceMock.Verify(s => s.GetDrgTypeInfoAsync(configurationGuid, true, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetEncounterSavings_WhenConfigurationGuidIsEmpty_ReturnsBadRequest()
|
||||
{
|
||||
var result = await _controller.GetEncounterSavings(Guid.Empty, cancellationToken: CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestObjectResult>();
|
||||
_serviceMock.Verify(s => s.GetEncounterSavingInfoAsync(It.IsAny<Guid>(), It.IsAny<Guid?>(), It.IsAny<bool?>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetEncounterSavings_WhenConfigurationGuidIsValid_ReturnsOk()
|
||||
{
|
||||
var configurationGuid = Guid.NewGuid();
|
||||
var expected = new[] { new QviLineItemEncounterSavingInfo { EncounterRecordNumber = "enc-1" } };
|
||||
_serviceMock
|
||||
.Setup(s => s.GetEncounterSavingInfoAsync(configurationGuid, It.IsAny<Guid?>(), It.IsAny<bool?>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(expected);
|
||||
|
||||
var result = await _controller.GetEncounterSavings(configurationGuid, cancellationToken: CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
var okResult = result.Result as OkObjectResult;
|
||||
var value = okResult?.Value as System.Collections.Generic.IEnumerable<QviLineItemEncounterSavingInfo>;
|
||||
value.Should().NotBeNull();
|
||||
value!.Single().EncounterRecordNumber.Should().Be("enc-1");
|
||||
_serviceMock.Verify(s => s.GetEncounterSavingInfoAsync(configurationGuid, null, null, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class StrategicChargeCodeControllerTests
|
||||
{
|
||||
private StrategicChargeCodeController _controller;
|
||||
private Mock<IStrategicOpportunityService> _mockOpportunityService;
|
||||
private Mock<IStrategicChargeCodeOpportunityDetailService> _mockDetailService;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_mockOpportunityService = new Mock<IStrategicOpportunityService>();
|
||||
_mockDetailService = new Mock<IStrategicChargeCodeOpportunityDetailService>();
|
||||
_controller = new StrategicChargeCodeController(_mockOpportunityService.Object, _mockDetailService.Object);
|
||||
}
|
||||
|
||||
// --- GetOpportunityChargeCode ---
|
||||
|
||||
[Test]
|
||||
public async Task GetOpportunityChargeCode_ReturnsOk_WhenFound()
|
||||
{
|
||||
const long opportunityId = 1;
|
||||
var data = new StrategicOpportunityChargeCodeEditData();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityChargeCode(opportunityId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(data);
|
||||
|
||||
var result = await _controller.GetOpportunityChargeCode(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetOpportunityChargeCode_ReturnsNotFound_WhenMissing()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityChargeCode(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityChargeCodeEditData)null);
|
||||
|
||||
var result = await _controller.GetOpportunityChargeCode(99, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- CreateOpportunityChargeCode ---
|
||||
|
||||
[Test]
|
||||
public async Task CreateOpportunityChargeCode_ReturnsOk_WithNewId_WhenSuccessful()
|
||||
{
|
||||
const long newId = 42;
|
||||
var dto = new StrategicOpportunityChargeCodeDto();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CreateOpportunityChargeCodeAsync(dto, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(newId);
|
||||
|
||||
var result = await _controller.CreateOpportunityChargeCode(dto, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(newId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateOpportunityChargeCode_ReturnsBadRequest_WhenServiceReturnsZero()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CreateOpportunityChargeCodeAsync(It.IsAny<StrategicOpportunityChargeCodeDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(0L);
|
||||
|
||||
var result = await _controller.CreateOpportunityChargeCode(new StrategicOpportunityChargeCodeDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
|
||||
// --- UpdateOpportunityChargeCode ---
|
||||
|
||||
[Test]
|
||||
public async Task UpdateOpportunityChargeCode_ReturnsOk_WhenSuccessful()
|
||||
{
|
||||
const long opportunityId = 5;
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateOpportunityChargeCode(opportunityId, It.IsAny<StrategicOpportunityChargeCodeUpdateDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(opportunityId);
|
||||
|
||||
var result = await _controller.UpdateOpportunityChargeCode(opportunityId, new StrategicOpportunityChargeCodeUpdateDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateOpportunityChargeCode_ReturnsNotFound_WhenServiceReturnsZero()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateOpportunityChargeCode(It.IsAny<long>(), It.IsAny<StrategicOpportunityChargeCodeUpdateDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(0L);
|
||||
|
||||
var result = await _controller.UpdateOpportunityChargeCode(99, new StrategicOpportunityChargeCodeUpdateDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- DeleteOpportunityChargeCode ---
|
||||
|
||||
[Test]
|
||||
public async Task DeleteOpportunityChargeCode_ReturnsNotFound_WhenOpportunityDoesNotExist()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.ContainsAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.DeleteOpportunityChargeCode(99, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NotFoundResult>();
|
||||
_mockOpportunityService.Verify(s => s.DeleteChargeCodeOpportunityAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteOpportunityChargeCode_ReturnsNoContent_WhenDeleted()
|
||||
{
|
||||
const long opportunityId = 7;
|
||||
_mockOpportunityService.Setup(s => s.ContainsAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
_mockOpportunityService.Setup(s => s.DeleteChargeCodeOpportunityAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.DeleteOpportunityChargeCode(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NoContentResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteOpportunityChargeCode_ReturnsBadRequest_WhenDeleteFails()
|
||||
{
|
||||
const long opportunityId = 7;
|
||||
_mockOpportunityService.Setup(s => s.ContainsAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
_mockOpportunityService.Setup(s => s.DeleteChargeCodeOpportunityAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.DeleteOpportunityChargeCode(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
|
||||
// --- GetGoalDetail ---
|
||||
|
||||
[Test]
|
||||
public async Task GetGoalDetail_ReturnsOk_WhenFound()
|
||||
{
|
||||
const long opportunityId = 3;
|
||||
var getData = new StrategicOpportunityGetDetailData();
|
||||
var data = new StrategicOpportunityDetailData();
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, false, getData, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(data);
|
||||
|
||||
var result = await _controller.GetGoalDetail(opportunityId, getData, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGoalDetail_ReturnsNotFound_WhenMissing()
|
||||
{
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityDetailData(It.IsAny<long>(), false, It.IsAny<StrategicOpportunityGetDetailData>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityDetailData)null);
|
||||
|
||||
var result = await _controller.GetGoalDetail(99, new StrategicOpportunityGetDetailData(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGoalDetail_PassesIsTrackingFalse_ToService()
|
||||
{
|
||||
const long opportunityId = 3;
|
||||
var getData = new StrategicOpportunityGetDetailData();
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, false, getData, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new StrategicOpportunityDetailData());
|
||||
|
||||
await _controller.GetGoalDetail(opportunityId, getData, CancellationToken.None);
|
||||
|
||||
_mockDetailService.Verify(s => s.GetStrategicOpportunityDetailData(opportunityId, false, getData, CancellationToken.None), Times.Once);
|
||||
}
|
||||
|
||||
// --- GetTrackingDetail ---
|
||||
|
||||
[Test]
|
||||
public async Task GetTrackingDetail_ReturnsOk_WhenFound()
|
||||
{
|
||||
const long opportunityId = 4;
|
||||
var getData = new StrategicOpportunityGetDetailData();
|
||||
var data = new StrategicOpportunityDetailData();
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, true, getData, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(data);
|
||||
|
||||
var result = await _controller.GetTrackingDetail(opportunityId, getData, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetTrackingDetail_ReturnsNotFound_WhenMissing()
|
||||
{
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityDetailData(It.IsAny<long>(), true, It.IsAny<StrategicOpportunityGetDetailData>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityDetailData)null);
|
||||
|
||||
var result = await _controller.GetTrackingDetail(99, new StrategicOpportunityGetDetailData(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetTrackingDetail_PassesIsTrackingTrue_ToService()
|
||||
{
|
||||
const long opportunityId = 4;
|
||||
var getData = new StrategicOpportunityGetDetailData();
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, true, getData, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new StrategicOpportunityDetailData());
|
||||
|
||||
await _controller.GetTrackingDetail(opportunityId, getData, CancellationToken.None);
|
||||
|
||||
_mockDetailService.Verify(s => s.GetStrategicOpportunityDetailData(opportunityId, true, getData, CancellationToken.None), Times.Once);
|
||||
}
|
||||
|
||||
// --- UpdateDetails ---
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDetails_ReturnsBadRequest_WhenDtoIsNull()
|
||||
{
|
||||
var result = await _controller.UpdateDetails(1, null, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
||||
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityChargeCodeAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDetails_ReturnsBadRequest_WhenAdjustmentsIsEmpty()
|
||||
{
|
||||
var dto = new StrategicChargeCodeController.UpdateDetailsDto
|
||||
{
|
||||
Adjustments = new List<StrategicOpportunityChargeCodeAdjustment>(),
|
||||
Rows = new List<string> { "system", "entity" }
|
||||
};
|
||||
|
||||
var result = await _controller.UpdateDetails(1, dto, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
||||
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityChargeCodeAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDetails_ReturnsBadRequest_WhenRowsIsEmpty()
|
||||
{
|
||||
var dto = new StrategicChargeCodeController.UpdateDetailsDto
|
||||
{
|
||||
Adjustments = new List<StrategicOpportunityChargeCodeAdjustment> { new() { NodeKey = "101", CostsValue = 5000m } },
|
||||
Rows = new List<string>()
|
||||
};
|
||||
|
||||
var result = await _controller.UpdateDetails(1, dto, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
||||
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityChargeCodeAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDetails_ReturnsOk_AndCallsService_WhenDtoIsValid()
|
||||
{
|
||||
const long opportunityId = 42;
|
||||
var dto = new StrategicChargeCodeController.UpdateDetailsDto
|
||||
{
|
||||
Adjustments = new List<StrategicOpportunityChargeCodeAdjustment> { new() { NodeKey = "101", CostsValue = 5000m } },
|
||||
Rows = new List<string> { "system", "entity", "chargeCode" }
|
||||
};
|
||||
|
||||
var result = await _controller.UpdateDetails(opportunityId, dto, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<OkResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
||||
opportunityId, dto.Adjustments, dto.Rows, CancellationToken.None), Times.Once);
|
||||
}
|
||||
|
||||
// --- Route attribute assertions ---
|
||||
|
||||
[Test]
|
||||
public void Controller_HasExpectedRouteTemplate()
|
||||
{
|
||||
var routeAttr = typeof(StrategicChargeCodeController).GetCustomAttribute<RouteAttribute>();
|
||||
|
||||
routeAttr.Should().NotBeNull();
|
||||
routeAttr.Template.Should().Be("api/v{api-version:apiVersion}/[controller]");
|
||||
}
|
||||
|
||||
[TestCase(nameof(StrategicChargeCodeController.GetOpportunityChargeCode), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.CreateOpportunityChargeCode), typeof(HttpPostAttribute), null)]
|
||||
[TestCase(nameof(StrategicChargeCodeController.UpdateOpportunityChargeCode), typeof(HttpPutAttribute), "{opportunityId}")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.DeleteOpportunityChargeCode), typeof(HttpDeleteAttribute), "{opportunityId}")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.GetGoalDetail), typeof(HttpPostAttribute), "{opportunityId}/goal-detail")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.ExportInitiative), typeof(HttpPostAttribute), "{opportunityId}/export")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.ExportOpportunity), typeof(HttpPostAttribute), "{opportunityId}/export-opportunity")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.GetTrackingDetail), typeof(HttpPostAttribute), "{opportunityId}/tracking-detail")]
|
||||
[TestCase(nameof(StrategicChargeCodeController.UpdateDetails), typeof(HttpPutAttribute), "{opportunityId}/details")]
|
||||
public void ActionRoute_HasExpectedHttpMethodAndTemplate(string methodName, Type httpAttrType, string expectedTemplate)
|
||||
{
|
||||
var method = typeof(StrategicChargeCodeController).GetMethod(methodName);
|
||||
method.Should().NotBeNull($"method '{methodName}' should exist on {nameof(StrategicChargeCodeController)}");
|
||||
|
||||
var attr = method.GetCustomAttribute(httpAttrType);
|
||||
attr.Should().NotBeNull($"{methodName} should be decorated with [{httpAttrType.Name}]");
|
||||
var template = (string)attr.GetType().GetProperty("Template").GetValue(attr);
|
||||
template.Should().Be(expectedTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class StrategicGeneralLedgerControllerTests
|
||||
{
|
||||
private StrategicGeneralLedgerController _controller;
|
||||
private Mock<IStrategicOpportunityService> _mockOpportunityService;
|
||||
private Mock<IStrategicGLOpportunityDetailService> _mockDetailService;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_mockOpportunityService = new Mock<IStrategicOpportunityService>();
|
||||
_mockDetailService = new Mock<IStrategicGLOpportunityDetailService>();
|
||||
_controller = new StrategicGeneralLedgerController(_mockOpportunityService.Object, _mockDetailService.Object);
|
||||
}
|
||||
|
||||
// --- GetOpportunityGL ---
|
||||
|
||||
[Test]
|
||||
public async Task GetOpportunityGL_ReturnsOk_WhenFound()
|
||||
{
|
||||
const long opportunityId = 1;
|
||||
var data = new StrategicOpportunityGLEditData();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityGL(opportunityId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(data);
|
||||
|
||||
var result = await _controller.GetOpportunityGL(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetOpportunityGL_ReturnsNotFound_WhenMissing()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityGL(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityGLEditData)null);
|
||||
|
||||
var result = await _controller.GetOpportunityGL(99, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- CreateOpportunityGeneralLedger ---
|
||||
|
||||
[Test]
|
||||
public async Task CreateOpportunityGeneralLedger_ReturnsOk_WithNewId_WhenSuccessful()
|
||||
{
|
||||
const long newId = 42;
|
||||
var dto = new StrategicOpportunityGLDto();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CreateOpportunityGeneralLedgerAsync(dto, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(newId);
|
||||
|
||||
var result = await _controller.CreateOpportunityGeneralLedger(dto, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(newId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateOpportunityGeneralLedger_ReturnsBadRequest_WhenServiceReturnsZero()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CreateOpportunityGeneralLedgerAsync(It.IsAny<StrategicOpportunityGLDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(0L);
|
||||
|
||||
var result = await _controller.CreateOpportunityGeneralLedger(new StrategicOpportunityGLDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
|
||||
// --- UpdateOpportunityGeneralLedger ---
|
||||
|
||||
[Test]
|
||||
public async Task UpdateOpportunityGeneralLedger_ReturnsOk_WhenSuccessful()
|
||||
{
|
||||
const long opportunityId = 5;
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateOpportunityGeneralLedger(opportunityId, It.IsAny<StrategicOpportunityGLUpdateDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(opportunityId);
|
||||
|
||||
var result = await _controller.UpdateOpportunityGeneralLedger(opportunityId, new StrategicOpportunityGLUpdateDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateOpportunityGeneralLedger_ReturnsNotFound_WhenServiceReturnsZero()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateOpportunityGeneralLedger(It.IsAny<long>(), It.IsAny<StrategicOpportunityGLUpdateDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(0L);
|
||||
|
||||
var result = await _controller.UpdateOpportunityGeneralLedger(99, new StrategicOpportunityGLUpdateDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- DeleteOpportunityGeneralLedger ---
|
||||
|
||||
[Test]
|
||||
public async Task DeleteOpportunityGeneralLedger_ReturnsNotFound_WhenOpportunityDoesNotExist()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.ContainsGLAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.DeleteOpportunityGeneralLedger(99, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NotFoundResult>();
|
||||
_mockOpportunityService.Verify(s => s.DeleteGeneralLedgerOpportunityAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteOpportunityGeneralLedger_ReturnsNoContent_WhenDeleted()
|
||||
{
|
||||
const long opportunityId = 7;
|
||||
_mockOpportunityService.Setup(s => s.ContainsGLAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
_mockOpportunityService.Setup(s => s.DeleteGeneralLedgerOpportunityAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.DeleteOpportunityGeneralLedger(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NoContentResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteOpportunityGeneralLedger_ReturnsBadRequest_WhenDeleteFails()
|
||||
{
|
||||
const long opportunityId = 7;
|
||||
_mockOpportunityService.Setup(s => s.ContainsGLAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
_mockOpportunityService.Setup(s => s.DeleteGeneralLedgerOpportunityAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.DeleteOpportunityGeneralLedger(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
|
||||
// --- GetGLDetail ---
|
||||
|
||||
[Test]
|
||||
public async Task GetGLDetail_ReturnsOk_WhenFound()
|
||||
{
|
||||
const long opportunityId = 3;
|
||||
var dimensions = new[] { "system", "entity" };
|
||||
var data = new StrategicOpportunityGLDetailData();
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityGLDetailData(opportunityId, false, dimensions, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(data);
|
||||
|
||||
var result = await _controller.GetGLDetail(opportunityId, dimensions, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGLDetail_ReturnsNotFound_WhenMissing()
|
||||
{
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityGLDetailData(It.IsAny<long>(), false, It.IsAny<string[]>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityGLDetailData)null);
|
||||
|
||||
var result = await _controller.GetGLDetail(99, Array.Empty<string>(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGLDetail_PassesIsTrackingFalse_ToService()
|
||||
{
|
||||
const long opportunityId = 3;
|
||||
var dimensions = new[] { "system" };
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityGLDetailData(opportunityId, false, dimensions, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new StrategicOpportunityGLDetailData());
|
||||
|
||||
await _controller.GetGLDetail(opportunityId, dimensions, CancellationToken.None);
|
||||
|
||||
_mockDetailService.Verify(s => s.GetStrategicOpportunityGLDetailData(opportunityId, false, dimensions, CancellationToken.None), Times.Once);
|
||||
}
|
||||
|
||||
// --- GetGLTrackingDetail ---
|
||||
|
||||
[Test]
|
||||
public async Task GetGLTrackingDetail_ReturnsOk_WhenFound()
|
||||
{
|
||||
const long opportunityId = 4;
|
||||
var dimensions = new[] { "system", "glAccount" };
|
||||
var data = new StrategicOpportunityGLDetailData();
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityGLDetailData(opportunityId, true, dimensions, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(data);
|
||||
|
||||
var result = await _controller.GetGLTrackingDetail(opportunityId, dimensions, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGLTrackingDetail_ReturnsNotFound_WhenMissing()
|
||||
{
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityGLDetailData(It.IsAny<long>(), true, It.IsAny<string[]>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityGLDetailData)null);
|
||||
|
||||
var result = await _controller.GetGLTrackingDetail(99, Array.Empty<string>(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGLTrackingDetail_PassesIsTrackingTrue_ToService()
|
||||
{
|
||||
const long opportunityId = 4;
|
||||
var dimensions = new[] { "system" };
|
||||
_mockDetailService
|
||||
.Setup(s => s.GetStrategicOpportunityGLDetailData(opportunityId, true, dimensions, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new StrategicOpportunityGLDetailData());
|
||||
|
||||
await _controller.GetGLTrackingDetail(opportunityId, dimensions, CancellationToken.None);
|
||||
|
||||
_mockDetailService.Verify(s => s.GetStrategicOpportunityGLDetailData(opportunityId, true, dimensions, CancellationToken.None), Times.Once);
|
||||
}
|
||||
|
||||
// --- UpdateGLDetails ---
|
||||
|
||||
[Test]
|
||||
public async Task UpdateGLDetails_ReturnsBadRequest_WhenDtoIsNull()
|
||||
{
|
||||
var result = await _controller.UpdateGLDetails(1, null, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityGLDetailsData(
|
||||
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityGLAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateGLDetails_ReturnsBadRequest_WhenAdjustmentsIsEmpty()
|
||||
{
|
||||
var dto = new UpdateGLDetailsDto
|
||||
{
|
||||
Adjustments = new List<StrategicOpportunityGLAdjustment>(),
|
||||
Rows = new List<string> { "system", "entity" }
|
||||
};
|
||||
|
||||
var result = await _controller.UpdateGLDetails(1, dto, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityGLDetailsData(
|
||||
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityGLAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateGLDetails_ReturnsBadRequest_WhenRowsIsEmpty()
|
||||
{
|
||||
var dto = new UpdateGLDetailsDto
|
||||
{
|
||||
Adjustments = new List<StrategicOpportunityGLAdjustment> { new() { NodeKey = "101", GoalAmountValue = 5000m } },
|
||||
Rows = new List<string>()
|
||||
};
|
||||
|
||||
var result = await _controller.UpdateGLDetails(1, dto, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<BadRequestResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityGLDetailsData(
|
||||
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityGLAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateGLDetails_ReturnsOk_AndCallsService_WhenDtoIsValid()
|
||||
{
|
||||
const long opportunityId = 42;
|
||||
var dto = new UpdateGLDetailsDto
|
||||
{
|
||||
Adjustments = new List<StrategicOpportunityGLAdjustment> { new() { NodeKey = "101", GoalAmountValue = 5000m } },
|
||||
Rows = new List<string> { "system", "entity", "glAccount" }
|
||||
};
|
||||
|
||||
var result = await _controller.UpdateGLDetails(opportunityId, dto, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<OkResult>();
|
||||
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityGLDetailsData(
|
||||
opportunityId, dto.Adjustments, dto.Rows, CancellationToken.None), Times.Once);
|
||||
}
|
||||
|
||||
// --- Route attribute assertions ---
|
||||
|
||||
[Test]
|
||||
public void Controller_HasExpectedRouteTemplate()
|
||||
{
|
||||
var routeAttr = typeof(StrategicGeneralLedgerController).GetCustomAttribute<RouteAttribute>();
|
||||
|
||||
routeAttr.Should().NotBeNull();
|
||||
routeAttr.Template.Should().Be("api/v{api-version:apiVersion}/[controller]");
|
||||
}
|
||||
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.GetOpportunityGL), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.CreateOpportunityGeneralLedger), typeof(HttpPostAttribute), null)]
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.UpdateOpportunityGeneralLedger), typeof(HttpPutAttribute), "{opportunityId}")]
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.DeleteOpportunityGeneralLedger), typeof(HttpDeleteAttribute), "{opportunityId}")]
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.GetGLDetail), typeof(HttpPostAttribute), "{opportunityId}/detail")]
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.GetGLTrackingDetail), typeof(HttpPostAttribute), "{opportunityId}/tracking-detail")]
|
||||
[TestCase(nameof(StrategicGeneralLedgerController.UpdateGLDetails), typeof(HttpPutAttribute), "{opportunityId}/details")]
|
||||
public void ActionRoute_HasExpectedHttpMethodAndTemplate(string methodName, Type httpAttrType, string expectedTemplate)
|
||||
{
|
||||
var method = typeof(StrategicGeneralLedgerController).GetMethod(methodName);
|
||||
method.Should().NotBeNull($"method '{methodName}' should exist on {nameof(StrategicGeneralLedgerController)}");
|
||||
|
||||
var attr = method.GetCustomAttribute(httpAttrType);
|
||||
attr.Should().NotBeNull($"{methodName} should be decorated with [{httpAttrType.Name}]");
|
||||
var template = (string)attr.GetType().GetProperty("Template").GetValue(attr);
|
||||
template.Should().Be(expectedTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.Controllers;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class StrategicOpportunityControllerTests
|
||||
{
|
||||
private StrategicOpportunityController _controller;
|
||||
private Mock<IStrategicOpportunityService> _mockOpportunityService;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_mockOpportunityService = new Mock<IStrategicOpportunityService>();
|
||||
_controller = new StrategicOpportunityController(_mockOpportunityService.Object);
|
||||
}
|
||||
|
||||
// --- GetGeneralInfo ---
|
||||
|
||||
[Test]
|
||||
public async Task GetGeneralInfo_ReturnsOk_WhenOpportunityExists()
|
||||
{
|
||||
const long opportunityId = 1;
|
||||
var info = new StrategicOpportunityGeneralInfo();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityGeneralInfo(opportunityId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(info);
|
||||
|
||||
var result = await _controller.GetGeneralInfo(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(info);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetGeneralInfo_ReturnsNotFound_WhenOpportunityMissing()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityGeneralInfo(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityGeneralInfo)null);
|
||||
|
||||
var result = await _controller.GetGeneralInfo(99, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- GetDrawerDetails ---
|
||||
|
||||
[Test]
|
||||
public async Task GetDrawerDetails_ReturnsOk_WhenOpportunityExists()
|
||||
{
|
||||
const long opportunityId = 5;
|
||||
var details = new StrategicOpportunityDrawerDetails();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityDrawerDetails(opportunityId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(details);
|
||||
|
||||
var result = await _controller.GetDrawerDetails(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(details);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetDrawerDetails_ReturnsNotFound_WhenOpportunityMissing()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityDrawerDetails(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((StrategicOpportunityDrawerDetails)null);
|
||||
|
||||
var result = await _controller.GetDrawerDetails(99, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- Copy ---
|
||||
|
||||
[Test]
|
||||
public async Task Copy_ReturnsOk_WithNewId_WhenSuccessful()
|
||||
{
|
||||
const long opportunityId = 10;
|
||||
const long newId = 42;
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CopyStrategicOpportunity(opportunityId, It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(newId);
|
||||
|
||||
var result = await _controller.Copy(opportunityId, "Copy of Opp", CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(newId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Copy_ReturnsBadRequest_WhenServiceReturnsZero()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CopyStrategicOpportunity(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(0L);
|
||||
|
||||
var result = await _controller.Copy(10, "Copy of Opp", CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<BadRequestResult>();
|
||||
}
|
||||
|
||||
// --- SetStatus ---
|
||||
|
||||
[Test]
|
||||
public async Task SetStatus_ReturnsOk_WhenUpdateSucceeds()
|
||||
{
|
||||
const long opportunityId = 3;
|
||||
var dto = new StrategicOpportunityUpdateStatusDto();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateStrategicOpportunityStatus(opportunityId, dto, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.SetStatus(opportunityId, dto, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SetStatus_ReturnsNotFound_WhenUpdateFails()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateStrategicOpportunityStatus(It.IsAny<long>(), It.IsAny<StrategicOpportunityUpdateStatusDto>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.SetStatus(99, new StrategicOpportunityUpdateStatusDto(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- UpdateLayout ---
|
||||
|
||||
[Test]
|
||||
public async Task UpdateLayout_ReturnsOk_WhenUpdateSucceeds()
|
||||
{
|
||||
const long opportunityId = 7;
|
||||
var layout = new StrategicOpportunityLayout();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateStrategicOpportunityLayout(opportunityId, layout, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.UpdateLayout(opportunityId, layout, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateLayout_ReturnsNotFound_WhenUpdateFails()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateStrategicOpportunityLayout(It.IsAny<long>(), It.IsAny<StrategicOpportunityLayout>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.UpdateLayout(99, new StrategicOpportunityLayout(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- UpdateRoles ---
|
||||
|
||||
[Test]
|
||||
public async Task UpdateRoles_ReturnsOk_WhenUpdateSucceeds()
|
||||
{
|
||||
const long opportunityId = 4;
|
||||
var dto = new StrategicRolesUpdate();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateRoles(opportunityId, dto, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.UpdateRoles(opportunityId, dto, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateRoles_ReturnsNotFound_WhenUpdateFails()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.UpdateRoles(It.IsAny<long>(), It.IsAny<StrategicRolesUpdate>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.UpdateRoles(99, new StrategicRolesUpdate(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- RunTrackingTask ---
|
||||
|
||||
[Test]
|
||||
public async Task RunTrackingTask_ReturnsOk_WhenSuccessful()
|
||||
{
|
||||
const long opportunityId = 8;
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.RunTrackingTask(opportunityId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.RunTrackingTask(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RunTrackingTask_ReturnsNotFound_WhenOpportunityMissing()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.RunTrackingTask(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.RunTrackingTask(99, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- GetMeasures ---
|
||||
|
||||
[Test]
|
||||
public async Task GetMeasures_ReturnsOk_WhenMeasuresExist()
|
||||
{
|
||||
const long opportunityId = 2;
|
||||
var measures = new List<StrategicOpportunityMeasureDto> { new() };
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityMeasures(opportunityId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(measures);
|
||||
|
||||
var result = await _controller.GetMeasures(opportunityId, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>()
|
||||
.Which.Value.Should().Be(measures);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetMeasures_ReturnsNotFound_WhenNoMeasures()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.GetStrategicOpportunityMeasures(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<StrategicOpportunityMeasureDto>());
|
||||
|
||||
var result = await _controller.GetMeasures(99, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- CompleteInitiative ---
|
||||
|
||||
[Test]
|
||||
public async Task CompleteInitiative_ReturnsOk_WhenSuccessful()
|
||||
{
|
||||
const long opportunityId = 6;
|
||||
var data = new StrategicOpportunityCompleteInitiativeData();
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CompleteInitiative(opportunityId, data, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var result = await _controller.CompleteInitiative(opportunityId, data, CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<OkObjectResult>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CompleteInitiative_ReturnsNotFound_WhenOpportunityMissing()
|
||||
{
|
||||
_mockOpportunityService
|
||||
.Setup(s => s.CompleteInitiative(It.IsAny<long>(), It.IsAny<StrategicOpportunityCompleteInitiativeData>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await _controller.CompleteInitiative(99, new StrategicOpportunityCompleteInitiativeData(), CancellationToken.None);
|
||||
|
||||
result.Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
// --- Route attribute assertions ---
|
||||
|
||||
[Test]
|
||||
public void Controller_HasExpectedRouteTemplate()
|
||||
{
|
||||
var routeAttr = typeof(StrategicOpportunityController).GetCustomAttribute<RouteAttribute>();
|
||||
|
||||
routeAttr.Should().NotBeNull();
|
||||
routeAttr.Template.Should().Be("api/v{api-version:apiVersion}/[controller]");
|
||||
}
|
||||
|
||||
[TestCase(nameof(StrategicOpportunityController.GetOpportunities), typeof(HttpGetAttribute), "")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetGeneralInfo), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.IsNameValid), typeof(HttpPostAttribute), "[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetLayout), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.UpdateLayout), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.SetStatus), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetInitiatives), typeof(HttpGetAttribute), "[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.UpdateInitiative), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.UpdateRoles), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.UpdateRampUps), typeof(HttpPutAttribute), "{opportunityId}/ramp-ups")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetRampUps), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.RunTrackingTask), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.CompleteInitiative), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetMeasures), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetPrimaryMeasure), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetMeasureDetails), typeof(HttpGetAttribute), "{measureId}/[action]")]
|
||||
[TestCase(nameof(StrategicOpportunityController.GetDrawerDetails), typeof(HttpGetAttribute), "{opportunityId}")]
|
||||
[TestCase(nameof(StrategicOpportunityController.Copy), typeof(HttpPostAttribute), "{opportunityId}/copy")]
|
||||
public void ActionRoute_HasExpectedHttpMethodAndTemplate(string methodName, Type httpAttrType, string expectedTemplate)
|
||||
{
|
||||
var method = typeof(StrategicOpportunityController).GetMethod(methodName);
|
||||
method.Should().NotBeNull($"method '{methodName}' should exist on {nameof(StrategicOpportunityController)}");
|
||||
|
||||
var attr = method.GetCustomAttribute(httpAttrType);
|
||||
attr.Should().NotBeNull($"{methodName} should be decorated with [{httpAttrType.Name}]");
|
||||
var template = (string)attr.GetType().GetProperty("Template").GetValue(attr);
|
||||
template.Should().Be(expectedTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Network;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Generics
|
||||
{
|
||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, EnumMappingIgnoreCase = true)]
|
||||
public static partial class ClientNetworkOpportunityMapper
|
||||
{
|
||||
|
||||
public static ClientNetworkOpportunityDto MapToDto(this ClientNetworkOpportunity src)
|
||||
{
|
||||
var mapped = src.Mapped();
|
||||
return mapped;
|
||||
}
|
||||
|
||||
private static partial ClientNetworkOpportunityDto Mapped(this ClientNetworkOpportunity src);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityComments;
|
||||
using static Strata.ContinuousImprovement.Api.Controllers.CommentsController;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Api.Generics
|
||||
{
|
||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, EnumMappingIgnoreCase = true)]
|
||||
public static partial class CommentMapper
|
||||
{
|
||||
public static partial Comment Map(this NewComment src);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Hangfire;
|
||||
using Hangfire.Common;
|
||||
using Hangfire.States;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.Generics
|
||||
{
|
||||
public class FakeBackgroundJobClient : IBackgroundJobClient
|
||||
{
|
||||
public Queue<Job> Jobs { get; } = new Queue<Job>();
|
||||
|
||||
public string Create(Job job, IState state)
|
||||
{
|
||||
Jobs.Enqueue(job);
|
||||
return Jobs.Count.ToString();
|
||||
}
|
||||
|
||||
public bool ChangeState(string jobId, IState state, string expectedState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Api.SnowflakeControllers;
|
||||
using Strata.DecisionSupport.Biz.Mapperly;
|
||||
using Strata.DecisionSupport.Biz.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
using VerifyTests;
|
||||
using Encounters = Strata.DecisionSupport.Biz.Encounters;
|
||||
using Parameters = Strata.DecisionSupport.Biz.Parameters;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit.SnowflakeControllers
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("Controller")]
|
||||
public class UtilizationVariationControllerTest
|
||||
{
|
||||
public VerifySettings verifySettings;
|
||||
|
||||
private UtilizationVariationController controller;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var charges = new List<Encounters.ChargeInfo> {
|
||||
new Encounters.ChargeInfo()
|
||||
};
|
||||
var jazzCharges = charges.Select(c => JazzEncounterInfoMapper.MapToJazz(c));
|
||||
var service = Mock.Of<UtilizationVariationService>();
|
||||
|
||||
controller = new UtilizationVariationController(service);
|
||||
verifySettings = TestExtensions.TestSettings();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestController()
|
||||
{
|
||||
var controllerExists = controller != null;
|
||||
await Verifier.Verify(controllerExists, verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetChargeInfoTestCases))]
|
||||
public async Task TestGetChargeInfo(Guid opportunityGuid, IEnumerable<int> chargeCodeIds)
|
||||
{
|
||||
var queryInfo = new Parameters.ChargeQueryInfo(Guid.NewGuid(), 10, chargeCodeIds.ToList(), opportunityGuid);
|
||||
var response = await controller.GetChargeInfoAsync(queryInfo, CancellationToken.None);
|
||||
await Verifier.Verify(response, verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetChargeInfoTestCases))]
|
||||
public async Task TestGetAllChargeInfo(Guid opportunityGuid, IEnumerable<int> chargeCodeIds)
|
||||
{
|
||||
var queryInfo = new Parameters.AllChargeQueryInfo(opportunityGuid, chargeCodeIds.ToList());
|
||||
var response = await controller.GetAllChargeInfoAsync(queryInfo, CancellationToken.None);
|
||||
await Verifier.Verify(response, verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> GetChargeInfoTestCases()
|
||||
{
|
||||
yield return new TestCaseData(Guid.Empty, Enumerable.Empty<int>())
|
||||
.SetName("{m} Empty OpportunityGuid");
|
||||
yield return new TestCaseData(Guid.Empty, new[] { 1 })
|
||||
.SetName("{m} with ChargeCodes");
|
||||
yield return new TestCaseData(Guid.NewGuid(), new[] { 1 })
|
||||
.SetName("{m} with Opportunity and ChargeCodes");
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Configuration\**" />
|
||||
<Compile Remove="Resource\Examples\AllOpportunity\**" />
|
||||
<Compile Remove="Utilities\**" />
|
||||
<EmbeddedResource Remove="Configuration\**" />
|
||||
<EmbeddedResource Remove="Resource\Examples\AllOpportunity\**" />
|
||||
<EmbeddedResource Remove="Utilities\**" />
|
||||
<None Remove="Configuration\**" />
|
||||
<None Remove="Resource\Examples\AllOpportunity\**" />
|
||||
<None Remove="Utilities\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Biz\All\OpportunityType.cs" />
|
||||
<Compile Remove="Biz\Generics\ConfigurationServiceTest.cs" />
|
||||
<Compile Remove="Biz\Generics\DbSetExtensions.cs" />
|
||||
<Compile Remove="Biz\Generics\VariationOpportunityServiceTest.cs" />
|
||||
<Compile Remove="Biz\SlugifyParameterTransformerTest.cs" />
|
||||
<Compile Remove="Biz\StaffingToDemand\Opportunities\StaffingToDemandOpportunityServiceTest.cs" />
|
||||
<Compile Remove="Biz\UtilizationVariation\Opportunities\VariationOpportunityFilter.cs" />
|
||||
<Compile Remove="Generics\OpportunityTypeExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resource\Examples\ExcelFilter.xlsx" />
|
||||
<None Remove="Resource\PRExcelFilter.xlsx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Strata - Backup.ContinuousImprovement.Test.Unit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClosedXML.Signed" Version="0.95.4" />
|
||||
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="NUnit" Version="4.3.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Remotion.Linq.Development" Version="2.2.0" />
|
||||
<PackageReference Include="Talentsoft.Moq.SetupAsync" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Strata.ContinuousImprovement.Biz\Strata.ContinuousImprovement.Biz.csproj" />
|
||||
<ProjectReference Include="..\..\src\Strata.ContinuousImprovement.Api\Strata.ContinuousImprovement.Api.csproj" />
|
||||
<ProjectReference Include="..\Strata.ContinuousImprovement.Biz.Test.Unit\Strata.ContinuousImprovement.Biz.Test.Unit.csproj" />
|
||||
<ProjectReference Include="..\Strata.ContinuousImprovement.Biz.Test.Unit\Strata.ContinuousImprovement.Biz.Test.Unit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Resource\Examples\AllOpportunity\" />
|
||||
<Folder Include="Resource\Examples\EncounterOpportunity\" />
|
||||
<Folder Include="Resource\Examples\FreeFormOpportunity\" />
|
||||
<Folder Include="Resource\Examples\GeneralLedgerOpportunity\" />
|
||||
<Folder Include="Resource\Examples\PayrollOpportunity\" />
|
||||
<Folder Include="Resource\Examples\QualityVariationItemOpportunity\" />
|
||||
<Folder Include="Resource\Examples\StaffingToDemandOpportunity\" />
|
||||
<Folder Include="Resource\Examples\VariationOpportunity\" />
|
||||
<Folder Include="SnowflakeControllers\snapshots\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,47 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using VerifyTests;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Api.Test.Unit
|
||||
{
|
||||
public static class TestExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The test name
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>
|
||||
/// The name of the test split by camel case for a max length of 200 characters.
|
||||
/// </returns>
|
||||
public static string TestName(this TestContext context)
|
||||
{
|
||||
var testName = context.Test.Name.SplitCamelCase();
|
||||
testName = testName[..Math.Min(200, testName.Length)];
|
||||
return testName;
|
||||
}
|
||||
|
||||
public static string SplitCamelCase(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) { return input; }
|
||||
|
||||
const string pattern = "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-r + t-z])|[A-Z](?=[A-Z][s][a-z]))";
|
||||
|
||||
if (input.StartsWith("FY", StringComparison.Ordinal))
|
||||
{ // hack for FY2012, etc
|
||||
return "FY " + Regex.Replace(input.Remove(0, 2), pattern, "$1 ");
|
||||
}
|
||||
input = Regex.Replace(input, @"\{|\}|:|,|\)|\(", "", RegexOptions.Singleline | RegexOptions.IgnoreCase);
|
||||
return Regex.Replace(input, pattern, "$1 ");
|
||||
}
|
||||
|
||||
public static VerifySettings TestSettings()
|
||||
{
|
||||
var settings = new VerifySettings();
|
||||
settings.UseDirectory("snapshots");
|
||||
settings.ScrubInlineGuids();
|
||||
return settings;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"configProperties": {
|
||||
"System.Drawing.EnableUnixSupport": true
|
||||
}
|
||||
}
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Strata.ContinuousImprovement.Biz.ClientWarehouses;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.GMDN;
|
||||
using Strata.ContinuousImprovement.Biz.Network;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityComments;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityTypeWorkbooks;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityWorkbooks;
|
||||
using Strata.ContinuousImprovement.Biz.Population;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.CentralDbIntegrationTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
public class TestCentralDbOrm : IntegrationTestBase
|
||||
{
|
||||
public async Task TestGeneric<T>(TestContext testContext) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
var testName = testContext.TestName(typeof(T).Name);
|
||||
var dbSet = ((DbContext)CentralDbContext).Set<T>();
|
||||
var count = await dbSet.CountAsync(CancellationToken.None);
|
||||
Assert.That(count, Is.GreaterThanOrEqualTo(0));
|
||||
if (count == 0)
|
||||
{
|
||||
if (Activator.CreateInstance(typeof(T)) is T obj)
|
||||
{
|
||||
await dbSet.AddAsync(obj, CancellationToken.None);
|
||||
await ((DbContext)CentralDbContext).SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
var result = await dbSet.FirstOrDefaultAsync(CancellationToken.None);
|
||||
Assert.That(result, Is.Not.Null.And.InstanceOf<T>());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TestFirst<T>(TestContext testContext) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
var testName = testContext.TestName(typeof(T).Name) + " First";
|
||||
var dbSet = ((DbContext)CentralDbContext).Set<T>();
|
||||
var any = await dbSet.AnyAsync(CancellationToken.None);
|
||||
if (!any)
|
||||
{
|
||||
if (Activator.CreateInstance(typeof(T)) is T obj)
|
||||
{
|
||||
await dbSet.AddAsync(obj, CancellationToken.None);
|
||||
await ((DbContext)CentralDbContext).SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
var result = await dbSet.FirstOrDefaultAsync(CancellationToken.None);
|
||||
Assert.That(result, Is.Not.Null.And.InstanceOf<T>());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TestCount<T>(TestContext testContext) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
var testName = testContext.TestName(typeof(T).Name) + " Count";
|
||||
var dbSet = ((DbContext)CentralDbContext).Set<T>();
|
||||
var count = await dbSet.CountAsync(CancellationToken.None);
|
||||
Assert.That(count, Is.GreaterThanOrEqualTo(0));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TestCaseSources))]
|
||||
public async Task TestOrmCases(string typeName, bool first)
|
||||
{
|
||||
var entityType = ((DbContext)CentralDbContext).Model.GetEntityTypes()
|
||||
.SingleOrDefault(et => et.ClrType.Name == typeName);
|
||||
if (entityType == null) { return; }
|
||||
if (first) { FirstCaseTests(typeName); }
|
||||
CountCaseTests(typeName);
|
||||
}
|
||||
|
||||
public async Task FirstCaseTests(string typeName)
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case nameof(ClientNetworkOpportunity):
|
||||
await TestFirst<ClientNetworkOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ClientNetworkOpportunityLink):
|
||||
await TestFirst<ClientNetworkOpportunityLink>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ClientWarehouse):
|
||||
await TestFirst<ClientWarehouse>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Comment):
|
||||
await TestFirst<Comment>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DeviceIdentifierDimension):
|
||||
await TestFirst<DeviceIdentifierDimension>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DeviceIdentifierTerm):
|
||||
await TestFirst<DeviceIdentifierTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNCategory):
|
||||
await TestFirst<GMDNCategory>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNTerm):
|
||||
await TestFirst<GMDNTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNTermCategory):
|
||||
await TestFirst<GMDNTermCategory>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDDevice):
|
||||
await TestFirst<GUDIDDevice>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDIdentifier):
|
||||
await TestFirst<GUDIDIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDTerm):
|
||||
await TestFirst<GUDIDTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(NetworkOpportunity):
|
||||
await TestFirst<NetworkOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityTypeWorkbook):
|
||||
await TestFirst<OpportunityTypeWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityWorkbook):
|
||||
await TestFirst<OpportunityWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(TmpGUDIDIdentifier):
|
||||
await TestFirst<TmpGUDIDIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CountCaseTests(string typeName)
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case nameof(ClientNetworkOpportunity):
|
||||
await TestCount<ClientNetworkOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ClientNetworkOpportunityLink):
|
||||
await TestCount<ClientNetworkOpportunityLink>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ClientWarehouse):
|
||||
await TestCount<ClientWarehouse>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Comment):
|
||||
await TestCount<Comment>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DeviceIdentifierDimension):
|
||||
await TestCount<DeviceIdentifierDimension>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DeviceIdentifierTerm):
|
||||
await TestCount<DeviceIdentifierTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNCategory):
|
||||
await TestCount<GMDNCategory>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNTerm):
|
||||
await TestCount<GMDNTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNTermCategory):
|
||||
await TestCount<GMDNTermCategory>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDDevice):
|
||||
await TestCount<GUDIDDevice>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDIdentifier):
|
||||
await TestCount<GUDIDIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDTerm):
|
||||
await TestCount<GUDIDTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(NetworkOpportunity):
|
||||
await TestCount<NetworkOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityTypeWorkbook):
|
||||
await TestCount<OpportunityTypeWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityWorkbook):
|
||||
await TestCount<OpportunityWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(TmpGUDIDIdentifier):
|
||||
await TestCount<TmpGUDIDIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task MissingCentralDbContextTestCases()
|
||||
{
|
||||
var types = ((DbContext)CentralDbContext).Model.GetEntityTypes()
|
||||
.Select(e => e.ClrType).OrderBy(c => c.Name).ToList();
|
||||
var missingTests = TestCaseSources()
|
||||
.Select(tc => tc.Arguments.First().ToString())
|
||||
.Where(tc => !types.Select(t => t.Name).Contains(tc));
|
||||
foreach (var test in missingTests)
|
||||
{
|
||||
try
|
||||
{
|
||||
throw new NotImplementedException(test);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ILB
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CentralDbContextTestCases()
|
||||
{
|
||||
var types = ((DbContext)CentralDbContext).Model.GetEntityTypes()
|
||||
.Select(e => e.ClrType).OrderBy(c => c.Name).ToList();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
switch (type.Name)
|
||||
{
|
||||
case nameof(ClientNetworkOpportunity):
|
||||
await TestGeneric<ClientNetworkOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ClientNetworkOpportunityLink):
|
||||
await TestGeneric<ClientNetworkOpportunityLink>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ClientWarehouse):
|
||||
await TestGeneric<ClientWarehouse>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Comment):
|
||||
await TestGeneric<Comment>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DeviceIdentifierDimension):
|
||||
await TestGeneric<DeviceIdentifierDimension>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DeviceIdentifierTerm):
|
||||
await TestGeneric<DeviceIdentifierTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNCategory):
|
||||
await TestGeneric<GMDNCategory>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNTerm):
|
||||
await TestGeneric<GMDNTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GMDNTermCategory):
|
||||
await TestGeneric<GMDNTermCategory>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDDevice):
|
||||
await TestGeneric<GUDIDDevice>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDIdentifier):
|
||||
await TestGeneric<GUDIDIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GUDIDTerm):
|
||||
await TestGeneric<GUDIDTerm>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(NetworkOpportunity):
|
||||
await TestGeneric<NetworkOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityTypeWorkbook):
|
||||
await TestGeneric<OpportunityTypeWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityWorkbook):
|
||||
await TestGeneric<OpportunityWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(TmpGUDIDIdentifier):
|
||||
await TestGeneric<TmpGUDIDIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ExplorationInclusionExclusion):
|
||||
await TestGeneric<ExplorationInclusionExclusion>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ExplorationPopulation):
|
||||
await TestGeneric<ExplorationPopulation>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ExplorationPopulationEncounter):
|
||||
await TestGeneric<ExplorationPopulationEncounter>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicOpportunity):
|
||||
await TestGeneric<StrategicOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicOpportunityChargeCode):
|
||||
await TestGeneric<StrategicOpportunityChargeCode>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicOpportunityGL):
|
||||
await TestGeneric<StrategicOpportunityGL>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicStakeholdersLink):
|
||||
await TestGeneric<StrategicStakeholdersLink>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicOpportunityMeasure):
|
||||
await TestGeneric<StrategicOpportunityMeasure>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicOpportunityWorkbook):
|
||||
await TestGeneric<StrategicOpportunityWorkbook>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(StrategicOpportunityRampUp):
|
||||
await TestGeneric<StrategicOpportunityRampUp>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityRoleAssignment.OpportunityRoleAssignment):
|
||||
await TestGeneric<OpportunityRoleAssignment.OpportunityRoleAssignment>(TestContext.CurrentContext);
|
||||
break;
|
||||
default:
|
||||
var pathArr = TestContext.CurrentContext.TestDirectory.Split(Path.DirectorySeparatorChar);
|
||||
var path = string.Join(Path.DirectorySeparatorChar, pathArr[..(pathArr.Length - 3)].Append("CentralDbIntegrationTests"));
|
||||
var fileName = Path.Combine(path, $"{nameof(TestCentralDbOrm)}_{type.Name}.cs");
|
||||
if (File.Exists(fileName)) break;
|
||||
var testFile = @"
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.CentralDbIntegrationTests
|
||||
{
|
||||
[TestFixture, Category(""Integration""), Category(""CCI"")]
|
||||
public partial class TestCentralDbContext : IntegrationTestBase
|
||||
{
|
||||
[Test]
|
||||
public async Task TestCentralDb" + type.Name + @"()
|
||||
{
|
||||
await TestGeneric<" + type.Name + @">(TestContext.CurrentContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
File.WriteAllText(fileName, testFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ClientNetworkOpportunityTest()
|
||||
{
|
||||
var opp = await CentralDbContext.ClientNetworkOpportunities.FirstOrDefaultAsync(CancellationToken.None);
|
||||
opp ??= new ClientNetworkOpportunity();
|
||||
opp.NetworkOpportunity ??= new NetworkOpportunity
|
||||
{
|
||||
Name = nameof(ClientNetworkOpportunityTest),
|
||||
Description = nameof(ClientNetworkOpportunityTest),
|
||||
WorkbookTemplateId = 0
|
||||
};
|
||||
var result = new ClientNetworkOpportunityDto(opp);
|
||||
Assert.That(result, Is.Not.Null.And.InstanceOf<ClientNetworkOpportunityDto>()
|
||||
.And.Property(nameof(ClientNetworkOpportunityDto.Name)).Not.Null
|
||||
.And.Property(nameof(ClientNetworkOpportunityDto.Description)).Not.Null
|
||||
.And.Property(nameof(ClientNetworkOpportunityDto.WorkbookTemplateId)).Not.Null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Strata.ContinuousImprovement.Biz.ClientWarehouses;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.ContinuousImprovementServiceTests.ClientWarehouses
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
public class TestClientWarehouseService : IntegrationTestBase
|
||||
{
|
||||
private IClientWarehouseService _clientWarehouseService;
|
||||
private List<ClientWarehouse> _clientWarehouses;
|
||||
private ClientWarehouse _clientWarehouse = new ClientWarehouse
|
||||
{
|
||||
DatabaseGuid = Guid.NewGuid(),
|
||||
ClientName = "client",
|
||||
WarehouseSize = WarehouseSize.S
|
||||
};
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task SetupServices()
|
||||
{
|
||||
_clientWarehouseService = new ClientWarehouseService((CentralDbContext)CentralDbContext);
|
||||
_clientWarehouses = new List<ClientWarehouse>();
|
||||
for (var i = 1; i <= 10; i++)
|
||||
{
|
||||
var warehouse = new ClientWarehouse
|
||||
{
|
||||
DatabaseGuid = Guid.NewGuid(),
|
||||
ClientName = $"client_{i}",
|
||||
WarehouseSize = WarehouseSize.S
|
||||
};
|
||||
warehouse.ClientWarehouseId = await _clientWarehouseService.Add(warehouse, CancellationToken.None);
|
||||
_clientWarehouses.Add(warehouse);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestAdd()
|
||||
{
|
||||
var id = await _clientWarehouseService.Add(_clientWarehouse, CancellationToken.None);
|
||||
var warehousesExists = await _clientWarehouseService.Exists(_clientWarehouse.DatabaseGuid, CancellationToken.None);
|
||||
Assert.That(warehousesExists, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetByDatabaseGuid()
|
||||
{
|
||||
var warehouseClient = new ClientWarehouse
|
||||
{
|
||||
DatabaseGuid = Guid.NewGuid(),
|
||||
ClientName = "client",
|
||||
WarehouseSize = WarehouseSize.S
|
||||
};
|
||||
var id = await _clientWarehouseService.Add(warehouseClient, CancellationToken.None);
|
||||
var warehouse = await _clientWarehouseService.GetByDatabaseGuid(warehouseClient.DatabaseGuid, CancellationToken.None);
|
||||
Assert.That(warehouse, Is.Not.Null.And.InstanceOf<ClientWarehouse>()
|
||||
.And.Property(nameof(ClientWarehouse.ClientName)).EqualTo(warehouseClient.ClientName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetAll()
|
||||
{
|
||||
var testName = TestContext.CurrentContext.TestName();
|
||||
var warehouses = await _clientWarehouseService.GetAll(CancellationToken.None);
|
||||
var intersect = _clientWarehouses.Intersect(warehouses);
|
||||
Assert.That(intersect, Is.Not.Null.And.Not.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestDelete()
|
||||
{
|
||||
var testName = TestContext.CurrentContext.TestName();
|
||||
var warehouse = new ClientWarehouse
|
||||
{
|
||||
DatabaseGuid = Guid.NewGuid(),
|
||||
ClientName = "client",
|
||||
WarehouseSize = WarehouseSize.S
|
||||
};
|
||||
var id = await _clientWarehouseService.Add(warehouse, CancellationToken.None);
|
||||
var warehousesExists = await _clientWarehouseService.Exists(warehouse.DatabaseGuid, CancellationToken.None);
|
||||
Assert.That(warehousesExists, Is.True);
|
||||
var warehouseDeleted = await _clientWarehouseService.Delete(warehouse.DatabaseGuid, CancellationToken.None);
|
||||
warehousesExists = await _clientWarehouseService.Exists(warehouse.DatabaseGuid, CancellationToken.None);
|
||||
Assert.That(!warehousesExists, Is.EqualTo(warehouseDeleted));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.DecisionSupport.Models.Queries;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.ContinuousImprovementServiceTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
internal class TestClientService : IntegrationTestBase
|
||||
{
|
||||
|
||||
[Test, Ignore("Client is not authorized. Need to revisit when we can auth the user performing tests.")]
|
||||
public async Task ServiceTest()
|
||||
{
|
||||
var queryInfoStr = @"{""HasManufacturerClientDSS"":false,""IsManufacturerInferred"":true,""HasSupplyClientDSS"":false,""IsSupplyInferred"":true,""IsUsingCustomEntityDimension"":false,""IsBaseline"":true,""OpportunityGuid"":""72ff5c93-f9a8-444c-a4a0-cd61c19334fa"",""ChargeCodeIds"":[],""First"":0,""Rows"":10000}";
|
||||
var queryInfo = JsonSerializer.Deserialize<EncounterChargeQueryInfo>(queryInfoStr);
|
||||
var result = await ContinuousImprovementService.DecisionSupportEncounterChargeInfo(queryInfo, CancellationToken.None);
|
||||
|
||||
await Verifier.Verify(result, verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.UtilizationVariation.VariationCaseTypes;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static Strata.ContinuousImprovement.Biz.Exploration.ExplorationService;
|
||||
using ExplorationFilter = Strata.ContinuousImprovement.Biz.Exploration.Filters.Filter;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration")]
|
||||
public class ExplorationFilterServiceIntegrationTests : IntegrationTestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Integration test for BuildDataFiltersAsync with real database context.
|
||||
///
|
||||
/// POTENTIAL FAILURE REASON: Database connection issues or missing test data.
|
||||
///
|
||||
/// TO FIX:
|
||||
/// 1. Ensure test database is properly configured and accessible
|
||||
/// 2. Verify test data exists in the database
|
||||
/// 3. Check connection strings in test configuration
|
||||
/// 4. Consider using test data seeding for consistent results
|
||||
/// </summary>
|
||||
[Test, Category("Integration")]
|
||||
public async Task BuildDataFiltersAsync_WithRealDatabaseContext_HandlesFilterChipProcessing()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new ExplorationFilter
|
||||
{
|
||||
FilterChipItems = new List<FilterChipItem>
|
||||
{
|
||||
new FilterChipItem
|
||||
{
|
||||
ChipType = ChipType.CostDriver,
|
||||
Key = ChipKey.CostDriver,
|
||||
Operator = OperatorType.In,
|
||||
Filters = new List<TreeNode>
|
||||
{
|
||||
new TreeNode { Id = "LOS", DisplayText = "LOS" },
|
||||
new TreeNode { Id = "Pharmacy", DisplayText = "Pharmacy" }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await ExplorationFilterService.BuildDataFiltersAsync(
|
||||
null, null, filter, false, false, false, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.SphChargeCodeCostDriver, Contains.Item("LOS"));
|
||||
Assert.That(result.SphChargeCodeCostDriver, Contains.Item("Pharmacy"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integration test for GetFilterStringsAsync with real database context.
|
||||
///
|
||||
/// POTENTIAL FAILURE REASON: Database schema changes or missing stored procedures.
|
||||
///
|
||||
/// TO FIX:
|
||||
/// 1. Verify database schema matches expected structure
|
||||
/// 2. Ensure all required stored procedures exist
|
||||
/// 3. Check for recent database migrations
|
||||
/// 4. Update test expectations to match current schema
|
||||
/// </summary>
|
||||
[Test, Category("Integration")]
|
||||
public async Task GetFilterStringsAsync_WithRealDatabaseContext_GeneratesValidSqlFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<FilterChipItem>
|
||||
{
|
||||
new FilterChipItem
|
||||
{
|
||||
ChipType = ChipType.Boolean,
|
||||
Key = ChipKey.IsGreaterThanZeroCharges,
|
||||
IsSelected = true,
|
||||
Operator = OperatorType.In
|
||||
},
|
||||
new FilterChipItem
|
||||
{
|
||||
ChipType = ChipType.DateRange,
|
||||
Key = ChipKey.DischargeDateID,
|
||||
DateRange = new List<DateTime>
|
||||
{
|
||||
new DateTime(2024, 1, 1),
|
||||
new DateTime(2024, 12, 31)
|
||||
}
|
||||
}
|
||||
};
|
||||
var queryParams = new QueryParams { CodeType = CaseTypeCategories.MSDRG };
|
||||
|
||||
// Act
|
||||
var result = await ExplorationFilterService.GetFilterStringsAsync(filters, queryParams, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.QueryFilters, Does.StartWith("WHERE"));
|
||||
Assert.That(result.QueryFilters, Does.Contain("el.Charges > 0"));
|
||||
Assert.That(queryParams.DischargeDateStart, Is.EqualTo("2024-01-01"));
|
||||
Assert.That(queryParams.DischargeDateEnd, Is.EqualTo("2025-01-01"));
|
||||
}
|
||||
|
||||
[Test, Category("Integration")]
|
||||
public void CreateCostDriverFilter_GeneratesCompleteFilterList()
|
||||
{
|
||||
// Act
|
||||
var filter = ExplorationFilterService.CreateCostDriverFilter();
|
||||
|
||||
// Assert
|
||||
Assert.That(filter, Is.Not.Null);
|
||||
Assert.That(filter.ChipType, Is.EqualTo(ChipType.CostDriver));
|
||||
Assert.That(filter.Filters.Count(), Is.EqualTo(7)); // Should have all 7 valid SPH cost drivers
|
||||
|
||||
var expectedCostDrivers = new[]
|
||||
{
|
||||
"Anesthesia and Recovery", "Imaging", "Laboratory",
|
||||
"LOS", "OR Time", "Pharmacy", "Supplies"
|
||||
};
|
||||
|
||||
foreach (var expectedDriver in expectedCostDrivers)
|
||||
{
|
||||
Assert.That(filter.Filters.Any(f => f.Id == expectedDriver),
|
||||
Is.True, $"Missing expected cost driver: {expectedDriver}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Snowflake.Data.Client;
|
||||
using Strata.ContinuousImprovement.Biz.ClientWarehouses;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Snowflake;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration;
|
||||
using Strata.ContinuousImprovement.Biz.FiscalMonths;
|
||||
using Strata.ContinuousImprovement.Biz.GMDN;
|
||||
using Strata.ContinuousImprovement.Biz.Network;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityComments;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityTypeWorkbooks;
|
||||
using Strata.ContinuousImprovement.Biz.OpportunityWorkbooks;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.Biz.Utilities;
|
||||
using Strata.ContinuousImprovement.Client;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.Schema.Client;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using Strata.StrataSphereCompare.Client;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[SetUpFixture]
|
||||
public class IntegrationTestBase
|
||||
{
|
||||
protected VerifySettings verifyScrubbedSettings;
|
||||
protected VerifySettings verifySettings;
|
||||
protected VerifySettings verifySqlSettings;
|
||||
protected VerifySettings verifyErrorSettings;
|
||||
|
||||
public const string Quick = "quick";
|
||||
public const string ContinuousImprovementEndpoint = "https://continuousimprovement-api.dev.stratanetwork.net";
|
||||
|
||||
protected readonly Guid GmAutomationDatabase = new("cda90beb-6c2e-41af-9083-857a09fe8183"); // GM Automation (Refreshed)
|
||||
|
||||
protected Guid VariationOpportunityGuid { get; set; }
|
||||
protected Guid LOSOpportunityGuid { get; set; }
|
||||
protected ICentralDbContext CentralDbContext { get; set; }
|
||||
protected JazzEntityFrameworkFactory JazzConnBuilderFactory { get; set; }
|
||||
#pragma warning disable NUnit1032
|
||||
protected IJazzDbContext JazzDbContext { get; set; }
|
||||
protected ISnowflakeDatabaseContext SnowflakeDatabaseContext;
|
||||
protected IServiceProvider ServiceProvider; //used to grab any necessary DI objects
|
||||
#pragma warning restore NUnit1032
|
||||
protected ILogger<IntegrationTestBase>? logger;
|
||||
protected IContinuousImprovementService ContinuousImprovementService;
|
||||
protected IExplorationService ExplorationService;
|
||||
protected ISchemaServiceClient SchemaServiceClient;
|
||||
protected IExplorationFilterService ExplorationFilterService; // ADD THIS LINE
|
||||
protected ILogger<ContinuousImprovementService> ServiceLogger;
|
||||
internal IClaimsPrincipalAccessor ClaimsPrincipalAccessor { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public virtual async Task SetDbContextAndServices()
|
||||
{
|
||||
#if DEBUG
|
||||
//When running locally sets the profile to the CI profile so you have access to resources
|
||||
Environment.SetEnvironmentVariable("AWS_Profile", "sdt-continuous-improvement-service-role");
|
||||
#endif
|
||||
var services = ServiceCollectionFactory.Create();
|
||||
services.AddScoped<ISnowflakeDatabaseContext, SnowflakeDatabaseContext>(provider =>
|
||||
{
|
||||
ISnowflakeDatabaseContextFactory snowflakeDatabaseContextFactory = provider.GetRequiredService<ISnowflakeDatabaseContextFactory>();
|
||||
return snowflakeDatabaseContextFactory.Create($"{GmAutomationDatabase:N}");
|
||||
});
|
||||
ServiceProvider = services.BuildServiceProvider();
|
||||
var sfConnBuilderFactory = ServiceProvider.GetRequiredService<ISnowflakeDatabaseContextFactory>();
|
||||
SnowflakeDatabaseContext = sfConnBuilderFactory.Create($"{GmAutomationDatabase:N}");
|
||||
|
||||
try
|
||||
{
|
||||
var jazzConnBuilderFactory = ServiceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
JazzDbContext = await jazzConnBuilderFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CentralDbContext = ServiceProvider.GetService<CentralDbContext>()
|
||||
?? throw new NullReferenceException(nameof(DbContexts.CentralDbContext));
|
||||
await ((CentralDbContext)CentralDbContext).Database.MigrateAsync(CancellationToken.None);
|
||||
JazzConnBuilderFactory = new JazzEntityFrameworkFactory();
|
||||
await JazzConnBuilderFactory.EnsureCreatedAsync(CentralDbContext, JazzDbContext, CancellationToken.None);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
#region Verifier Settings
|
||||
|
||||
verifySettings = TestExtensions.TestSettings();
|
||||
verifySqlSettings = TestExtensions.TestSettings();
|
||||
verifySqlSettings
|
||||
.ScrubLinesWithReplace(_ =>
|
||||
{
|
||||
var regex = new Regex(@"(.*)\s+in\s+\([\d,]*\)(.*)", RegexOptions.IgnoreCase);
|
||||
if (regex.IsMatch(_))
|
||||
{
|
||||
return regex.Replace(_, "$1 IN (...)$2");
|
||||
}
|
||||
var regexDate = new Regex(@"(.*)'[\d]{1,2}/[\d]{1,2}/[\d]{4}'(.*)");
|
||||
if (regexDate.IsMatch(_))
|
||||
{
|
||||
var line = _;
|
||||
while (regexDate.IsMatch(line))
|
||||
{
|
||||
line = regexDate.Replace(line, "$1'mm/dd/yyyy'$2");
|
||||
}
|
||||
return line;
|
||||
}
|
||||
return _;
|
||||
});
|
||||
verifyScrubbedSettings = TestExtensions.TestSettings();
|
||||
verifyScrubbedSettings
|
||||
.ScrubLinesWithReplace(replaceLine: _ => $"'{"".PadLeft(_.Length, '~')}'");
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<bool>();
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<short>();
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<int>();
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<long>();
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<byte>();
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<decimal>();
|
||||
verifyScrubbedSettings
|
||||
.ScrubMembersWithType<double>();
|
||||
verifyErrorSettings = TestExtensions.TestSettings();
|
||||
verifyErrorSettings.UseDirectory("errorSnapshots");
|
||||
#endregion
|
||||
|
||||
SchemaServiceClient = Mock.Of<ISchemaServiceClient>();
|
||||
ExplorationFilterService = new ExplorationFilterService(
|
||||
ServiceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>(),
|
||||
SchemaServiceClient,
|
||||
Mock.Of<ILogger<ExplorationFilterService>>());
|
||||
|
||||
var httpClientFactory = ServiceProvider.GetRequiredService<IHttpClientFactory>();
|
||||
var httpClient = httpClientFactory.CreateClient(Quick);
|
||||
httpClient.BaseAddress = new Uri(ContinuousImprovementEndpoint);
|
||||
ContinuousImprovementService = new ContinuousImprovementService(httpClient);
|
||||
|
||||
var featureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
|
||||
Mock.Get(featureFlagWrapper)
|
||||
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var strataSphereCompareService = Mock.Of<IStrataSphereCompareService>();
|
||||
|
||||
ExplorationService = new ExplorationService(
|
||||
JazzConnBuilderFactory,
|
||||
SnowflakeDatabaseContext,
|
||||
Mock.Of<IFiscalMonthResolver>(),
|
||||
featureFlagWrapper,
|
||||
strataSphereCompareService,
|
||||
ExplorationFilterService,
|
||||
Mock.Of<ILogger<ExplorationService>>());
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> TestCaseSources()
|
||||
{
|
||||
foreach (bool value in new[] { false, true })
|
||||
{
|
||||
yield return new TestCaseData(nameof(ClientNetworkOpportunity), value)
|
||||
.SetName(nameof(ClientNetworkOpportunity) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(ClientNetworkOpportunityLink), value)
|
||||
.SetName(nameof(ClientNetworkOpportunityLink) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(ClientWarehouse), value)
|
||||
.SetName(nameof(ClientWarehouse) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(Comment), value)
|
||||
.SetName(nameof(Comment) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(DeviceIdentifierDimension), value)
|
||||
.SetName(nameof(DeviceIdentifierDimension) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(DeviceIdentifierTerm), value)
|
||||
.SetName(nameof(DeviceIdentifierTerm) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(GMDNCategory), value)
|
||||
.SetName(nameof(GMDNCategory) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(GMDNTerm), value)
|
||||
.SetName(nameof(GMDNTerm) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(GMDNTermCategory), value)
|
||||
.SetName(nameof(GMDNTermCategory) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(GUDIDDevice), value)
|
||||
.SetName(nameof(GUDIDDevice) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(GUDIDIdentifier), value)
|
||||
.SetName(nameof(GUDIDIdentifier) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(GUDIDTerm), value)
|
||||
.SetName(nameof(GUDIDTerm) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(NetworkOpportunity), value)
|
||||
.SetName(nameof(NetworkOpportunity) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(OpportunityTypeWorkbook), value)
|
||||
.SetName(nameof(OpportunityTypeWorkbook) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(OpportunityWorkbook), value)
|
||||
.SetName(nameof(OpportunityWorkbook) + $" {(value ? "First" : "Count")}");
|
||||
yield return new TestCaseData(nameof(TmpGUDIDIdentifier), value)
|
||||
.SetName(nameof(TmpGUDIDIdentifier) + $" {(value ? "First" : "Count")}");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task QueryTest<T, TI>(TI queryInfo)
|
||||
where TI : class
|
||||
where T : class
|
||||
{
|
||||
var query = Query(queryInfo);
|
||||
if (string.IsNullOrEmpty(query)) { return; }
|
||||
try
|
||||
{
|
||||
var result = await SnowflakeDatabaseContext
|
||||
.QueryAsync<T>(query, nameof(QueryTest), CancellationToken.None);
|
||||
await Verify(result, verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName(nameof(result)));
|
||||
}
|
||||
catch (SnowflakeDbException dbex)
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string Query<T>(T queryInfo)
|
||||
where T : class
|
||||
=> "";
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task TearDown()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ((CentralDbContext)CentralDbContext).Database.EnsureDeletedAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.FeatureFlags.Client;
|
||||
using Strata.Schema.Client;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Claims;
|
||||
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.JazzIntegrationTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
internal class TestCCOpportunityDetailService : IntegrationTestBase
|
||||
{
|
||||
protected ICCOpportunityDetailService _ccOpportunityDetailService;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetupChargeCodeDetailService()
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim("database_guid", GmAutomationDatabase.ToString())
|
||||
};
|
||||
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
|
||||
var claimsPrincipalMock = Mock.Of<IClaimsPrincipalAccessor>();
|
||||
Mock.Get(claimsPrincipalMock)
|
||||
.Setup(x => x.GetCurrentClaimsPrincipal())
|
||||
.Returns(principal);
|
||||
|
||||
var featureFlagServiceMock = Mock.Of<IFeatureFlagServiceClient>();
|
||||
Mock.Get(featureFlagServiceMock)
|
||||
.Setup(x => x.IsEnabledAsync("is-decision-support-queries-enabled", It.IsAny<Guid>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
_ccOpportunityDetailService = new CCOpportunityDetailService(
|
||||
JazzConnBuilderFactory,
|
||||
Mock.Of<ISchemaServiceClient>(),
|
||||
SnowflakeDatabaseContext,
|
||||
featureFlagServiceMock,
|
||||
Mock.Of<ILogger<CCOpportunityDetailService>>(),
|
||||
claimsPrincipalMock);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OpportunityCases))]
|
||||
public async Task OpportunityDetailQueryTest_Returns_Data(bool isBaseline, CCOpportunityDataModel opportunity)
|
||||
{
|
||||
// arrange
|
||||
var configuration = CreateConfiguration();
|
||||
|
||||
// act
|
||||
var results = await _ccOpportunityDetailService.GetCCOpportunityDetailQueryResult(
|
||||
opportunity,
|
||||
configuration,
|
||||
CancellationToken.None,
|
||||
isBaseline: true);
|
||||
|
||||
// assert
|
||||
results.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OpportunityCases))]
|
||||
public async Task OpportunityDetailLevelCasesQueryTest_Returns_Data(bool isBaseline, CCOpportunityDataModel opportunity)
|
||||
{
|
||||
// arrange
|
||||
var configuration = CreateConfiguration();
|
||||
|
||||
// act
|
||||
var results = await _ccOpportunityDetailService.CCOpportunityLevelCasesQuery(
|
||||
opportunity,
|
||||
configuration,
|
||||
CancellationToken.None,
|
||||
isBaseline: true);
|
||||
|
||||
// assert
|
||||
results.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
private Configuration.Configuration CreateConfiguration()
|
||||
{
|
||||
return new Configuration.Configuration
|
||||
{
|
||||
OrgDefinedEndDate = new DateTime(2024, 1, 1)
|
||||
};
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> OpportunityCases()
|
||||
{
|
||||
yield return new TestCaseData(true, GetSystemLevelData()).SetName("System");
|
||||
}
|
||||
|
||||
private static CCOpportunityDataModel GetSystemLevelData()
|
||||
{
|
||||
return new CCOpportunityDataModel
|
||||
{
|
||||
OpportunityLevel = OpportunityLevel.Organization.ToString(),
|
||||
ChargeCodeJson =
|
||||
@"{""HierarchyPaths"":[""CC|CCRU|Blood""],""Members"":[
|
||||
{""MemberGuid"":""4041e269-34c4-4a1d-bebf-c6e0a0799539"",""Name"":""10002765 - HB RBC LEUKOREDUCED EA UNIT"",""Id"":""3540""}]}",
|
||||
|
||||
ClinicalIndicatorJson =
|
||||
@"{""HierarchyPaths"":[""CLINICALINDICATOR|CLINICALINDI|188""],""Members"":[{""MemberGuid"":""d1e38321-5e37-4dad-9a54-ca8599763e7a"",""Name"":""All Readmissions"",""Id"":""188""}]}",
|
||||
|
||||
OpportunityLevelJson =
|
||||
@"{""HierarchyPaths"":[""SYS|SYSTEM""],
|
||||
""Members"":[{""MemberGuid"":""00000000-0000-0000-0000-000000000000"",
|
||||
""Id"":""1"",
|
||||
""Name"":""System""}]}"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities;
|
||||
using Strata.Schema.Client;
|
||||
using Strata.Schema.Client.Dtos;
|
||||
using Strata.Schema.Client.Dtos.Info;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.JazzIntegrationTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
internal class TestGetENOpportunityDetailQuery : IntegrationTestBase
|
||||
{
|
||||
[Test]
|
||||
public async Task TestGetENOpportunityDetailQuery_Returns_Data()
|
||||
{
|
||||
// arrange
|
||||
var config = CreateConfiguration();
|
||||
var opportunity = CreateOpportunity();
|
||||
var schemaService = new Mock<ISchemaServiceClient>().Object;
|
||||
Mock.Get(schemaService)
|
||||
.Setup(x => x.GetDimensionMembersFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<MemberDto> { new() { Id = "188", } });
|
||||
Mock.Get(schemaService)
|
||||
.Setup(x => x.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ScoreDimensionInfoDto { DimensionGuid = new Guid("3465fcd8-9149-4d96-8456-3ecaa3ffdadd") });
|
||||
var logger = Mock.Of<ILogger<ENOpportunityDetailJob>>();
|
||||
|
||||
// act
|
||||
var results = await JazzDbContext.GetENOpportunityDetailQuery(opportunity, config, schemaService, logger, GmAutomationDatabase);
|
||||
|
||||
// assert
|
||||
results.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
private ENOpportunityDataModel CreateOpportunity()
|
||||
{
|
||||
return new ENOpportunityDataModel()
|
||||
{
|
||||
EncounterOpportunityJSON = @"{
|
||||
""EncounterGroup"":1,""IsSeasonal"":false, ""CaseTypes"":[],
|
||||
""PatientPopulations"":[{""Text"":""All Readmissions"",""Id"":""CLINICALINDICATOR|CLINICALINDI|188""}],
|
||||
""OpportunityLevel"":""Organization"",
|
||||
""EncounterOpportunityLevel"":0,
|
||||
""Entities"":[],""ServiceLines"":[],""Specialties"":[],""Providers"":[],""IsBaseline"":true}"
|
||||
};
|
||||
}
|
||||
|
||||
private Configuration.Configuration CreateConfiguration()
|
||||
{
|
||||
return new Configuration.Configuration
|
||||
{
|
||||
OrgDefinedEndDate = new DateTime(2024, 1, 1)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Cube;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Jazz.Dimensions;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Reports;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Security;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Filter;
|
||||
using Strata.ContinuousImprovement.Biz.FreeForm.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.FreeForm.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.GeneralLedger.Details;
|
||||
using Strata.ContinuousImprovement.Biz.GeneralLedger.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.GMDN;
|
||||
using Strata.ContinuousImprovement.Biz.InitiativeRollupColumns;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Network;
|
||||
using Strata.ContinuousImprovement.Biz.Payroll.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.PeerGroups;
|
||||
using Strata.ContinuousImprovement.Biz.Population;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.Breakdowns;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.Events;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.Metrics;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Score;
|
||||
using Strata.ContinuousImprovement.Biz.Security;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.Biz.UserProfiles;
|
||||
using Strata.ContinuousImprovement.Biz.UserSetting;
|
||||
using Strata.ContinuousImprovement.Biz.UtilizationVariation;
|
||||
using Strata.ContinuousImprovement.Biz.UtilizationVariation.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.UtilizationVariation.VariationCaseTypes;
|
||||
using Strata.Stratasphere.Biz.DbContexts;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.JazzIntegrationTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
public partial class TestJazzDbContext : IntegrationTestBase
|
||||
{
|
||||
|
||||
public async Task TestGeneric<T>(TestContext testContext) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
var count = await ((DbContext)JazzDbContext).Set<T>().CountAsync(CancellationToken.None);
|
||||
Assert.That(count, Is.GreaterThanOrEqualTo(0));
|
||||
if (count == 0) { return; }
|
||||
var result = await ((DbContext)JazzDbContext).Set<T>().FirstOrDefaultAsync(CancellationToken.None);
|
||||
Assert.That(result, Is.Not.Null.And.InstanceOf<T>());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ILB
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task JazzDbTestCases()
|
||||
{
|
||||
var types = ((DbContext)JazzDbContext).Model.GetEntityTypes()
|
||||
.Select(e => e.ClrType).OrderBy(c => c.Name).ToList();
|
||||
foreach (var type in types)
|
||||
{
|
||||
switch (type.Name)
|
||||
{
|
||||
case nameof(AllOpportunity):
|
||||
TestGeneric<AllOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(CaseTypeFamily):
|
||||
TestGeneric<CaseTypeFamily>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(CciUserSetting):
|
||||
TestGeneric<CciUserSetting>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(CCOpportunity):
|
||||
TestGeneric<CCOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(CCOpportunityDataModel):
|
||||
TestGeneric<CCOpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(CCOpportunityDetailQueryResult):
|
||||
TestGeneric<CCOpportunityDetailQueryResult>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(CCOpportunityLevelCaseQueryResult):
|
||||
TestGeneric<CCOpportunityLevelCaseQueryResult>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ChargeCode.ChargeCode):
|
||||
TestGeneric<ChargeCode.ChargeCode>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ChargeCodeOpportunityDetail):
|
||||
TestGeneric<ChargeCodeOpportunityDetail>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ChargeCodeOpportunityLevelCase):
|
||||
TestGeneric<ChargeCodeOpportunityLevelCase>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Configuration.Configuration):
|
||||
TestGeneric<Configuration.Configuration>(TestContext.CurrentContext);
|
||||
break;␍
|
||||
case nameof(CostComponentRollup):
|
||||
TestGeneric<CostComponentRollup>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DboUserSetting):
|
||||
TestGeneric<DboUserSetting>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimCaseType):
|
||||
TestGeneric<DimCaseType>(TestContext.CurrentContext);
|
||||
break;␍
|
||||
case nameof(DimCostComponent):
|
||||
TestGeneric<DimCostComponent>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimDeviceIdentifier):
|
||||
TestGeneric<DimDeviceIdentifier>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimSupply):
|
||||
TestGeneric<DimSupply>(TestContext.CurrentContext);
|
||||
break;␍ case nameof(DimEntity):
|
||||
TestGeneric<DimEntity>(TestContext.CurrentContext);
|
||||
break;␍ case nameof(StringValue):
|
||||
TestGeneric<StringValue>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ENOpportunity):
|
||||
TestGeneric<ENOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ENOpportunityDataModel):
|
||||
TestGeneric<ENOpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ENOpportunityMetricData):
|
||||
TestGeneric<ENOpportunityMetricData>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(EXOpportunity):
|
||||
TestGeneric<EXOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FactGL):
|
||||
TestGeneric<FactGL>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FactGLSampled):
|
||||
TestGeneric<FactGLSampled>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FactStatistic):
|
||||
TestGeneric<FactStatistic>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FactStatisticSampledMonthly):
|
||||
TestGeneric<FactStatisticSampledMonthly>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FFFInitiativeTrackingData):
|
||||
TestGeneric<FFFInitiativeTrackingData>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FFInitiativeTrackingData):
|
||||
TestGeneric<FFInitiativeTrackingData>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FFOpportunity):
|
||||
TestGeneric<FFOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(FFOpportunityDataModel):
|
||||
TestGeneric<FFOpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Folder):
|
||||
TestGeneric<Folder>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GLOpportunity):
|
||||
TestGeneric<GLOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(GLOpportunityDataModel):
|
||||
TestGeneric<GLOpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Identity):
|
||||
TestGeneric<Identity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Initiative):
|
||||
TestGeneric<Initiative>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(InitiativeComment):
|
||||
TestGeneric<InitiativeComment>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(InitiativeCriteria):
|
||||
TestGeneric<InitiativeCriteria>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(InitiativeCriteriaQueryResult):
|
||||
TestGeneric<InitiativeCriteriaQueryResult>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(InitiativeRollupColumn):
|
||||
TestGeneric<InitiativeRollupColumn>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(InitiativeRollupValue):
|
||||
TestGeneric<InitiativeRollupValue>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(InitiativeScore):
|
||||
TestGeneric<InitiativeScore>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(Lock):
|
||||
// Lock is inaccessible due to its protection level
|
||||
break;
|
||||
case nameof(NetworkOpportunityLink):
|
||||
TestGeneric<NetworkOpportunityLink>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityMetricCohortData):
|
||||
TestGeneric<OpportunityMetricCohortData>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(OpportunityReport):
|
||||
TestGeneric<OpportunityReport>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(PeerGroup):
|
||||
TestGeneric<PeerGroup>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(PeerGroupCriteriaMapping):
|
||||
TestGeneric<PeerGroupCriteriaMapping>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(PROpportunity):
|
||||
TestGeneric<PROpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(PROpportunityDataModel):
|
||||
TestGeneric<PROpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(QVIBreakdown):
|
||||
TestGeneric<QVIBreakdown>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(QVIEvent):
|
||||
TestGeneric<QVIEvent>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(QVIOpportunity):
|
||||
TestGeneric<QVIOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(REReport):
|
||||
TestGeneric<REReport>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(RESQLReportRegisteredAttribute):
|
||||
TestGeneric<RESQLReportRegisteredAttribute>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(S3ACLSimple):
|
||||
TestGeneric<S3ACLSimple>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(S3SimplePermission):
|
||||
TestGeneric<S3SimplePermission>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(S3SimplePermissionDefault):
|
||||
TestGeneric<S3SimplePermissionDefault>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ScoreAttribute):
|
||||
TestGeneric<ScoreAttribute>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ScoreDimension):
|
||||
TestGeneric<ScoreDimension>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(SimplePermission):
|
||||
TestGeneric<SimplePermission>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(SourceOfSavingsReport):
|
||||
TestGeneric<SourceOfSavingsReport>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(SystemSetting):
|
||||
TestGeneric<SystemSetting>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(UserGroup):
|
||||
TestGeneric<UserGroup>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(UserProfile):
|
||||
TestGeneric<UserProfile>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(UVCaseType):
|
||||
TestGeneric<UVCaseType>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(UVOpportunity):
|
||||
TestGeneric<UVOpportunity>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(VariationOpportunityDataModel):
|
||||
TestGeneric<VariationOpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;␍
|
||||
case nameof(ExplorationVariationOpportunityDataModel):
|
||||
TestGeneric<ExplorationVariationOpportunityDataModel>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(ViewScoreAttribute):
|
||||
TestGeneric<ViewScoreAttribute>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimAprdrg):
|
||||
TestGeneric<DimAprdrg>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimCpt):
|
||||
TestGeneric<DimCpt>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimIcd10Dx):
|
||||
TestGeneric<DimIcd10Dx>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimIcd10Px):
|
||||
TestGeneric<DimIcd10Px>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimMsdrg):
|
||||
TestGeneric<DimMsdrg>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(DimMsdrgRollup):
|
||||
TestGeneric<DimMsdrgRollup>(TestContext.CurrentContext);
|
||||
break;
|
||||
case nameof(QviMetrics):
|
||||
TestGeneric<QviMetrics>(TestContext.CurrentContext);
|
||||
break;
|
||||
default:
|
||||
var pathArr = TestContext.CurrentContext.TestDirectory.Split(Path.DirectorySeparatorChar);
|
||||
var path = string.Join(Path.DirectorySeparatorChar, pathArr[..^3].Append("JazzIntegrationTests"));
|
||||
var fileName = Path.Combine(path, $"{nameof(TestJazzDbContext)}_{type.Name}.cs");
|
||||
if (File.Exists(fileName)) break;
|
||||
var testFile = @"
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.ContinuousImprovementServiceTests
|
||||
{
|
||||
[TestFixture, Category(""Integration""), Category(""CCI"")]
|
||||
public partial class TestJazzDbContext : IntegrationTestBase
|
||||
{
|
||||
[Test]
|
||||
public async Task Test" + type.Name + @"()
|
||||
{
|
||||
TestGeneric<" + type.Name + @">(TestContext.CurrentContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
File.WriteAllText(fileName, testFile);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
await Verify(types, verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Strata.ApiLib.Core.Cors.Extensions;
|
||||
using Strata.ApiLib.Core.StrataAuthentication.Bootstrappers;
|
||||
using Strata.Configuration.Client.DependencyInjection.ConsoleApps;
|
||||
using Strata.ContinuousImprovement.Biz.Configurations;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static class ServiceCollectionFactory
|
||||
{
|
||||
private static readonly Guid DevMaster = new("54409f55-5ddf-4748-8222-9ccfb04fb4f5");
|
||||
private static readonly Guid SeleniumAutomationDatabase = new("6bcd9090-3446-4cc1-b1aa-38afae76a1c6");
|
||||
private static readonly Guid SeleniumAutomationRefreshed4Master = new("feb7a099-8b2a-436c-9aba-0f853c8c60c9");
|
||||
private static readonly string IntegrationTesting = $"ci_{DateTime.Now:yyyyMMdd}_{Guid.NewGuid():N}";
|
||||
|
||||
public static IServiceCollection Create()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var jazzDbGuid = Debugger.IsAttached ? SeleniumAutomationRefreshed4Master : SeleniumAutomationDatabase;
|
||||
try
|
||||
{
|
||||
var configuration = StrataConfigurationFactory.GetConfiguration();
|
||||
services.AddSingleton(configuration);
|
||||
services.AddStrataAuthentication(configuration);
|
||||
services.AddStrataCors(configuration);
|
||||
services.AddContinuousImprovementServices(configuration, IntegrationTesting);
|
||||
services.AddAsyncDbContextFactory<JazzDbContext>(options =>
|
||||
{
|
||||
options
|
||||
.WithConnectionString((provider, token) => provider.GetConnectionStringFromSmc(jazzDbGuid, token))
|
||||
.WithDbContextOptions((connectionString, provider, builder) =>
|
||||
builder.UseSqlServer(connectionString, options => { options.CommandTimeout(300); }));
|
||||
});
|
||||
services.AddScoped<IStrategicOpportunityService, StrategicOpportunityService>();
|
||||
|
||||
services.AddLogging();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
+410
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+302
@@ -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)]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+193
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+207
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+457
@@ -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)),
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+411
@@ -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
|
||||
";
|
||||
}
|
||||
}
|
||||
+653
@@ -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
|
||||
|
||||
";
|
||||
}
|
||||
}
|
||||
+46
@@ -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>());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="OpportunityMetricCohortQueryTests\**" />
|
||||
<EmbeddedResource Remove="OpportunityMetricCohortQueryTests\**" />
|
||||
<None Remove="OpportunityMetricCohortQueryTests\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="SnowflakeIntegrationTests\DistributionProcess\TestData\**\*.csv">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="NUnit" Version="4.3.2" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.3.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
<PackageReference Include="Strata.ApiLib.Core" Version="8.4.1" />
|
||||
<PackageReference Include="Strata.Configuration.Client" Version="9.15.1" />
|
||||
<PackageReference Include="Strata.SnowflakeLib" Version="4.3.0" />
|
||||
<PackageReference Include="Strata.SqlTools.EntityFramework" Version="6.0.3" />
|
||||
<PackageReference Include="Verify.NUnit" Version="29.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Strata.ContinuousImprovement.Biz\Strata.ContinuousImprovement.Biz.csproj" />
|
||||
<ProjectReference Include="..\Strata.ContinuousImprovement.Biz.Test.Unit\Strata.ContinuousImprovement.Biz.Test.Unit.csproj" />
|
||||
<ProjectReference Include="..\Strata.ContinuousImprovement.JazzEntityFrameworkStub\Strata.ContinuousImprovement.JazzEntityFrameworkStub.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="NUnit.Framework" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="SnowflakeIntegrationTests\DistributionProcess\snapshots\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+533
@@ -0,0 +1,533 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
|
||||
using Strata.ContinuousImprovement.Biz.Utilities;
|
||||
using Strata.Id.Client;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration.StrategicItems
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture]
|
||||
public class StrategicItemDimensionSyncTests : IntegrationTestBase
|
||||
{
|
||||
private StrategicItemDimensionSyncService _syncService;
|
||||
private int _testStrataId;
|
||||
private List<long> _testOpportunityIds;
|
||||
private List<Guid> _testUserGuids;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task TestFixtureSetup()
|
||||
{
|
||||
await SetDbContextAndServices();
|
||||
|
||||
// Get real StrataId from the test database
|
||||
var idServiceClient = ServiceProvider.GetRequiredService<IIdServiceClient>();
|
||||
_testStrataId = await idServiceClient.GetStrataId(GmAutomationDatabase, CancellationToken.None);
|
||||
|
||||
_testOpportunityIds = new List<long>();
|
||||
_testUserGuids = new List<Guid>();
|
||||
|
||||
try
|
||||
{
|
||||
_testUserGuids = await CreateTestUserProfiles();
|
||||
|
||||
_syncService = new StrategicItemDimensionSyncService(
|
||||
ServiceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>(),
|
||||
(CentralDbContext)CentralDbContext,
|
||||
SnowflakeDatabaseContext
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If setup fails, clean up any partial data
|
||||
await CleanupTestUserProfiles();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public async Task TestCleanup()
|
||||
{
|
||||
// Clean up Snowflake dimension records
|
||||
foreach (var opportunityId in _testOpportunityIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
var deleteSql = "DELETE FROM cci.dimstrategicitem WHERE strategic_item_id = :StrategicItemId";
|
||||
await SnowflakeDatabaseContext.ExecuteCommandAsync(deleteSql, new { StrategicItemId = opportunityId });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up CentralDbContext records
|
||||
var opportunities = await ((CentralDbContext)CentralDbContext).StrategicOpportunities
|
||||
.Where(o => _testOpportunityIds.Contains(o.OpportunityId))
|
||||
.ToListAsync();
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunities.RemoveRange(opportunities);
|
||||
|
||||
var glOpportunities = await ((CentralDbContext)CentralDbContext).StrategicOpportunityGLs
|
||||
.Where(o => _testOpportunityIds.Contains(o.OpportunityId))
|
||||
.ToListAsync();
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityGLs.RemoveRange(glOpportunities);
|
||||
|
||||
// Clean up stakeholder links
|
||||
var stakeholders = await ((CentralDbContext)CentralDbContext).StakeholdersLinks
|
||||
.Where(s => _testOpportunityIds.Contains(s.StrategicOpportunityId))
|
||||
.ToListAsync();
|
||||
((CentralDbContext)CentralDbContext).StakeholdersLinks.RemoveRange(stakeholders);
|
||||
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
|
||||
_testOpportunityIds.Clear();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task TestFixtureTearDown()
|
||||
{
|
||||
await CleanupTestUserProfiles();
|
||||
|
||||
// Ensure base class cleanup runs to delete CentralDbContext database
|
||||
await base.TearDown();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_NewOpportunity_ShouldInsertDimensionRecord()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestOpportunity(_testUserGuids);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityChargeCodes.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var dimensionRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
|
||||
dimensionRecord.Should().NotBeNull();
|
||||
dimensionRecord.Name.Should().Be(opportunity.Name);
|
||||
dimensionRecord.StrategicItemType.Should().Be("Opportunity");
|
||||
dimensionRecord.OpportunityType.Should().Be("Charge Code");
|
||||
dimensionRecord.Status.Should().Be("New Opportunity");
|
||||
dimensionRecord.BaselineType.Should().Be(opportunity.BaselineType.ToString().SplitCamelCase());
|
||||
dimensionRecord.BaselineDistribution.Should().Be(opportunity.BaselineDistribution.ToString());
|
||||
dimensionRecord.RollupOneId.Should().Be(opportunity.Rollup1Id);
|
||||
dimensionRecord.RollupTwoId.Should().Be(opportunity.Rollup2Id);
|
||||
dimensionRecord.RollupThreeId.Should().Be(opportunity.Rollup3Id);
|
||||
dimensionRecord.BaselineStartDate.Should().Be(opportunity.BaselineStartDate);
|
||||
dimensionRecord.BaselineEndDate.Should().Be(opportunity.BaselineEndDate);
|
||||
dimensionRecord.EstimatedTrackingDuration.Should().Be(opportunity.EstimatedTrackingDuration);
|
||||
dimensionRecord.TeamMembers.Should().Be(opportunity.TeamMembers);
|
||||
dimensionRecord.Owner.Should().NotBeNullOrEmpty();
|
||||
dimensionRecord.CostLeader.Should().NotBeNullOrEmpty();
|
||||
dimensionRecord.ExecutiveSponsor.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_ExistingOpportunity_ShouldUpdateDimensionRecord()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestOpportunity(_testUserGuids);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityChargeCodes.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
// Insert initial dimension record
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Modify opportunity
|
||||
opportunity.Name = "Updated Test Opportunity";
|
||||
opportunity.Status = (byte)StrategicOpportunityStatus.InValidation;
|
||||
opportunity.EstimatedGoal = 50000;
|
||||
opportunity.TotalImprovement = 25000;
|
||||
opportunity.LastRunTrackingDate = DateTime.SpecifyKind(new DateTime(2024, 3, 15), DateTimeKind.Utc);
|
||||
opportunity.LastRunTrackingStatus = RunTrackingStatus.Success;
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var dimensionRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
|
||||
dimensionRecord.Should().NotBeNull();
|
||||
dimensionRecord.Name.Should().Be("Updated Test Opportunity");
|
||||
dimensionRecord.Status.Should().Be("In Validation");
|
||||
dimensionRecord.EstimatedGoal.Should().Be(50000);
|
||||
dimensionRecord.TotalImprovement.Should().Be(25000);
|
||||
dimensionRecord.LastRunTrackingDate.Should().Be(new DateOnly(2024, 3, 15));
|
||||
dimensionRecord.LastRunTrackingStatus.Should().Be(RunTrackingStatus.Success.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_WithStakeholders_ShouldSyncStakeholderNames()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestOpportunity(_testUserGuids);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityChargeCodes.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
// Add stakeholders
|
||||
var stakeholder1 = new StrategicStakeholdersLink(opportunity.OpportunityId, _testUserGuids[3]);
|
||||
var stakeholder2 = new StrategicStakeholdersLink(opportunity.OpportunityId, _testUserGuids[4]);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StakeholdersLinks.AddRange(stakeholder1, stakeholder2);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var dimensionRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
|
||||
dimensionRecord.Should().NotBeNull();
|
||||
dimensionRecord.Stakeholders.Should().NotBeNullOrEmpty();
|
||||
dimensionRecord.Stakeholders.Should().Contain("Stakeholder1, Test");
|
||||
dimensionRecord.Stakeholders.Should().Contain("Stakeholder2, Test");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_StatusChange_ShouldUpdateStrategicItemType()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestOpportunity(_testUserGuids);
|
||||
opportunity.Status = (byte)StrategicOpportunityStatus.NewOpportunity;
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityChargeCodes.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
var initialRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
initialRecord.StrategicItemType.Should().Be("Opportunity");
|
||||
|
||||
// Change to Initiative
|
||||
opportunity.Status = (byte)StrategicOpportunityStatus.NewInitiative;
|
||||
opportunity.TrackingStartDate = new DateOnly(2024, 1, 1);
|
||||
opportunity.TrackingEndDate = new DateOnly(2024, 12, 31);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var updatedRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
updatedRecord.StrategicItemType.Should().Be("Initiative");
|
||||
updatedRecord.Status.Should().Be("New Initiative");
|
||||
updatedRecord.TrackingStartDate.Should().Be(new DateOnly(2024, 1, 1));
|
||||
updatedRecord.TrackingEndDate.Should().Be(new DateOnly(2024, 12, 31));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_GLOpportunity_ShouldSetOpportunityTypeToGeneralLedger()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestGLOpportunity(_testUserGuids);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityGLs.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var dimensionRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
|
||||
dimensionRecord.Should().NotBeNull();
|
||||
dimensionRecord.OpportunityType.Should().Be("General Ledger");
|
||||
dimensionRecord.Name.Should().Be(opportunity.Name);
|
||||
dimensionRecord.StrategicItemType.Should().Be("Opportunity");
|
||||
dimensionRecord.Status.Should().Be("New Opportunity");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_GLOpportunityTrackingStatus_ShouldSyncLastRunTracking()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestGLOpportunity(_testUserGuids);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityGLs.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Simulate tracking run completing
|
||||
opportunity.LastRunTrackingDate = DateTime.SpecifyKind(new DateTime(2024, 5, 1), DateTimeKind.Utc);
|
||||
opportunity.LastRunTrackingStatus = RunTrackingStatus.Success;
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var dimensionRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
|
||||
dimensionRecord.Should().NotBeNull();
|
||||
dimensionRecord.LastRunTrackingDate.Should().Be(new DateOnly(2024, 5, 1));
|
||||
dimensionRecord.LastRunTrackingStatus.Should().Be(RunTrackingStatus.Success.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteStrategicItemDimension_ShouldRemoveDimensionRecord()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestOpportunity(_testUserGuids);
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityChargeCodes.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
var recordBeforeDelete = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
recordBeforeDelete.Should().NotBeNull();
|
||||
|
||||
// Act
|
||||
await _syncService.DeleteAsync(opportunity.OpportunityId);
|
||||
|
||||
// Assert
|
||||
var recordAfterDelete = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
recordAfterDelete.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpsertStrategicItemDimension_WithNullDates_ShouldHandleNullsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = CreateTestOpportunity(_testUserGuids);
|
||||
opportunity.TrackingStartDate = default;
|
||||
opportunity.TrackingEndDate = default;
|
||||
opportunity.LastRunTrackingDate = null;
|
||||
|
||||
((CentralDbContext)CentralDbContext).StrategicOpportunityChargeCodes.Add(opportunity);
|
||||
await ((CentralDbContext)CentralDbContext).SaveChangesAsync();
|
||||
_testOpportunityIds.Add(opportunity.OpportunityId);
|
||||
|
||||
// Act
|
||||
await _syncService.UpsertAsync(opportunity, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var dimensionRecord = await GetDimensionRecord(opportunity.OpportunityId);
|
||||
|
||||
dimensionRecord.Should().NotBeNull();
|
||||
dimensionRecord.TrackingStartDate.Should().BeNull();
|
||||
dimensionRecord.TrackingEndDate.Should().BeNull();
|
||||
dimensionRecord.LastRunTrackingDate.Should().BeNull();
|
||||
dimensionRecord.BaselineStartDate.Should().NotBeNull(); // Required field
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private async Task CleanupTestUserProfiles()
|
||||
{
|
||||
if (_testUserGuids == null || _testUserGuids.Count == 0) return;
|
||||
|
||||
var jazzContextFactory = ServiceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
await using var jazzContext = await jazzContextFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
|
||||
var guidParams = _testUserGuids
|
||||
.Select((g, i) => new Microsoft.Data.SqlClient.SqlParameter($"@g{i}", g))
|
||||
.ToArray();
|
||||
|
||||
var paramNames = string.Join(", ", guidParams.Select(p => p.ParameterName));
|
||||
await jazzContext.Database.ExecuteSqlRawAsync(
|
||||
$"DELETE FROM dbo.UserProfile WHERE UserGuid IN ({paramNames})",
|
||||
guidParams.Cast<object>().ToArray());
|
||||
}
|
||||
|
||||
private async Task<List<Guid>> CreateTestUserProfiles()
|
||||
{
|
||||
var runId = Guid.NewGuid().ToString("N")[..8];
|
||||
|
||||
var userGuids = new List<Guid>
|
||||
{
|
||||
Guid.NewGuid(), // Owner
|
||||
Guid.NewGuid(), // Cost Leader
|
||||
Guid.NewGuid(), // Executive Sponsor
|
||||
Guid.NewGuid(), // Stakeholder 1
|
||||
Guid.NewGuid() // Stakeholder 2
|
||||
};
|
||||
|
||||
var jazzContextFactory = ServiceProvider.GetRequiredService<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
await using var jazzContext = await jazzContextFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
|
||||
var insertSql = @"
|
||||
INSERT INTO dbo.UserProfile (UserGuid, UserName, NameFirst, NameLast)
|
||||
VALUES (@UserGuid, @UserName, @NameFirst, @NameLast)";
|
||||
|
||||
var profiles = new[]
|
||||
{
|
||||
new { UserGuid = userGuids[0], UserName = $"owner_{runId}", NameFirst = "Test", NameLast = "Owner" },
|
||||
new { UserGuid = userGuids[1], UserName = $"costleader_{runId}", NameFirst = "Test", NameLast = "CostLeader" },
|
||||
new { UserGuid = userGuids[2], UserName = $"executivesponsor_{runId}", NameFirst = "Test", NameLast = "ExecutiveSponsor" },
|
||||
new { UserGuid = userGuids[3], UserName = $"stakeholder1_{runId}", NameFirst = "Test", NameLast = "Stakeholder1" },
|
||||
new { UserGuid = userGuids[4], UserName = $"stakeholder2_{runId}", NameFirst = "Test", NameLast = "Stakeholder2" }
|
||||
};
|
||||
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
await jazzContext.Database.ExecuteSqlRawAsync(insertSql,
|
||||
new Microsoft.Data.SqlClient.SqlParameter("@UserGuid", profile.UserGuid),
|
||||
new Microsoft.Data.SqlClient.SqlParameter("@UserName", profile.UserName),
|
||||
new Microsoft.Data.SqlClient.SqlParameter("@NameFirst", profile.NameFirst),
|
||||
new Microsoft.Data.SqlClient.SqlParameter("@NameLast", profile.NameLast));
|
||||
}
|
||||
|
||||
return userGuids;
|
||||
}
|
||||
|
||||
private StrategicOpportunityChargeCode CreateTestOpportunity(List<Guid> userGuids)
|
||||
{
|
||||
return new StrategicOpportunityChargeCode
|
||||
{
|
||||
Name = $"Test Opportunity {Guid.NewGuid()}",
|
||||
StrataId = _testStrataId,
|
||||
OpportunityType = StrategicOpportunityTypes.ChargeCode,
|
||||
Status = (byte)StrategicOpportunityStatus.NewOpportunity,
|
||||
BaselineType = BaselineType.MonthRange,
|
||||
BaselineDistribution = DistributionMethod.Monthly,
|
||||
BaselineStartDate = new DateOnly(2024, 1, 1),
|
||||
BaselineEndDate = new DateOnly(2024, 6, 30),
|
||||
EstimatedTrackingDuration = 12,
|
||||
EstimatedGoal = 100000,
|
||||
TotalImprovement = 0,
|
||||
Rollup1Id = 1,
|
||||
Rollup2Id = 1,
|
||||
Rollup3Id = 1,
|
||||
OwnerGuid = userGuids[0],
|
||||
CostLeaderGuid = userGuids[1],
|
||||
ExecutiveSponsorGuid = userGuids[2],
|
||||
TeamMembers = "Team Member 1, Team Member 2",
|
||||
ChargeCodeHPath = "test/path",
|
||||
PatientPopulationHPath = string.Empty,
|
||||
OpportunityFiltersJSON = "[]",
|
||||
LayoutJSON = "{}",
|
||||
LastRunTrackingStatus = RunTrackingStatus.None
|
||||
};
|
||||
}
|
||||
|
||||
private StrategicOpportunityGL CreateTestGLOpportunity(List<Guid> userGuids)
|
||||
{
|
||||
return new StrategicOpportunityGL
|
||||
{
|
||||
Name = $"Test GL Opportunity {Guid.NewGuid()}",
|
||||
StrataId = _testStrataId,
|
||||
OpportunityType = StrategicOpportunityTypes.GeneralLedger,
|
||||
Status = (byte)StrategicOpportunityStatus.NewOpportunity,
|
||||
BaselineType = BaselineType.MonthRange,
|
||||
BaselineDistribution = DistributionMethod.Monthly,
|
||||
BaselineStartDate = new DateOnly(2024, 1, 1),
|
||||
BaselineEndDate = new DateOnly(2024, 6, 30),
|
||||
EstimatedTrackingDuration = 12,
|
||||
EstimatedGoal = 100000,
|
||||
TotalImprovement = 0,
|
||||
Rollup1Id = 1,
|
||||
Rollup2Id = 1,
|
||||
Rollup3Id = 1,
|
||||
OwnerGuid = userGuids[0],
|
||||
CostLeaderGuid = userGuids[1],
|
||||
ExecutiveSponsorGuid = userGuids[2],
|
||||
TeamMembers = "Team Member 1, Team Member 2",
|
||||
DepartmentHPath = string.Empty,
|
||||
OpportunityFiltersJSON = "[]",
|
||||
LayoutJSON = "{}",
|
||||
LastRunTrackingStatus = RunTrackingStatus.None
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DimensionRecord> GetDimensionRecord(long opportunityId)
|
||||
{
|
||||
var query = @"
|
||||
SELECT
|
||||
strategic_item_id AS StrategicItemId,
|
||||
name AS Name,
|
||||
rollup_one_id AS RollupOneId,
|
||||
rollup_two_id AS RollupTwoId,
|
||||
rollup_three_id AS RollupThreeId,
|
||||
baseline_type AS BaselineType,
|
||||
strategic_item_type AS StrategicItemType,
|
||||
opportunity_type AS OpportunityType,
|
||||
status AS Status,
|
||||
baseline_distribution AS BaselineDistribution,
|
||||
baseline_start_date AS BaselineStartDate,
|
||||
baseline_end_date AS BaselineEndDate,
|
||||
estimated_tracking_duration AS EstimatedTrackingDuration,
|
||||
tracking_start_date AS TrackingStartDate,
|
||||
tracking_end_date AS TrackingEndDate,
|
||||
estimated_goal AS EstimatedGoal,
|
||||
total_improvement AS TotalImprovement,
|
||||
cost_leader AS CostLeader,
|
||||
executive_sponsor AS ExecutiveSponsor,
|
||||
owner AS Owner,
|
||||
stakeholders AS Stakeholders,
|
||||
team_members AS TeamMembers,
|
||||
last_run_tracking_date AS LastRunTrackingDate,
|
||||
last_run_tracking_status AS LastRunTrackingStatus
|
||||
FROM cci.dimstrategicitem
|
||||
WHERE strategic_item_id = :StrategicItemId";
|
||||
|
||||
var parameters = new { StrategicItemId = opportunityId };
|
||||
|
||||
var results = await SnowflakeDatabaseContext.QueryAsync<DimensionRecord>(
|
||||
query,
|
||||
"GetDimensionRecord",
|
||||
parameters
|
||||
);
|
||||
|
||||
return results.FirstOrDefault();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Test DTO
|
||||
|
||||
private class DimensionRecord
|
||||
{
|
||||
public long StrategicItemId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int RollupOneId { get; set; }
|
||||
public int RollupTwoId { get; set; }
|
||||
public int RollupThreeId { get; set; }
|
||||
public string BaselineType { get; set; }
|
||||
public string StrategicItemType { get; set; }
|
||||
public string OpportunityType { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string BaselineDistribution { get; set; }
|
||||
public DateOnly? BaselineStartDate { get; set; }
|
||||
public DateOnly? BaselineEndDate { get; set; }
|
||||
public int EstimatedTrackingDuration { get; set; }
|
||||
public DateOnly? TrackingStartDate { get; set; }
|
||||
public DateOnly? TrackingEndDate { get; set; }
|
||||
public decimal EstimatedGoal { get; set; }
|
||||
public decimal TotalImprovement { get; set; }
|
||||
public string CostLeader { get; set; }
|
||||
public string ExecutiveSponsor { get; set; }
|
||||
public string Owner { get; set; }
|
||||
public string Stakeholders { get; set; }
|
||||
public string TeamMembers { get; set; }
|
||||
public DateOnly? LastRunTrackingDate { get; set; }
|
||||
public string LastRunTrackingStatus { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Integration
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Integration"), Category("CCI")]
|
||||
public class TestCIIntegration : IntegrationTestBase
|
||||
{
|
||||
[Test]
|
||||
public async Task VariationOpportunityTest()
|
||||
{
|
||||
var query = @"select OpportunityGuid from cci.VariationOpportunity where CostDriver = 'Pharmacy' order by Savings DESC limit 1";
|
||||
var opportunityGuid = await SnowflakeDatabaseContext.ExecuteScalarAsync<string>(query);
|
||||
VariationOpportunityGuid = new Guid(opportunityGuid ?? Guid.NewGuid().ToString());
|
||||
Assert.That(opportunityGuid, Is.Not.Null.And.EqualTo(VariationOpportunityGuid.ToString()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task LosVariationOpportunityTest()
|
||||
{
|
||||
var query = @"select OpportunityGuid from cci.VariationOpportunity where CostDriver = 'LOS' order by Savings DESC limit 1";
|
||||
var opportunityGuid = await SnowflakeDatabaseContext.ExecuteScalarAsync<string>(query);
|
||||
LOSOpportunityGuid = new Guid(opportunityGuid ?? Guid.NewGuid().ToString());
|
||||
Assert.That(opportunityGuid, Is.Not.Null.And.EqualTo(LOSOpportunityGuid.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"AllowedHosts": "*",
|
||||
"Version": "0.0.0",
|
||||
"configuration": {
|
||||
"basicAuthUsername": "BkQsRDdmdjkFNhKVuTI.TpujQJXuKhGDcV.F_hjADMRTaEUWMJnviaesoOrhhfnz",
|
||||
"basicAuthPassword": "GWPIIWECDqh.hjDNdpxVEfeFe-bYFwPSx-oBGFD_od_SUQVk-QqTIkZY_NXMeqeo"
|
||||
},
|
||||
"endPoints": {
|
||||
"dataschema": "https://dataschema-api.dev.stratanetwork.net/",
|
||||
"schema": "https://schema-api.dev.stratanetwork.net",
|
||||
"analytics": "https://analytics-api.dev.stratanetwork.net"
|
||||
},
|
||||
"StrataConfigServerBaseUrl": "https://configuration.dev.stratanetwork.net",
|
||||
"NetworkOpportunity": {
|
||||
"Creators": [ "" ]
|
||||
},
|
||||
"HealthCheck": {
|
||||
"StrataJazz": true,
|
||||
"Snowflake": true
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"hangfire": {
|
||||
"url": "https://localhost:44369",
|
||||
"redis": {
|
||||
"prefix": "continuousimprovement-jobs-service",
|
||||
"configOptions": "ddenredisc01.sdt.local:6379,ddenredisc02.sdt.local:6379,ddenredisc03.sdt.local:6379,ddenredisc04.sdt.local:6379,ddenredisc05.sdt.local:6379,ddenredisc06.sdt.local:6379"
|
||||
}
|
||||
},
|
||||
"accessGudid": {
|
||||
"url": "https://accessgudid.nlm.nih.gov/download/delimited"
|
||||
},
|
||||
"datadog": {
|
||||
"seedgmdnjob": "https://app.datadoghq.com/logs/livetail?query=service%3ASeedGMDNTermDataJob%20env%3Adev&cols=status%2Cenv%2C%40db.strata_id%2C%40db.org_pin%2C%40db.name%2C%40db.instance%2C%40process.name%2Cservice&index=%2A&messageDisplay=inline&stream_sort=desc&viz=stream&from_ts=1692207497147&to_ts=1692293897147&live=true"
|
||||
},
|
||||
"aws": {
|
||||
"snowflakeAdminSecretName": "stratareplication/snowflake/automation/connectionstring"
|
||||
},
|
||||
"strataApi": {
|
||||
"clientSecret": "V_okTrkWYBs-PqMP.inbJShIvMGWcpdqCDq_ZjntVF.ydgRCoYaDVJYEWEIdsjJ_"
|
||||
},
|
||||
"ChargeCodeOpportunityValidation": {
|
||||
"maxChargeCodeSelections": 100,
|
||||
"maxOpportunityLevelSelections": 10
|
||||
},
|
||||
"Snowflake": {
|
||||
"Url": "https://stratadev.us-east-1.privatelink.snowflakecomputing.com",
|
||||
"Account": "stratadev"
|
||||
}
|
||||
}
|
||||
+642
@@ -0,0 +1,642 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.All.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter;
|
||||
using Strata.ContinuousImprovement.Biz.Filter;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Pagination;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.Hangfire.Jazz.Client;
|
||||
using Strata.Hangfire.Jazz.Client.Models;
|
||||
using Strata.Schema.Client;
|
||||
using Strata.Schema.Client.Dtos;
|
||||
using Strata.Schema.Client.Dtos.Info;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.All.Opportunities
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class AllOpportunityServiceTest
|
||||
{
|
||||
private IAllOpportunityService _allOpportunityService;
|
||||
private Mock<IAsyncDbContextFactory<JazzDbContext>> _dbContextFactoryMock;
|
||||
private Mock<IJazzDbContext> _jazzDbContextMock;
|
||||
private Mock<ISchemaServiceClient> _schemaServiceMock;
|
||||
private Mock<IJazzHangfireServiceClient> _hangfireServiceMock;
|
||||
private Mock<IConfigurationService> _configurationMock;
|
||||
private IInitiativeService _initiativeService;
|
||||
private IUpdateFactOpportunitySavingsService _updateFactOpportunitySavingsService;
|
||||
private static readonly ResourceFileExtractor _extractor = new ResourceFileExtractor(".Resource.");
|
||||
private List<Biz.Configuration.Configuration> _configurations;
|
||||
internal static List<Guid> ConfigGuids = new() { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
|
||||
internal static List<string> ConfigNames = new() { "FY2021", "FY2022", "FY2023", "FY2024" };
|
||||
private ScoreDimensionInfoDto _dimension;
|
||||
private List<ScoreHierarchyDto> _hierarchies;
|
||||
private List<HierarchyNodeDto> _hierarchyNodes;
|
||||
private List<FilterHelper> _filterHelpers;
|
||||
private JobEnqueuedResponse _jobResponse;
|
||||
|
||||
[SetUp]
|
||||
public void TestSetUp()
|
||||
{
|
||||
_dbContextFactoryMock = new Mock<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
_jazzDbContextMock = new Mock<IJazzDbContext>();
|
||||
_schemaServiceMock = new Mock<ISchemaServiceClient>();
|
||||
_hangfireServiceMock = new Mock<IJazzHangfireServiceClient>();
|
||||
_configurationMock = new Mock<IConfigurationService>();
|
||||
_initiativeService = Mock.Of<IInitiativeService>();
|
||||
_updateFactOpportunitySavingsService = Mock.Of<IUpdateFactOpportunitySavingsService>();
|
||||
Mock.Get(_initiativeService)
|
||||
.Setup(s => s.DeleteAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
Mock.Get(_updateFactOpportunitySavingsService)
|
||||
.Setup(s => s.UpdateFactOpportunitySavings(It.IsAny<CancellationToken>()));
|
||||
_allOpportunityService = new AllOpportunityService(_dbContextFactoryMock.Object, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
_configurations = new List<Biz.Configuration.Configuration>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[0],
|
||||
Name = ConfigNames.First(),
|
||||
DepartmentRollupGlobalId = string.Empty,
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
},
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[1],
|
||||
Name = ConfigNames[1],
|
||||
DepartmentRollupGlobalId = "Department",
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
},
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[2],
|
||||
Name = ConfigNames[2],
|
||||
DepartmentRollupGlobalId = string.Empty,
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
},
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[3],
|
||||
Name = ConfigNames.Last(),
|
||||
DepartmentRollupGlobalId = string.Empty,
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
}
|
||||
};
|
||||
_dimension = new ScoreDimensionInfoDto
|
||||
{
|
||||
FriendlyName = "test",
|
||||
DimensionGuid = Guid.NewGuid(),
|
||||
DefaultHierarchyGuid = Guid.NewGuid()
|
||||
};
|
||||
_hierarchies = new List<ScoreHierarchyDto>
|
||||
{
|
||||
new ScoreHierarchyDto
|
||||
{
|
||||
FriendlyName = "test",
|
||||
HierarchyGuid = _dimension.DefaultHierarchyGuid
|
||||
}
|
||||
};
|
||||
_hierarchyNodes = new List<HierarchyNodeDto>
|
||||
{
|
||||
new HierarchyNodeDto
|
||||
{
|
||||
Name = "Case Type",
|
||||
Path = "CaseType|CaseType|1"
|
||||
}
|
||||
};
|
||||
_jobResponse = new JobEnqueuedResponse
|
||||
{
|
||||
JobId = Guid.NewGuid()
|
||||
};
|
||||
_filterHelpers = new List<FilterHelper>
|
||||
{
|
||||
new FilterHelper
|
||||
{
|
||||
Text = "Case Type",
|
||||
Id = "CaseType|CaseType|1"
|
||||
}
|
||||
};
|
||||
|
||||
_hangfireServiceMock.Setup(moq =>
|
||||
moq.EnqueueJobAsync(It.IsAny<EnqueueJobDto>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_jobResponse));
|
||||
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == ConfigGuids[0]), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations[0]));
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == ConfigGuids[1]), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations[1]));
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == ConfigGuids[2]), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations[2]));
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == _configurations.Last().ConfigurationGuid), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations.Last()));
|
||||
|
||||
_schemaServiceMock.Setup(moq => moq.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_dimension));
|
||||
_schemaServiceMock.Setup(moq => moq.GetDimensionByIdAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_dimension));
|
||||
_schemaServiceMock.Setup(moq => moq.GetScoreHierarchiesByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_hierarchies.AsEnumerable()));
|
||||
_schemaServiceMock.Setup(moq => moq.GetHierarchyNodesByHierarchyGuidAsync(It.IsAny<Guid>(),
|
||||
It.IsAny<HierarchyNodeParametersDto>(), It.IsAny<List<ExtraFilterDto>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_hierarchyNodes.AsEnumerable()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestFirstPage_Sorted_Asc()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var allOpportunityService = new AllOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
var filters = new AllFilters()
|
||||
{
|
||||
OpportunityTypes = new List<string>(),
|
||||
IdentifiedSavings = 0,
|
||||
ViewVisible = true,
|
||||
ExcludeInitiatives = false,
|
||||
OpportunitySearch = "",
|
||||
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid
|
||||
};
|
||||
|
||||
var pagingOptions = new PagingOptions("IdentifiedSavings", SortOrder.Asc);
|
||||
|
||||
// act
|
||||
var taskResult = await allOpportunityService
|
||||
.GetPagedAsync(pagingOptions, filters, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
taskResult.Data.Should().NotBeNullOrEmpty()
|
||||
.And.AllBeOfType<AllOpportunity>()
|
||||
.And.HaveCountGreaterThan(1);
|
||||
taskResult.Data.First().IdentifiedSavings
|
||||
.Should().BeLessThan(taskResult.Data.Last().IdentifiedSavings);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestFirstPage_Sorted_Desc()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var allOpportunityService = new AllOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
var filters = new AllFilters()
|
||||
{
|
||||
OpportunityTypes = new List<string>(),
|
||||
IdentifiedSavings = 0,
|
||||
ViewVisible = true,
|
||||
ExcludeInitiatives = false,
|
||||
OpportunitySearch = "",
|
||||
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid
|
||||
};
|
||||
|
||||
var pagingOptions = new PagingOptions("IdentifiedSavings", SortOrder.Desc);
|
||||
|
||||
// act
|
||||
var taskResult = await allOpportunityService
|
||||
.GetPagedAsync(pagingOptions, filters, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
taskResult.Data.Should().NotBeNullOrEmpty()
|
||||
.And.AllBeOfType<AllOpportunity>()
|
||||
.And.HaveCountGreaterThan(1);
|
||||
taskResult.Data.First().IdentifiedSavings
|
||||
.Should().BeGreaterThan(taskResult.Data.Last().IdentifiedSavings);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetFilterData))]
|
||||
public void TestValidateFilters(AllFilters filters)
|
||||
{
|
||||
if (filters == null) return;
|
||||
if (filters.OpportunityTypes.Any())
|
||||
filters.OpportunityTypes.Should()
|
||||
.AllBeOfType<string>()
|
||||
.And.NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetExcelWorkbook()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opp = framework.ENOpportunities.First();
|
||||
var filters = new AllFilters()
|
||||
{
|
||||
ConfigurationGuid = framework.Configurations.First().ConfigurationGuid,
|
||||
OpportunityTypes = new[] {$"{(int)OpportunityTypes.Variation}",
|
||||
$"{(int)OpportunityTypes.QualityVariation}",
|
||||
$"{(int)OpportunityTypes.FreeForm}",
|
||||
$"{(int)OpportunityTypes.GeneralLedger}",
|
||||
$"{(int)OpportunityTypes.Payroll}",
|
||||
$"{(int)OpportunityTypes.Encounter}" },
|
||||
};
|
||||
var service = new AllOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var wb = await service.ExportAsync(new SortOptions(), filters, CancellationToken.None);
|
||||
#pragma warning disable S125 // Sections of code should not be commented out
|
||||
//wb.SaveAs(System.IO.Path.Combine(@"C:\Git\continuousimprovement\tests\Strata.ContinuousImprovement.Biz.Test.Unit\Resource", "AllExcelFilter.xlsx"));
|
||||
#pragma warning restore S125 // Sections of code should not be commented out
|
||||
|
||||
// assert
|
||||
wb.Worksheets.Should().HaveCount(1);
|
||||
var ws = wb.Worksheet(1);
|
||||
ws.Should().NotBeNull();
|
||||
|
||||
var expected = @"Examples.AllOpportunity.ExcelFilter.xlsx";
|
||||
using (var expectedStream = _extractor.ReadFileFromResourceToStream(expected))
|
||||
using (var actualStream = new MemoryStream())
|
||||
{
|
||||
wb.SaveAs(actualStream);
|
||||
actualStream.Compare(expectedStream, out var message).Should().BeTrue(message);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetExcelWorkbookNullFilter()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var service = new AllOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var wb = await service.ExportAsync(null, null, CancellationToken.None);
|
||||
#pragma warning disable S125 // Sections of code should not be commented out
|
||||
//wb.SaveAs(System.IO.Path.Combine(@"C:\Git\continuousimprovement\tests\Strata.ContinuousImprovement.Biz.Test.Unit\Resource", "AllExcelNullFilter.xlsx"));
|
||||
#pragma warning restore S125 // Sections of code should not be commented out
|
||||
|
||||
// assert
|
||||
wb.Worksheets.Should().HaveCount(1);
|
||||
var ws = wb.Worksheet(1);
|
||||
ws.Should().NotBeNull();
|
||||
|
||||
var expected = @"Examples.AllOpportunity.ExcelNullFilter.xlsx";
|
||||
using (var expectedStream = _extractor.ReadFileFromResourceToStream(expected))
|
||||
using (var actualStream = new MemoryStream())
|
||||
{
|
||||
wb.SaveAs(actualStream);
|
||||
actualStream.Compare(expectedStream, out var message).Should().BeTrue(message);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestGetFilterOptionsAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var opportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
var taskResult = await opportunityService
|
||||
.GetFilterOptionsAsync(new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345"), cancellationToken);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNull();
|
||||
return;
|
||||
}
|
||||
taskResult.Should()
|
||||
.BeOfType<AllFilterOptions>()
|
||||
.And.NotBeNull();
|
||||
taskResult.OpportunityTypes.Should().AllBeOfType<FilterMember>().And.NotBeNullOrEmpty()
|
||||
.And.NotContain(cd => string.IsNullOrEmpty(cd.Id))
|
||||
.And.NotContain(cd => string.IsNullOrEmpty(cd.Text));
|
||||
}
|
||||
catch (NotImplementedException nie)
|
||||
{
|
||||
nie.Should().BeOfType<NotImplementedException>();
|
||||
}
|
||||
catch (AssertionException ae)
|
||||
{
|
||||
ae.Should().BeOfType<AssertionException>();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Should().BeOfType<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test, Ignore("Handled by the other services")]
|
||||
public async Task TestSetHidden()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opportunities = framework.ENOpportunities;
|
||||
var opportunity = opportunities.First(vo => !vo.IsHidden);
|
||||
var isHidden = opportunity.IsHidden;
|
||||
var service = new AllOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
await service.SetHiddenAsync(opportunity.OpportunityGuid, !isHidden, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
var result = await service.GetById(opportunity.OpportunityGuid, CancellationToken.None);
|
||||
result.IsHidden.Should().Be(!isHidden);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetHiddenAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
try
|
||||
{
|
||||
var opportunities = allOpportunityService.GetAllAsync(cancellationToken).Result;
|
||||
var opportunity = opportunities.FirstOrDefault(o => o.IsHidden);
|
||||
if (opportunity == null) return;
|
||||
var isHidden = opportunity.IsHidden;
|
||||
|
||||
//act
|
||||
await allOpportunityService.SetHiddenAsync(opportunity.OpportunityGuid, !isHidden,
|
||||
|
||||
cancellationToken);
|
||||
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName)) return;
|
||||
|
||||
var result = allOpportunityService.GetById(opportunity.OpportunityGuid, cancellationToken).Result;
|
||||
|
||||
result.IsHidden.Should().Be(!isHidden);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
switch (e.GetType().Name)
|
||||
{
|
||||
case "AggregateException":
|
||||
case "OperationCanceledException":
|
||||
return;
|
||||
}
|
||||
e.Should().NotBeOfType<Exception>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetHiddenAsync_NoOpportunity(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var opportunity = jazzEntityFrameworkFactory.ENOpportunities.FirstOrDefault(o => o.IsHidden);
|
||||
if (opportunity == null) return;
|
||||
var isHidden = opportunity.IsHidden;
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
await allOpportunityService.SetHiddenAsync(Guid.Empty, !isHidden,
|
||||
cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Should().BeOfType<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetNoteAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
try
|
||||
{
|
||||
var opportunities = allOpportunityService.GetAllAsync(cancellationToken).Result;
|
||||
|
||||
var opportunity = opportunities.FirstOrDefault(o => o.Note == string.Empty);
|
||||
|
||||
if (opportunity == null) return;
|
||||
|
||||
var note = "new test note";
|
||||
// act
|
||||
await allOpportunityService.SetNoteAsync(opportunity.OpportunityGuid, note,
|
||||
CancellationToken.None);
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName)) return;
|
||||
var result = allOpportunityService.GetById(opportunity.OpportunityGuid, cancellationToken).Result;
|
||||
result.Note.Should().Be(note);
|
||||
|
||||
// assert
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
switch (e.GetType().Name)
|
||||
{
|
||||
case "AggregateException":
|
||||
case "OperationCanceledException":
|
||||
return;
|
||||
}
|
||||
e.Should().NotBeOfType<Exception>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test, Ignore("Handled by the other services")]
|
||||
public async Task TestSetNote()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opportunities = framework.ENOpportunities;
|
||||
var opportunity = opportunities.First(vo => string.IsNullOrEmpty(vo.Note));
|
||||
var note = opportunity.Note;
|
||||
var expected = "Now is the time for all good engineers to write great code!";
|
||||
var service = new AllOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
await service.SetNoteAsync(opportunity.OpportunityGuid, expected, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
var result = await service.GetById(opportunity.OpportunityGuid, CancellationToken.None);
|
||||
result.Note.Should().NotBeNullOrEmpty()
|
||||
.And.NotBe(note)
|
||||
.And.Be(expected);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetNoteAsync_NoOpportunity(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var opportunity = jazzEntityFrameworkFactory.ENOpportunities.FirstOrDefault(o => o.Note == string.Empty);
|
||||
if (opportunity == null) return;
|
||||
var note = "new test note";
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
await allOpportunityService.SetNoteAsync(Guid.Empty, note, cancellationToken);
|
||||
|
||||
// assert
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Should().BeOfType<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworks))]
|
||||
public async Task TestGetAllAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory)
|
||||
{
|
||||
// arrange
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var taskResult = await allOpportunityService.GetAllAsync(CancellationToken.None);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNullOrEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworks))]
|
||||
public async Task TestGetById(JazzEntityFrameworkFactory jazzEntityFrameworkFactory)
|
||||
{
|
||||
// arrange
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var tempResult = await allOpportunityService.GetAllAsync(CancellationToken.None);
|
||||
var taskResult =
|
||||
await allOpportunityService.GetById(tempResult.FirstOrDefault()?.OpportunityGuid ?? Guid.Empty, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNull()
|
||||
.And.BeOfType<AllOpportunity>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworks))]
|
||||
public async Task TestGetByIds(JazzEntityFrameworkFactory jazzEntityFrameworkFactory)
|
||||
{
|
||||
// arrange
|
||||
var allOpportunityService = new AllOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var tempResult = await allOpportunityService.GetAllAsync(CancellationToken.None);
|
||||
var ids = tempResult.Select(o => o.OpportunityGuid);
|
||||
var taskResult = await allOpportunityService.GetByIds(ids, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNullOrEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNullOrEmpty()
|
||||
.And.AllBeOfType<AllOpportunity>()
|
||||
.And.HaveCount(ids.Count());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TestTearDown()
|
||||
{
|
||||
// Method intentionally left empty.
|
||||
}
|
||||
|
||||
#region TestCaseData
|
||||
|
||||
public static IEnumerable<TestCaseData> GetFilterData()
|
||||
{
|
||||
yield return new TestCaseData(new AllFilters { ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("No Filters");
|
||||
yield return new TestCaseData(new AllFilters { ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("View Visible");
|
||||
yield return new TestCaseData(new AllFilters { ViewVisible = false, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("Not View Visible");
|
||||
yield return new TestCaseData(new AllFilters { IdentifiedSavings = 1999, ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName(
|
||||
"By Identified Savings");
|
||||
yield return new TestCaseData(new AllFilters { IdentifiedSavings = 999, ViewVisible = false, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName(
|
||||
"By Identified Savings Not ViewVisible");
|
||||
yield return new TestCaseData(new AllFilters { OpportunityTypes = new[] { "Entity" }, ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") })
|
||||
.SetName("By OpportunityTypes");
|
||||
yield return new TestCaseData(new AllFilters { OpportunityTypes = new[] { "Provider" }, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345"), ViewVisible = false })
|
||||
.SetName("By OpportunityTypes Not ViewVisible");
|
||||
yield return new TestCaseData(new AllFilters { OpportunitySearch = "EN - 101", ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("Search By Id");
|
||||
yield return new TestCaseData(new AllFilters { ExcludeInitiatives = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("Exclude Opportunities with Initiatives");
|
||||
}
|
||||
|
||||
//public static IEnumerable<TestCaseData> GetAllOpportunityDataModel()
|
||||
//{
|
||||
// return from configGuid in ConfigGuids
|
||||
// from EncounterGroup encounterGroup in Enum.GetValues(typeof(EncounterGroup))
|
||||
// where encounterGroup != EncounterGroup.Both
|
||||
// from EncounterOpportunityLevel encounterOpportunityLevel in Enum.GetValues(typeof(EncounterOpportunityLevel))
|
||||
// from encounterGroups in new[] { true, false }
|
||||
// from OpportunityTypes in new[] { true, false }
|
||||
// select new TestCaseData(new AllOpportunityDataModel
|
||||
// {
|
||||
// ConfigurationGuid = configGuid,
|
||||
// EncounterGroup = (int)encounterGroup,
|
||||
// EncounterOpportunityLevel = encounterOpportunityLevel,
|
||||
// HasAllEncounterGroups = encounterGroups,
|
||||
// CaseTypes = encounterGroup == EncounterGroup.CaseType
|
||||
// ? new List<FilterHelper>
|
||||
// {
|
||||
// new() {Text = $"{encounterGroup}", Id = "Id"}
|
||||
// }
|
||||
// : new List<FilterHelper>(),
|
||||
// PatientPopulations = encounterGroup == EncounterGroup.PatientPopulation
|
||||
// ? new List<FilterHelper>
|
||||
// {
|
||||
// new () {Text = $"{encounterGroup}", Id = "Id"}
|
||||
// }
|
||||
// : new List<FilterHelper>(),
|
||||
// HasAllOpportunityTypes = OpportunityTypes,
|
||||
// Entities = encounterOpportunityLevel == EncounterOpportunityLevel.Entity
|
||||
// ? new List<FilterHelper>
|
||||
// {
|
||||
// new () {Text = $"{encounterOpportunityLevel}", Id = "Id"}
|
||||
// }
|
||||
// : new List<FilterHelper>(),
|
||||
// ServiceLines = encounterOpportunityLevel == EncounterOpportunityLevel.ServiceLine
|
||||
// ? new List<FilterHelper>
|
||||
// {
|
||||
// new () {Text = $"{encounterOpportunityLevel}", Id = "Id"}
|
||||
// }
|
||||
// : new List<FilterHelper>(),
|
||||
// Specialties = encounterOpportunityLevel == EncounterOpportunityLevel.Specialty
|
||||
// ? new List<FilterHelper>
|
||||
// {
|
||||
// new () {Text = $"{encounterOpportunityLevel}", Id = "Id"}
|
||||
// }
|
||||
// : new List<FilterHelper>(),
|
||||
// Providers = encounterOpportunityLevel == EncounterOpportunityLevel.Provider
|
||||
// ? new List<FilterHelper>
|
||||
// {
|
||||
// new () {Text = $"{encounterOpportunityLevel}", Id = "Id"}
|
||||
// }
|
||||
// : new List<FilterHelper>()
|
||||
// }).SetName($"{nameof(TestCreateAsync)} {ConfigNames[ConfigGuids.IndexOf(configGuid)]} IsUsingCustomEntityDim: {(ConfigGuids.IndexOf(configGuid) == 1)} IsUsingSg2: {(ConfigGuids.IndexOf(configGuid) == 3)} {encounterGroup} {encounterOpportunityLevel}");
|
||||
//}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Test.Unit.Biz.All
|
||||
{
|
||||
public static class OpportunityTypesExtensions
|
||||
{
|
||||
public static string OpportunityTypeName(this OpportunityTypes opportunityType)
|
||||
{
|
||||
switch (opportunityType)
|
||||
{
|
||||
case OpportunityTypes.Variation:
|
||||
return "Utilization Variation";
|
||||
case OpportunityTypes.StaffingToDemand:
|
||||
return "Staffing To Demand";
|
||||
case OpportunityTypes.QualityVariation:
|
||||
return "Quality Variation";
|
||||
case OpportunityTypes.FreeForm:
|
||||
return "Free Form";
|
||||
case OpportunityTypes.GeneralLedger:
|
||||
return "General Ledger";
|
||||
case OpportunityTypes.Payroll:
|
||||
return "Payroll";
|
||||
case OpportunityTypes.Encounter:
|
||||
return "Encounter";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.Schema.Client.Dtos;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.ChargeCode.Opportunities
|
||||
{
|
||||
[TestFixture()]
|
||||
public class CCOpportunityServiceTests : OpportunityServiceTests
|
||||
{
|
||||
public IJazzDbContext DbContext { get; set; }
|
||||
private IAsyncDbContextFactory<JazzDbContext> _dbContextFactory { get; set; }
|
||||
public ICCOpportunityService CCOpportunityService { get; set; }
|
||||
public IOptions<ChargeCodeOpportunityValidationSection> SectionAccessor { get; set; }
|
||||
public ILogger<CCOpportunityService> Logger { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void TestSetup()
|
||||
{
|
||||
var chargeCodeDimensionGuid = Guid.NewGuid();
|
||||
var caseTypeDimensionGuid = Guid.NewGuid();
|
||||
Mock.Get(SchemaServiceClient)
|
||||
.Setup(s => s.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<string, CancellationToken>((globalId, token) =>
|
||||
globalId switch
|
||||
{
|
||||
"Charge Code" => Task.FromResult(new Schema.Client.Dtos.Info.ScoreDimensionInfoDto { DimensionGuid = chargeCodeDimensionGuid }),
|
||||
"Case Type" => Task.FromResult(new Schema.Client.Dtos.Info.ScoreDimensionInfoDto { DimensionGuid = caseTypeDimensionGuid }),
|
||||
_ => Task.FromResult(new Schema.Client.Dtos.Info.ScoreDimensionInfoDto { DimensionGuid = Guid.NewGuid() })
|
||||
});
|
||||
Mock.Get(SchemaServiceClient)
|
||||
.Setup(x => x.GetHierarchyNodesFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, IEnumerable<string>, CancellationToken>((dimensionGuid, codeHpaths, token)
|
||||
=> Task.FromResult(new List<HierarchyNodeDto> {
|
||||
new HierarchyNodeDto{
|
||||
Path = "Path",
|
||||
PathDisplay = "PathDisplay",
|
||||
Name = "Name",
|
||||
Count = 10,
|
||||
IsLeaf= false
|
||||
}
|
||||
}.AsEnumerable()));
|
||||
Mock.Get(SchemaServiceClient)
|
||||
.Setup(x => x.GetDimensionMembersFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<Guid, IEnumerable<string>, CancellationToken>((dimensionGuid, codeHpaths, token)
|
||||
=> Task.FromResult(new List<MemberDto> {
|
||||
new MemberDto
|
||||
{
|
||||
Name ="Name",
|
||||
Id = "02"
|
||||
}
|
||||
}.AsEnumerable()));
|
||||
Mock.Get(SchemaServiceClient)
|
||||
.Setup(x => x.GetFilteredDimensionMembersByGlobalIdAsync<ChargeCodePickerModel>(It.IsAny<string>(), It.IsAny<List<int>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(new List<ChargeCodePickerModel> {
|
||||
new ChargeCodePickerModel(){
|
||||
ChargeCodeId = 99,
|
||||
Name = "TestName",
|
||||
CostDriver = "TestCostDriver",
|
||||
Rollup = "TestRollup"
|
||||
}
|
||||
}.AsEnumerable()));
|
||||
var principal = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var defaultUser = principal.GetCurrentClaimsPrincipal();
|
||||
Mock.Get(ClaimsPrincipalAccessor)
|
||||
.Setup(x => x.GetCurrentClaimsPrincipal())
|
||||
.Returns(defaultUser);
|
||||
|
||||
SectionAccessor = Mock.Of<IOptions<ChargeCodeOpportunityValidationSection>>();
|
||||
Mock.Get(SectionAccessor)
|
||||
.Setup(x => x.Value)
|
||||
.Returns(new ChargeCodeOpportunityValidationSection
|
||||
{
|
||||
MaxChargeCodeSelections = 10,
|
||||
MaxOpportunityLevelSelections = 10,
|
||||
});
|
||||
Logger = Mock.Of<ILogger<CCOpportunityService>>();
|
||||
|
||||
CCOpportunityService = new CCOpportunityService(DbContextFactory,
|
||||
null, // CentralDbContext,
|
||||
ClaimsPrincipalAccessor,
|
||||
ConfigurationService,
|
||||
AuthorizationServiceClient,
|
||||
IdServiceClient,
|
||||
FiscalMonthResolver,
|
||||
SchemaServiceClient,
|
||||
BackgroundJobClient,
|
||||
NotificationHubContext,
|
||||
SectionAccessor,
|
||||
InitiativeService,
|
||||
SnowflakeDatabaseContext,
|
||||
UpdateFactOpportunitySavingsService,
|
||||
Logger);
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task CCOpportunityServiceTest()
|
||||
{
|
||||
await Verifier.Verify(CCOpportunityService, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetDbContextAsyncTest()
|
||||
{
|
||||
var dbContext = await CCOpportunityService.GetDbContext(CancellationToken.None);
|
||||
var names = dbContext.AllOpportunities.Select(opp => $"All - {opp.Name} {opp.OpportunityId}").ToList();
|
||||
names.AddRange(dbContext.ChargeCodeOpportunities.Select(opp => opp.Name ?? ""));
|
||||
names.AddRange(dbContext.EncounterOpportunities.Select(opp => opp.Name ?? ""));
|
||||
names.AddRange(dbContext.FFOpportunities.Select(opp => opp.Name ?? ""));
|
||||
names.AddRange(dbContext.GeneralLedgerOpportunities.Select(opp => opp.Name ?? ""));
|
||||
names.AddRange(dbContext.Initiatives.Select(opp => $"Initiative - {opp.Name} {opp.OpportunityId}"));
|
||||
names.AddRange(dbContext.PayrollOpportunities.Select(opp => opp.Name ?? ""));
|
||||
names.AddRange(dbContext.VariationOpportunities.Where(opp => opp.OpportunityKey.StartsWith("UV"))
|
||||
.Select(opp => $"{opp.CostDriver ?? ""} {opp.ServiceLineName ?? ""} {opp.EntityName ?? ""} {opp.OpportunityId}"));
|
||||
await Verifier.Verify(new
|
||||
{
|
||||
names = names.Distinct().OrderBy(x => x),
|
||||
counts = new[] {
|
||||
dbContext.Configurations.Count(),
|
||||
dbContext.AllOpportunities.Count(opp => !opp.OpportunityKey.StartsWith("FE")),
|
||||
dbContext.ChargeCodeOpportunities.Count(),
|
||||
dbContext.EncounterOpportunities.Count(),
|
||||
dbContext.FFOpportunities.Count(),
|
||||
dbContext.GeneralLedgerOpportunities.Count(),
|
||||
dbContext.Initiatives.Count(),
|
||||
dbContext.PayrollOpportunities.Count(),
|
||||
dbContext.VariationOpportunities.Count(opp => opp.OpportunityKey.StartsWith("UV"))
|
||||
}
|
||||
}, VerifySettings)
|
||||
.IgnoreInstance<Guid>((x) => true)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetChargeCodeCustomPickerMembersAsyncTest()
|
||||
{
|
||||
var result = await CCOpportunityService.GetChargeCodePickerModelMembers(new List<string>
|
||||
{
|
||||
"CC 1"
|
||||
}, CancellationToken.None);
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task ValidateOpportunityLevelsAsyncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task CreateAsyncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test(), Ignore("Still working on this")]
|
||||
public async Task UpdateAsyncTest()
|
||||
{
|
||||
var dbGuid = JazzEntityFrameworkFactory.DatabaseGuid;
|
||||
var opportunities = JAZZ_Framework.ChargeCodeOpportunities;
|
||||
var opportunity = opportunities.FirstOrDefault();
|
||||
opportunity.Name += " Updated";
|
||||
|
||||
var result = await CCOpportunityService.UpdateAsync(dbGuid, opportunity, CancellationToken.None);
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.DontScrubGuids()
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetByIdTest()
|
||||
{
|
||||
var opportunities = JAZZ_Framework.CCOpportunities;
|
||||
var opportunity = opportunities.FirstOrDefault();
|
||||
|
||||
var result = await CCOpportunityService.GetById(opportunity.OpportunityGuid, CancellationToken.None);
|
||||
result.Configuration = null;
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.DontScrubGuids()
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task DeleteAsyncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task GetInitiativeInfoAsyncTest()
|
||||
{
|
||||
var opportunities = JAZZ_Framework.CCOpportunities;
|
||||
var opportunity = opportunities.FirstOrDefault();
|
||||
|
||||
var result = await CCOpportunityService.GetInitiativeInfoAsync(opportunity.OpportunityGuid, CancellationToken.None);
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.DontScrubGuids()
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task RefreshOpportunityTrackingDataTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test(), Ignore("Still working on this")]
|
||||
public async Task ExportAsyncTest()
|
||||
{
|
||||
var opportunities = JAZZ_Framework.CCOpportunities;
|
||||
var opportunity = opportunities.FirstOrDefault();
|
||||
|
||||
var export = await CCOpportunityService.ExportAsync(opportunity.OpportunityGuid, new string[] { "CC 1" }, "", CancellationToken.None);
|
||||
var result = export.GetDataTables();
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.DontScrubGuids()
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task UpdateGoalTotalUnitsOfServiceAsyncTest()
|
||||
{
|
||||
var opportunities = JAZZ_Framework.CCOpportunities;
|
||||
var opportunity = opportunities.FirstOrDefault();
|
||||
|
||||
var result = await CCOpportunityService.UpdateGoalTotalUnitsOfServiceAsync(opportunity.OpportunityGuid, "|CC 1|", 100, CancellationToken.None);
|
||||
//await Verifier.Verify(result, VerifySettings)
|
||||
// .DontScrubGuids()
|
||||
// .UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public async Task UpdateGoalCostPerUnitAsyncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
using VerifyTests;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.ChargeCode.Opportunities
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class OpportunityDetailLevelQueryTests
|
||||
{
|
||||
private VerifySettings _verifySettings;
|
||||
|
||||
[SetUp]
|
||||
public void TestSetup()
|
||||
{
|
||||
_verifySettings = TestExtensions.TestSettings();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OpportunityDetailLevelQueryTestCases))]
|
||||
public async Task OpportunityDetailLevelQueryTest(OpportunityLevel opportunityLevel, QueryHelper encounterQueryHelper,
|
||||
QueryHelper opportunityLevelQueryHelper)
|
||||
{
|
||||
var query = new OpportunityDetailLevelQuery(opportunityLevel, encounterQueryHelper, opportunityLevelQueryHelper);
|
||||
var result = query.GetQuery();
|
||||
await Verifier.Verify(result, _verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> OpportunityDetailLevelQueryTestCases()
|
||||
{
|
||||
var configuration = Mock.Of<Biz.Configuration.Configuration>();
|
||||
configuration.DepartmentRollupGlobalId = "";
|
||||
var sqlParameters = new SqlParameter[] {
|
||||
new SqlParameter("@chargeCodeIds", new List<int> { 1, 2, 3 }),
|
||||
new SqlParameter("@caseTypeIds", new List<int> { 1, 2, 3 }),
|
||||
new SqlParameter("@startDate", new DateTime(2023,1,1)),
|
||||
new SqlParameter("@endDate", new DateTime(2024,1,1)),
|
||||
new SqlParameter("@isBaseline", true)
|
||||
};
|
||||
|
||||
foreach (var parameterName in new[] { "@caseTypeIds", "@indicatorIds" })
|
||||
{
|
||||
sqlParameters[1].ParameterName = parameterName;
|
||||
foreach (var isUsingSnowflake in new[] { true, false })
|
||||
{
|
||||
foreach (var oppLevel in Enum.GetValues<OpportunityLevel>())
|
||||
{
|
||||
switch (oppLevel)
|
||||
{
|
||||
case OpportunityLevel.ServiceLine:
|
||||
foreach (var serviceLine in new[] { "Sg2 Service Line Crosswalk", "Service Line" })
|
||||
{
|
||||
yield return new TestCaseData(oppLevel,
|
||||
new EncounterGroupQueryHelper(sqlParameters),
|
||||
new OpportunityLevelQueryHelper(oppLevel, configuration, serviceLine, $"{(serviceLine.Replace(" ", ""))}Id"))
|
||||
.SetName($"{{m}} {Enum.GetName<OpportunityLevel>(oppLevel)} {parameterName.Substring(1)} {serviceLine} {(isUsingSnowflake ? "Snowflake" : "")}");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
yield return new TestCaseData(oppLevel,
|
||||
new EncounterGroupQueryHelper(sqlParameters),
|
||||
new OpportunityLevelQueryHelper(oppLevel, configuration, "", ""))
|
||||
.SetName($"{{m}} {Enum.GetName<OpportunityLevel>(oppLevel)} {parameterName.Substring(1)} Default {(isUsingSnowflake ? "Snowflake" : "")}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.ChargeCode.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Snowflake;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.Biz.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
using VerifyTests;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.ChargeCode.Opportunities
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class OpportunityDetailQueryTests
|
||||
{
|
||||
private VerifySettings _verifySettings;
|
||||
private Biz.Configuration.Configuration _configuration;
|
||||
|
||||
[SetUp]
|
||||
public void TestSetup()
|
||||
{
|
||||
_configuration = Mock.Of<Biz.Configuration.Configuration>();
|
||||
_verifySettings = TestExtensions.TestSettings();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OpportunityDetailQueryTestCases))]
|
||||
public async Task OpportunityDetailQueryTest(OpportunityLevel opportunityLevel, IEnumerable<SqlParameter> sqlParameters, QueryHelper encounterQueryHelper, QueryHelper opportunityLevelQueryHelper, bool isUsingSnowflake)
|
||||
{
|
||||
var query = new OpportunityDetailQuery(opportunityLevel, encounterQueryHelper, opportunityLevelQueryHelper, isUsingSnowflake);
|
||||
var result = isUsingSnowflake
|
||||
? query.GetQuery().ConvertSqlToSnowflakeParameterSyntax()
|
||||
: query.GetQuery().InterpolateQueryArrayValues(ref sqlParameters);
|
||||
await Verifier.Verify(result, _verifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> OpportunityDetailQueryTestCases()
|
||||
{
|
||||
var configuration = Mock.Of<Biz.Configuration.Configuration>();
|
||||
configuration.DepartmentRollupGlobalId = "";
|
||||
var sqlParameters = new SqlParameter[] {
|
||||
new SqlParameter("@chargeCodeIds", new List<int> { 1, 2, 3 }),
|
||||
new SqlParameter("@caseTypeIds", new List<int> { 7, 8, 9 }),
|
||||
new SqlParameter("@oppLevelIds", new List<int>{ 4, 5, 6 }),
|
||||
new SqlParameter("@startDate", new DateTime(2023,1,1)),
|
||||
new SqlParameter("@endDate", new DateTime(2024,1,1)),
|
||||
new SqlParameter("@isBaseline", true)
|
||||
};
|
||||
|
||||
foreach (var parameterName in new[] { "@caseTypeIds", "@indicatorIds" })
|
||||
{
|
||||
sqlParameters[1].ParameterName = parameterName;
|
||||
foreach (var isUsingSnowflake in new[] { true, false })
|
||||
{
|
||||
foreach (var oppLevel in Enum.GetValues<OpportunityLevel>())
|
||||
{
|
||||
switch (oppLevel)
|
||||
{
|
||||
case OpportunityLevel.ServiceLine:
|
||||
foreach (var serviceLine in new[] { "Sg2 Service Line Crosswalk", "Service Line" })
|
||||
{
|
||||
yield return new TestCaseData(oppLevel, sqlParameters,
|
||||
new EncounterGroupQueryHelper(sqlParameters),
|
||||
new OpportunityLevelQueryHelper(oppLevel, configuration, serviceLine, $"{(serviceLine.Replace(" ", ""))}Id"), isUsingSnowflake)
|
||||
.SetName($"{{m}} {Enum.GetName<OpportunityLevel>(oppLevel)} {parameterName.Substring(1)} {serviceLine} {(isUsingSnowflake ? "Snowflake" : "")}");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
yield return new TestCaseData(oppLevel, sqlParameters,
|
||||
new EncounterGroupQueryHelper(sqlParameters),
|
||||
new OpportunityLevelQueryHelper(oppLevel, configuration, "", ""), isUsingSnowflake)
|
||||
.SetName($"{{m}} {Enum.GetName<OpportunityLevel>(oppLevel)} {parameterName.Substring(1)} Default {(isUsingSnowflake ? "Snowflake" : "")}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.FiscalMonths;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.Configuration
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class ConfigurationServiceTest
|
||||
{
|
||||
|
||||
private Mock<IConfigurationService> _configurationServiceMock;
|
||||
private Mock<IAsyncDbContextFactory<JazzDbContext>> _dbContextFactoryMock;
|
||||
private Mock<IJazzDbContext> _jazzDbContextMock;
|
||||
private Mock<IFiscalMonthResolver> _fiscalMonthResolverMock;
|
||||
|
||||
[SetUp]
|
||||
public void TestSetUp()
|
||||
{
|
||||
_configurationServiceMock = new Mock<IConfigurationService>();
|
||||
_dbContextFactoryMock = new Mock<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
_jazzDbContextMock = new Mock<IJazzDbContext>();
|
||||
_fiscalMonthResolverMock = new Mock<IFiscalMonthResolver>();
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestGetDefaultConfigurationMock(JazzEntityFrameworkFactory framework, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var dfltConfiguration = framework.Configurations.OrderBy(c => c.DisplayOrder).First();
|
||||
_configurationServiceMock
|
||||
.Setup(vos => vos
|
||||
.GetDefaultConfigurationAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(dfltConfiguration);
|
||||
var logger = Mock.Of<ILogger<ConfigurationService>>();
|
||||
var _configurationService = new ConfigurationService(framework, null, null, logger, _fiscalMonthResolverMock.Object);
|
||||
try
|
||||
{
|
||||
// act
|
||||
var taskResult = await _configurationService.GetDefaultConfigurationAsync(cancellationToken);
|
||||
|
||||
// assert
|
||||
if (taskResult == null)
|
||||
{
|
||||
framework.JazzDbContext.Should().BeNull();
|
||||
return;
|
||||
}
|
||||
taskResult.Should().BeOfType<Biz.Configuration.Configuration>();
|
||||
taskResult.Name.Should().Be(dfltConfiguration.Name);
|
||||
taskResult.DisplayOrder.Should().Be(dfltConfiguration.DisplayOrder);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
cancellationToken.Should().NotBe(CancellationToken.None);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Intentionally left empty
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestGetDefaultConfiguration(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// arrange
|
||||
var logger = Mock.Of<ILogger<ConfigurationService>>();
|
||||
await jazzEntityFrameworkFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
var configurationService = new ConfigurationService(jazzEntityFrameworkFactory, null, null, logger, _fiscalMonthResolverMock.Object);
|
||||
|
||||
// act
|
||||
var taskResult = await configurationService.GetDefaultConfigurationAsync(cancellationToken);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNull()
|
||||
.And.BeOfType<Biz.Configuration.Configuration>();
|
||||
taskResult.QVIOpportunityBreakdownName.Should().NotBeNullOrEmpty();
|
||||
taskResult.QVIOpportunityBreakdownNamePlural.Should().NotBeNullOrEmpty();
|
||||
taskResult.QVIOpportunityBreakdownEnum.Should()
|
||||
.Be((int)taskResult.QVIOpportunityBreakdown);
|
||||
}
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
ae.Should().NotBeOfType<AggregateException>();
|
||||
}
|
||||
catch (InvalidOperationException ioe)
|
||||
{
|
||||
ioe.Should().NotBeOfType<InvalidOperationException>();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
cancellationToken.Should().NotBe(CancellationToken.None);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Intentionally left blank
|
||||
}
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TestTearDown()
|
||||
{
|
||||
// Method intentionally left empty.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.FiscalMonths;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using config = Strata.ContinuousImprovement.Biz.Configuration;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.Configuration
|
||||
{
|
||||
|
||||
[TestFixture, Category("Unit")]
|
||||
public class ConfigurationTest
|
||||
{
|
||||
private IEnumerable<OpportunityTypes> excludedOpportTypes = new List<OpportunityTypes>()
|
||||
{
|
||||
OpportunityTypes.Blank,
|
||||
OpportunityTypes.Network
|
||||
};
|
||||
private IEnumerable<OpportunityTypes> OpportTypes { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
OpportTypes = Enum.GetValues(typeof(OpportunityTypes))
|
||||
.Cast<OpportunityTypes>()
|
||||
.Where(x => !excludedOpportTypes.Contains(x));
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(ConfigTestCases))]
|
||||
public void TestConfiguration(string rollup, string ids, int breakdown,
|
||||
IEnumerable<int> configuredIds, string name, string plural)
|
||||
{
|
||||
var config = new config.Configuration
|
||||
{
|
||||
DepartmentRollupGlobalId = rollup,
|
||||
EntityIdsCSV = string.IsNullOrEmpty(rollup) ? "" : ids,
|
||||
CustomEntityIdsCSV = string.IsNullOrEmpty(rollup) ? ids : "",
|
||||
QVIOpportunityBreakdown = (Biz.Configuration.Configuration.QVIOpportunityBreakdowns)breakdown
|
||||
};
|
||||
config.ConfiguredEntityIds.Should().AllBeEquivalentTo(configuredIds);
|
||||
config.QVIOpportunityBreakdownEnum.Should().Be(breakdown);
|
||||
config.QVIOpportunityBreakdownName.Should().Be(name);
|
||||
config.QVIOpportunityBreakdownNamePlural.Should().Be(plural);
|
||||
config.ServiceLineDimensionGuid.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(StartEndDateTestCases))]
|
||||
public void TestGetBaselineStartDate(DateTime expectedStartDate, DateTime endDate)
|
||||
{
|
||||
var configuration = GetConfigurationByDate(endDate);
|
||||
var opportunityTypeIds = Enum.GetValues<OpportunityTypes>().Cast<byte>().ToList();
|
||||
foreach (var opType in OpportTypes.Where(t => opportunityTypeIds.Contains((byte)t)))
|
||||
{
|
||||
var actualStartDate = configuration.GetBaselineStartDate(opType);
|
||||
actualStartDate.Should().Be(expectedStartDate);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(StartEndDateTestCases))]
|
||||
public void TestGetLastFiscalYearStartDate(DateTime expectedStartDate, DateTime endDate)
|
||||
{
|
||||
var configuration = GetConfigurationByDate(endDate, true);
|
||||
|
||||
var actualStartDate = configuration.GetLastFiscalYearStartDate();
|
||||
expectedStartDate = expectedStartDate.AddYears(-1);
|
||||
actualStartDate.Should().Be(expectedStartDate);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(StartEndDateTestCases))]
|
||||
public void TestGetFiscalYearStartDate(DateTime expectedStartDate, DateTime endDate)
|
||||
{
|
||||
var configuration = GetConfigurationByDate(endDate, true);
|
||||
|
||||
var actualStartDate = configuration.GetFiscalYearStartDate();
|
||||
actualStartDate.Should().Be(expectedStartDate);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TrackingCurrentMonthEndDateTestCases))]
|
||||
public void TestTrackingCurrentMonthEndDate(
|
||||
OpportunityTypes opportunityType,
|
||||
DateTime trackingCurrentMonth,
|
||||
DateTime expectedEndDate)
|
||||
{
|
||||
var configuration = GetConfigurationWithTrackingMonth(trackingCurrentMonth, opportunityType);
|
||||
|
||||
var actualEndDate = configuration.TrackingCurrentMonthEndDate(opportunityType);
|
||||
|
||||
actualEndDate.Should().Be(expectedEndDate);
|
||||
actualEndDate.Hour.Should().Be(0);
|
||||
actualEndDate.Minute.Should().Be(0);
|
||||
actualEndDate.Second.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TrackingCurrentMonthEndDateTestCases))]
|
||||
public void TestGetTrackingCurrentMonthEndDate(
|
||||
OpportunityTypes opportunityType,
|
||||
DateTime trackingCurrentMonth,
|
||||
DateTime expectedEndDate)
|
||||
{
|
||||
var configuration = GetConfigurationWithTrackingMonth(trackingCurrentMonth, opportunityType);
|
||||
|
||||
var actualEndDate = configuration.GetTrackingCurrentMonthEndDate(opportunityType);
|
||||
|
||||
actualEndDate.Should().Be(expectedEndDate.AddDays(-1));
|
||||
}
|
||||
|
||||
#region Configuration Test Cases
|
||||
|
||||
private static IEnumerable<TestCaseData> ConfigTestCases()
|
||||
{
|
||||
yield return new TestCaseData("Rollup", "1,2,3,4", 4,
|
||||
new[] { 1, 2, 3, 4 }, "Breakdown", "Breakdowns")
|
||||
.SetName("Default With Rollup");
|
||||
yield return new TestCaseData("", "1,2,3,4", 4,
|
||||
new[] { 1, 2, 3, 4 }, "Breakdown", "Breakdowns")
|
||||
.SetName("Default w/o Rollup");
|
||||
yield return new TestCaseData("Rollup", "1,2,3,4",
|
||||
(int)config.Configuration.QVIOpportunityBreakdowns.Entity,
|
||||
new[] { 1, 2, 3, 4 }, "Entity", "Entities")
|
||||
.SetName("Entity With Rollup");
|
||||
yield return new TestCaseData("", "1,2,3,4",
|
||||
(int)config.Configuration.QVIOpportunityBreakdowns.Entity,
|
||||
new[] { 1, 2, 3, 4 }, "Entity", "Entities")
|
||||
.SetName("Entity w/o Rollup");
|
||||
yield return new TestCaseData("Rollup", "1,2,3,4",
|
||||
(int)config.Configuration.QVIOpportunityBreakdowns.ServiceLine,
|
||||
new[] { 1, 2, 3, 4 }, "Service Line", "Service Lines")
|
||||
.SetName("Service Line With Rollup");
|
||||
yield return new TestCaseData("", "1,2,3,4",
|
||||
(int)config.Configuration.QVIOpportunityBreakdowns.ServiceLine,
|
||||
new[] { 1, 2, 3, 4 }, "Service Line", "Service Lines")
|
||||
.SetName("Service Line w/o Rollup");
|
||||
yield return new TestCaseData("Rollup", "1,2,3,4",
|
||||
(int)config.Configuration.QVIOpportunityBreakdowns.PhysicianSpecialty,
|
||||
new[] { 1, 2, 3, 4 }, "Physician Specialty", "Physician Specialties")
|
||||
.SetName("Physician Specialty With Rollup");
|
||||
yield return new TestCaseData("", "1,2,3,4",
|
||||
(int)config.Configuration.QVIOpportunityBreakdowns.PhysicianSpecialty,
|
||||
new[] { 1, 2, 3, 4 }, "Physician Specialty", "Physician Specialties")
|
||||
.SetName("Physician Specialty w/o Rollup");
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> StartEndDateTestCases()
|
||||
{
|
||||
yield return new TestCaseData(new DateTime(2023, 3, 1), new DateTime(2024, 2, 29));
|
||||
yield return new TestCaseData(new DateTime(2024, 3, 1), new DateTime(2025, 2, 28));
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> TrackingCurrentMonthEndDateTestCases()
|
||||
{
|
||||
yield return new TestCaseData(
|
||||
OpportunityTypes.ChargeCode,
|
||||
new DateTime(2024, 3, 15),
|
||||
new DateTime(2024, 4, 1))
|
||||
.SetName("ChargeCode - March 2024");
|
||||
|
||||
yield return new TestCaseData(
|
||||
OpportunityTypes.QualityVariation,
|
||||
new DateTime(2024, 6, 20),
|
||||
new DateTime(2024, 7, 1))
|
||||
.SetName("QualityVariation - June 2024");
|
||||
|
||||
yield return new TestCaseData(
|
||||
OpportunityTypes.Variation,
|
||||
new DateTime(2024, 12, 5),
|
||||
new DateTime(2025, 1, 1))
|
||||
.SetName("Variation - December 2024");
|
||||
|
||||
yield return new TestCaseData(
|
||||
OpportunityTypes.OrgDefined,
|
||||
new DateTime(2024, 2, 10),
|
||||
new DateTime(2024, 3, 1))
|
||||
.SetName("OrgDefined - February 2024");
|
||||
|
||||
yield return new TestCaseData(
|
||||
OpportunityTypes.Exploration,
|
||||
new DateTime(2024, 1, 31),
|
||||
new DateTime(2024, 2, 1))
|
||||
.SetName("Exploration - January 2024");
|
||||
|
||||
yield return new TestCaseData(
|
||||
OpportunityTypes.ChargeCode,
|
||||
new DateTime(2024, 2, 29),
|
||||
new DateTime(2024, 3, 1))
|
||||
.SetName("ChargeCode - Leap Year February");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
|
||||
private static config.Configuration GetConfigurationByDate(DateTime date, bool isFiscalMonthResolver = false)
|
||||
{
|
||||
var configuration = new config.Configuration
|
||||
{
|
||||
FiscalYearID = (short)date.Year,
|
||||
OrgDefinedEndDate = date,
|
||||
VariationEndDate = date,
|
||||
QualityVariationEndDate = date
|
||||
};
|
||||
|
||||
if (isFiscalMonthResolver)
|
||||
{
|
||||
var lastFiscalMonth = new FiscalMonth
|
||||
{
|
||||
FiscalMonthId = (byte)date.Month
|
||||
};
|
||||
var fmr = Mock.Of<IFiscalMonthResolver>();
|
||||
Mock.Get(fmr)
|
||||
.Setup(s => s.LastFiscalMonth())
|
||||
.Returns(() => lastFiscalMonth);
|
||||
configuration.FiscalMonthResolver = fmr;
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private static config.Configuration GetConfigurationWithTrackingMonth(
|
||||
DateTime trackingCurrentMonth,
|
||||
OpportunityTypes opportunityType)
|
||||
{
|
||||
var configuration = new config.Configuration();
|
||||
|
||||
switch (opportunityType)
|
||||
{
|
||||
case OpportunityTypes.ChargeCode:
|
||||
configuration.OrgDefinedTrackingCurrentMonth = trackingCurrentMonth;
|
||||
break;
|
||||
case OpportunityTypes.QualityVariation:
|
||||
configuration.QualityVariationTrackingCurrentMonth = trackingCurrentMonth;
|
||||
break;
|
||||
default:
|
||||
configuration.VariationTrackingCurrentMonth = trackingCurrentMonth;
|
||||
break;
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.DbContexts
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static class DbSetExtensions
|
||||
{
|
||||
public static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
|
||||
{
|
||||
var queryable = sourceList.AsQueryable();
|
||||
var dbSet = new Mock<DbSet<T>>();
|
||||
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
|
||||
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
|
||||
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
|
||||
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
|
||||
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));
|
||||
return dbSet.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.FreeForm.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.QualityVariation.Opportunities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.DbContexts
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class JazzDbContextTests
|
||||
{
|
||||
private JazzDbContext _context;
|
||||
private DbContextOptions<JazzDbContext> _options;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetUp()
|
||||
{
|
||||
_options = new DbContextOptionsBuilder<JazzDbContext>()
|
||||
.UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}")
|
||||
.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning))
|
||||
.Options;
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_context = new JazzDbContext(_options);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_context?.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConfigureConventions_ShouldSetDecimalPrecision_ForAllDecimalProperties()
|
||||
{
|
||||
// Act
|
||||
var model = _context.Model;
|
||||
|
||||
// Assert - Get all decimal properties from all entities
|
||||
var decimalProperties = model.GetEntityTypes()
|
||||
.SelectMany(e => e.GetProperties())
|
||||
.Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?))
|
||||
.ToList();
|
||||
|
||||
// Verify that we have decimal properties to test
|
||||
decimalProperties.Should().NotBeEmpty("There should be decimal properties in the model to test");
|
||||
|
||||
// Check that all decimal properties have the expected precision and scale
|
||||
var foregroundColor = Console.ForegroundColor;
|
||||
foreach (var property in decimalProperties)
|
||||
{
|
||||
var className = ((IEntityType)property.DeclaringType).ClrType.Name;
|
||||
var propertyName = property.Name;
|
||||
var precision = property.GetPrecision();
|
||||
var scale = property.GetScale();
|
||||
|
||||
if (precision == null || scale == null)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine($"Property {className}.{propertyName} does not have precision/scale configured.");
|
||||
}
|
||||
else if (className == "QVIOpportunity" && propertyName == "QVIRate")
|
||||
{
|
||||
precision.Should().Be(19, $"Property {className}.{propertyName} should have precision 19");
|
||||
scale.Should().Be(4, $"Property {className}.{propertyName} should have scale 4");
|
||||
}
|
||||
else if (propertyName.StartsWith("Month"))
|
||||
{
|
||||
precision.Should().Be(15, $"Property {className}.{propertyName} should have precision 15");
|
||||
scale.Should().Be(4, $"Property {className}.{propertyName} should have scale 4");
|
||||
}
|
||||
else if (propertyName.StartsWith("QVI"))
|
||||
{
|
||||
precision.Should().Be(29, $"Property {className}.{propertyName} should have precision 29");
|
||||
scale.Should().Be(11, $"Property {className}.{propertyName} should have scale 11");
|
||||
}
|
||||
}
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConfigureConventions_ShouldNotAffectNonDecimalProperties()
|
||||
{
|
||||
// Act
|
||||
var model = _context.Model;
|
||||
|
||||
// Assert - Check that non-decimal properties are not affected
|
||||
var nonDecimalProperties = model.GetEntityTypes()
|
||||
.SelectMany(e => e.GetProperties())
|
||||
.Where(p => p.ClrType != typeof(decimal) && p.ClrType != typeof(decimal?))
|
||||
.ToList();
|
||||
|
||||
nonDecimalProperties.Should().NotBeEmpty("There should be non-decimal properties to verify they're not affected");
|
||||
|
||||
foreach (var property in nonDecimalProperties)
|
||||
{
|
||||
var precision = property.GetPrecision();
|
||||
var scale = property.GetScale();
|
||||
|
||||
if (property.ClrType == typeof(string))
|
||||
{
|
||||
precision.Should().BeNull($"String property {((IEntityType)property.DeclaringType).ClrType.Name}.{property.Name} should not have precision");
|
||||
scale.Should().BeNull($"String property {((IEntityType)property.DeclaringType).ClrType.Name}.{property.Name} should not have scale");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DatabaseCreation_ShouldSucceed_WithDecimalPrecisionConfiguration()
|
||||
{
|
||||
// Act & Assert
|
||||
var canConnect = await _context.Database.CanConnectAsync();
|
||||
canConnect.Should().BeTrue("Database should be accessible with decimal precision configuration");
|
||||
|
||||
await _context.Database.EnsureCreatedAsync();
|
||||
var created = await _context.Database.CanConnectAsync();
|
||||
created.Should().BeTrue("Database should be created successfully with decimal precision configuration");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelValidation_ShouldPass_WithDecimalPrecisionConfiguration()
|
||||
{
|
||||
// Arrange & Act
|
||||
Action validateModel = () =>
|
||||
{
|
||||
// Accessing the model is enough to trigger validation in EF Core.
|
||||
// No explicit FinalizeModel method exists on IModel.
|
||||
var _ = _context.Model.GetEntityTypes().ToList();
|
||||
};
|
||||
|
||||
// Assert
|
||||
validateModel.Should().NotThrow("Model should validate successfully with decimal precision configuration");
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> OpportunityTestCases()
|
||||
{
|
||||
yield return new TestCaseData(new QVIOpportunity
|
||||
{
|
||||
OpportunityGuid = Guid.NewGuid(),
|
||||
ConfigurationGuid = Guid.NewGuid(),
|
||||
IdentifiedSavings = (double)98765.432102m,
|
||||
QVIRate = (decimal)98765.432102d,
|
||||
OpportunityKey = "TEST-004"
|
||||
}, (double)98765.4321m, (decimal)98765.4321d);
|
||||
}
|
||||
|
||||
private void AddOpportunityToContext(Opportunity opportunity)
|
||||
{
|
||||
switch (opportunity)
|
||||
{
|
||||
case QVIOpportunity qviOpportunity:
|
||||
_context.QualityVariationItemOpportunities.Add(qviOpportunity);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported opportunity type: {opportunity.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<object> GetSavedOpportunityAsync(Opportunity opportunity)
|
||||
{
|
||||
return opportunity switch
|
||||
{
|
||||
QVIOpportunity qviOpportunity => await _context.QualityVariationItemOpportunities
|
||||
.FirstOrDefaultAsync(o => o.OpportunityGuid == qviOpportunity.OpportunityGuid),
|
||||
_ => throw new ArgumentException($"Unsupported opportunity type: {opportunity.GetType().Name}")
|
||||
};
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(OpportunityTestCases))]
|
||||
public async Task DecimalProperty_ShouldHandlePrecisionCorrectly_InDatabase(Opportunity testOpportunity, double expectedIdentifiedSavings, decimal? expectedQVIRate = null)
|
||||
{
|
||||
// This test verifies that the precision works in practice with the InMemory provider
|
||||
// Note: InMemory provider doesn't enforce precision/scale, but this tests the configuration doesn't break anything
|
||||
|
||||
// Arrange
|
||||
await _context.Database.EnsureCreatedAsync();
|
||||
|
||||
// Act
|
||||
if (testOpportunity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(testOpportunity), "Test opportunity cannot be null");
|
||||
}
|
||||
|
||||
AddOpportunityToContext(testOpportunity);
|
||||
|
||||
// Assert
|
||||
Func<Task> saveAction = async () => await _context.SaveChangesAsync();
|
||||
await saveAction.Should().NotThrowAsync("Saving with configured decimal precision should work");
|
||||
|
||||
var savedOpportunity = await GetSavedOpportunityAsync(testOpportunity);
|
||||
|
||||
savedOpportunity.Should().NotBeNull();
|
||||
const double ToleranceForDecimalComparison = 19d;
|
||||
|
||||
if (savedOpportunity is FFOpportunityDataModel ffOppTest)
|
||||
{
|
||||
ffOppTest.IdentifiedSavings.Should().BeApproximately(expectedIdentifiedSavings, ToleranceForDecimalComparison);
|
||||
}
|
||||
else
|
||||
{
|
||||
((Opportunity)savedOpportunity).IdentifiedSavings.Should().BeApproximately(expectedIdentifiedSavings, ToleranceForDecimalComparison);
|
||||
if (savedOpportunity is QVIOpportunity qviOppTest && expectedQVIRate.HasValue)
|
||||
{
|
||||
qviOppTest.QVIRate.Should().BeApproximately(expectedQVIRate.Value, (decimal)ToleranceForDecimalComparison);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseMethod_ShouldBeCalled_InConfigureConventions()
|
||||
{
|
||||
// This test ensures that base.ConfigureConventions() is still called
|
||||
// We can't directly test this, but we can verify that EF Core's base conventions are applied
|
||||
|
||||
var model = _context.Model;
|
||||
|
||||
// Verify that base EF Core conventions are still working
|
||||
var stringProperties = model.GetEntityTypes()
|
||||
.SelectMany(e => e.GetProperties())
|
||||
.Where(p => p.ClrType == typeof(string))
|
||||
.ToList();
|
||||
|
||||
stringProperties.Should().NotBeEmpty("Should have string properties to verify base conventions work");
|
||||
|
||||
// EF Core base conventions should still work for string properties
|
||||
foreach (var property in stringProperties.Take(5)) // Just check a few
|
||||
{
|
||||
// Base conventions should still be applied (this varies by property, so we just ensure no exceptions)
|
||||
Action getAnnotations = () => { var _ = property.GetAnnotations(); };
|
||||
getAnnotations.Should().NotThrow($"Base conventions should work for {((IEntityType)property.DeclaringType).ClrType.Name}.{property.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Mappers;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.Encounter
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class EncounterOpportunityMetricTest
|
||||
{
|
||||
private ENOpportunityDto opportunity;
|
||||
private ENOpportunityDataModelWithFilters model;
|
||||
|
||||
[SetUp]
|
||||
public void TestSetup()
|
||||
{
|
||||
opportunity = new ENOpportunityDto
|
||||
{
|
||||
ConfigurationGuid = Guid.NewGuid(),
|
||||
Name = "Test Encounter Opportunity",
|
||||
EncounterGroup = 0,
|
||||
OpportunityLevel = 1,
|
||||
EncounterGroups = new List<string>
|
||||
{
|
||||
"CaseType|CaseType|1"
|
||||
},
|
||||
OpportunityLevels = new List<string>()
|
||||
{
|
||||
"ENT|ENT|1"
|
||||
},
|
||||
IsBaseline = true
|
||||
};
|
||||
model = opportunity.MapToDataModel(null);
|
||||
model.OpportunityGuid = Guid.NewGuid();
|
||||
model.AuthorGuid = Guid.NewGuid();
|
||||
model.AuthorName = "Someones Name";
|
||||
model.DateCreated = DateTime.Today;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestENOpportunityDataModel()
|
||||
{
|
||||
var data = opportunity.MapToDataModel(null);
|
||||
data.ConfigurationGuid.Should().NotBe(Guid.Empty)
|
||||
.And.Be(opportunity.ConfigurationGuid);
|
||||
data.EncounterGroup.Should().Be(opportunity.EncounterGroup);
|
||||
data.OpportunityLevel.Should().Be(Enum.GetName(typeof(OpportunityLevel), opportunity.OpportunityLevel));
|
||||
data.EncounterOpportunityLevel.Should().Be((OpportunityLevel)opportunity.OpportunityLevel);
|
||||
data.CaseTypes.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.EncounterGroups.Select(x => x).Contains(ct.Id));
|
||||
data.IsSeasonal.Should().Be(opportunity.IsSeasonal);
|
||||
data.IsBaseline.Should().Be(opportunity.IsBaseline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEncounterOpportunityMetric()
|
||||
{
|
||||
var metric = model.MapFromDataModel();
|
||||
metric.OpportunityGuid.Should().Be(model.OpportunityGuid);
|
||||
metric.ConfigurationGuid.Should().Be(model.ConfigurationGuid);
|
||||
metric.AuthorName.Should().Be(model.AuthorName);
|
||||
metric.AuthorGuid.Should().Be(model.AuthorGuid);
|
||||
metric.Name.Should().Be(model.Name);
|
||||
metric.EncounterOpportunityLevel.Should().Be(model.EncounterOpportunityLevel);
|
||||
metric.EncounterGroup.Should().Be(model.EncounterGroup);
|
||||
metric.CaseTypes.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.EncounterGroups.Select(x => x).Contains(ct.Id));
|
||||
var filter = opportunity.EncounterGroups.First().MapToFilterHelper();
|
||||
metric.CaseTypes.First().GetHashCode().Equals(filter.GetHashCode());
|
||||
metric.PatientPopulations.Should().BeEmpty();
|
||||
metric.Entities.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.OpportunityLevels.Select(x => x).Contains(ct.Id));
|
||||
metric.ServiceLines.Should().BeEmpty();
|
||||
metric.Specialties.Should().BeEmpty();
|
||||
metric.Providers.Should().BeEmpty();
|
||||
metric.IsBaseline.Should().Be(opportunity.IsBaseline);
|
||||
metric.ConfigurationGuid.Should().NotBe(Guid.Empty);
|
||||
metric.IsSeasonal.Should().Be(opportunity.IsSeasonal);
|
||||
metric.IsBaseline.Should().Be(opportunity.IsBaseline);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetEncounterOpportunityMetricTests))]
|
||||
public void TestEncounterOpportunityMetric(ENOpportunityDto enOpportunity)
|
||||
{
|
||||
var dataModel = opportunity.MapToDataModel(null);
|
||||
var metric = dataModel.MapFromDataModel();
|
||||
metric.OpportunityGuid.Should().Be(dataModel.OpportunityGuid);
|
||||
metric.ConfigurationGuid.Should().Be(dataModel.ConfigurationGuid);
|
||||
metric.AuthorName.Should().Be(dataModel.AuthorName);
|
||||
metric.AuthorGuid.Should().Be(dataModel.AuthorGuid);
|
||||
metric.Name.Should().Be(dataModel.Name);
|
||||
metric.EncounterOpportunityLevel.Should().Be(dataModel.EncounterOpportunityLevel);
|
||||
metric.EncounterGroup.Should().Be(dataModel.EncounterGroup);
|
||||
switch ((EncounterGroup)metric.EncounterGroup)
|
||||
{
|
||||
case EncounterGroup.CaseType:
|
||||
metric.CaseTypes.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.EncounterGroups.Select(x => x).Contains(ct.Id));
|
||||
var filter = opportunity.EncounterGroups.First().MapToFilterHelper();
|
||||
metric.CaseTypes.First().GetHashCode().Equals(filter.GetHashCode());
|
||||
break;
|
||||
case EncounterGroup.PatientPopulation:
|
||||
metric.PatientPopulations.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.EncounterGroups.Select(x => x).Contains(ct.Id));
|
||||
var filter1 = opportunity.EncounterGroups.First().MapToFilterHelper();
|
||||
metric.PatientPopulations.First().GetHashCode().Equals(filter1.GetHashCode());
|
||||
break;
|
||||
}
|
||||
metric.PatientPopulations.Should().BeEmpty();
|
||||
switch (metric.EncounterOpportunityLevel)
|
||||
{
|
||||
case OpportunityLevel.Organization:
|
||||
metric.Entities.Should().BeEmpty();
|
||||
metric.ServiceLines.Should().BeEmpty();
|
||||
metric.Specialties.Should().BeEmpty();
|
||||
metric.Providers.Should().BeEmpty();
|
||||
break;
|
||||
case OpportunityLevel.Entity:
|
||||
metric.Entities.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.OpportunityLevels.Select(x => x).Contains(ct.Id));
|
||||
metric.ServiceLines.Should().BeEmpty();
|
||||
metric.Specialties.Should().BeEmpty();
|
||||
metric.Providers.Should().BeEmpty();
|
||||
break;
|
||||
case OpportunityLevel.ServiceLine:
|
||||
metric.ServiceLines.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.OpportunityLevels.Select(x => x).Contains(ct.Id));
|
||||
metric.Entities.Should().BeEmpty();
|
||||
metric.Specialties.Should().BeEmpty();
|
||||
metric.Providers.Should().BeEmpty();
|
||||
break;
|
||||
case OpportunityLevel.Specialty:
|
||||
metric.Specialties.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.OpportunityLevels.Select(x => x).Contains(ct.Id));
|
||||
metric.Entities.Should().BeEmpty();
|
||||
metric.ServiceLines.Should().BeEmpty();
|
||||
metric.Providers.Should().BeEmpty();
|
||||
break;
|
||||
case OpportunityLevel.Provider:
|
||||
metric.Providers.Should().NotBeEmpty()
|
||||
.And.Contain(ct => opportunity.OpportunityLevels.Select(x => x).Contains(ct.Id));
|
||||
metric.Entities.Should().BeEmpty();
|
||||
metric.ServiceLines.Should().BeEmpty();
|
||||
metric.Specialties.Should().BeEmpty();
|
||||
break;
|
||||
}
|
||||
metric.IsBaseline.Should().Be(opportunity.IsBaseline);
|
||||
metric.ConfigurationGuid.Should().NotBe(Guid.Empty);
|
||||
metric.IsSeasonal.Should().Be(opportunity.IsSeasonal);
|
||||
metric.IsBaseline.Should().Be(opportunity.IsBaseline);
|
||||
}
|
||||
public static IEnumerable<TestCaseData> GetEncounterOpportunityMetricTests()
|
||||
{
|
||||
foreach (EncounterGroup encounterGroup in Enum.GetValues(typeof(EncounterGroup)))
|
||||
{
|
||||
if (encounterGroup == EncounterGroup.Both) continue;
|
||||
foreach (OpportunityLevel oppLevel in Enum.GetValues(typeof(OpportunityLevel)))
|
||||
{
|
||||
yield return new TestCaseData(new ENOpportunityDto
|
||||
{
|
||||
ConfigurationGuid = Guid.NewGuid(),
|
||||
Name = "Test Encounter Opportunity",
|
||||
EncounterGroup = (int)encounterGroup,
|
||||
OpportunityLevel = (int)oppLevel,
|
||||
EncounterGroups = new List<string>
|
||||
{
|
||||
"CaseType|CaseType|1"
|
||||
},
|
||||
OpportunityLevels = new List<string>()
|
||||
{
|
||||
"ENT|ENT|1"
|
||||
},
|
||||
IsBaseline = true
|
||||
}).SetName($"TestEncounterOpportunityMetric {encounterGroup} {oppLevel}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+813
@@ -0,0 +1,813 @@
|
||||
using FluentAssertions;
|
||||
using Hangfire;
|
||||
using Hangfire.Common;
|
||||
using Hangfire.States;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.Authorization;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities;
|
||||
using Strata.ContinuousImprovement.Biz.Encounter.Opportunities.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Filter;
|
||||
using Strata.ContinuousImprovement.Biz.FiscalMonths;
|
||||
using Strata.ContinuousImprovement.Biz.Generics;
|
||||
using Strata.ContinuousImprovement.Biz.Initiatives;
|
||||
using Strata.ContinuousImprovement.Biz.Notification;
|
||||
using Strata.ContinuousImprovement.Biz.Pagination;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.Hangfire.Jazz.Client;
|
||||
using Strata.Hangfire.Jazz.Client.Models;
|
||||
using Strata.Id.Client;
|
||||
using Strata.Schema.Client;
|
||||
using Strata.Schema.Client.Dtos;
|
||||
using Strata.Schema.Client.Dtos.Info;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.Encounter.Opportunities
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class ENOpportunityServiceTests
|
||||
{
|
||||
|
||||
private IENOpportunityService _encounterOpportunityService;
|
||||
private Mock<IAsyncDbContextFactory<JazzDbContext>> _dbContextFactoryMock;
|
||||
private Mock<IJazzDbContext> _jazzDbContextMock;
|
||||
private Mock<ISchemaServiceClient> _schemaServiceMock;
|
||||
private Mock<IJazzHangfireServiceClient> _hangfireServiceMock;
|
||||
private Mock<IConfigurationService> _configurationMock;
|
||||
private Mock<IFiscalMonthResolver> _fiscalMonthResolverMock;
|
||||
private Mock<IBackgroundJobClient> _backgroundJobClient;
|
||||
private Mock<IIdServiceClient> _idServiceClient;
|
||||
private Mock<IAuthorizationServiceClient> _authorizationServiceClient;
|
||||
private Mock<IHubContext<NotificationHub, INotificationHub>> _notificationHubContext;
|
||||
private IInitiativeService _initiativeService;
|
||||
private IUpdateFactOpportunitySavingsService _updateFactOpportunitySavingsService;
|
||||
private static readonly ResourceFileExtractor _extractor = new ResourceFileExtractor(".Resource.");
|
||||
private List<Biz.Configuration.Configuration> _configurations;
|
||||
internal static List<Guid> ConfigGuids = new() { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
|
||||
internal static List<string> ConfigNames = new() { "FY2021", "FY2022", "FY2023", "FY2024" };
|
||||
private ScoreDimensionInfoDto _dimension;
|
||||
private List<ScoreHierarchyDto> _hierarchies;
|
||||
private List<HierarchyNodeDto> _hierarchyNodes;
|
||||
private List<FilterHelper> _filterHelpers;
|
||||
private JobEnqueuedResponse _jobResponse;
|
||||
|
||||
[SetUp]
|
||||
public void TestSetUp()
|
||||
{
|
||||
_dbContextFactoryMock = new Mock<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
_jazzDbContextMock = new Mock<IJazzDbContext>();
|
||||
_schemaServiceMock = new Mock<ISchemaServiceClient>();
|
||||
_hangfireServiceMock = new Mock<IJazzHangfireServiceClient>();
|
||||
_configurationMock = new Mock<IConfigurationService>();
|
||||
_fiscalMonthResolverMock = new Mock<IFiscalMonthResolver>();
|
||||
_updateFactOpportunitySavingsService = Mock.Of<IUpdateFactOpportunitySavingsService>();
|
||||
_initiativeService = Mock.Of<IInitiativeService>();
|
||||
_authorizationServiceClient = new Mock<IAuthorizationServiceClient>();
|
||||
_notificationHubContext = new Mock<IHubContext<NotificationHub, INotificationHub>>();
|
||||
_notificationHubContext.Setup(s => s.Clients)
|
||||
.Returns(new Mock<IHubClients<INotificationHub>>().Object);
|
||||
Mock.Get(_updateFactOpportunitySavingsService)
|
||||
.Setup(s => s.UpdateFactOpportunitySavings(It.IsAny<CancellationToken>()));
|
||||
Mock.Get(_initiativeService)
|
||||
.Setup(s => s.DeleteAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
_backgroundJobClient = new Mock<IBackgroundJobClient>();
|
||||
_idServiceClient = new Mock<IIdServiceClient>();
|
||||
_encounterOpportunityService = new ENOpportunityService(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
|
||||
_configurations = new List<Biz.Configuration.Configuration>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[0],
|
||||
Name = ConfigNames.First(),
|
||||
DepartmentRollupGlobalId = string.Empty,
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
},
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[1],
|
||||
Name = ConfigNames[1],
|
||||
DepartmentRollupGlobalId = "Department",
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
},
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[2],
|
||||
Name = ConfigNames[2],
|
||||
DepartmentRollupGlobalId = string.Empty,
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
},
|
||||
new()
|
||||
{
|
||||
ConfigurationGuid = ConfigGuids[3],
|
||||
Name = ConfigNames.Last(),
|
||||
DepartmentRollupGlobalId = string.Empty,
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
}
|
||||
};
|
||||
_dimension = new ScoreDimensionInfoDto
|
||||
{
|
||||
FriendlyName = "test",
|
||||
DimensionGuid = Guid.NewGuid(),
|
||||
DefaultHierarchyGuid = Guid.NewGuid()
|
||||
};
|
||||
_hierarchies = new List<ScoreHierarchyDto>
|
||||
{
|
||||
new ScoreHierarchyDto
|
||||
{
|
||||
FriendlyName = "test",
|
||||
HierarchyGuid = _dimension.DefaultHierarchyGuid
|
||||
}
|
||||
};
|
||||
_hierarchyNodes = new List<HierarchyNodeDto>
|
||||
{
|
||||
new HierarchyNodeDto
|
||||
{
|
||||
Name = "Case Type",
|
||||
Path = "CaseType|CaseType|1"
|
||||
}
|
||||
};
|
||||
_jobResponse = new JobEnqueuedResponse
|
||||
{
|
||||
JobId = Guid.NewGuid()
|
||||
};
|
||||
_filterHelpers = new List<FilterHelper>
|
||||
{
|
||||
new FilterHelper
|
||||
{
|
||||
Id = "CaseType|CaseType|1"
|
||||
}
|
||||
};
|
||||
|
||||
_hangfireServiceMock.Setup(moq =>
|
||||
moq.EnqueueJobAsync(It.IsAny<EnqueueJobDto>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_jobResponse));
|
||||
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == ConfigGuids[0]), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations[0]));
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == ConfigGuids[1]), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations[1]));
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == ConfigGuids[2]), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations[2]));
|
||||
_configurationMock.Setup(moq => moq.GetConfigurationAsync(It.Is<Guid>(c => c == _configurations.Last().ConfigurationGuid), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_configurations.Last()));
|
||||
|
||||
_schemaServiceMock.Setup(moq => moq.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_dimension));
|
||||
_schemaServiceMock.Setup(moq => moq.GetDimensionByIdAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_dimension));
|
||||
_schemaServiceMock.Setup(moq => moq.GetScoreHierarchiesByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_hierarchies.AsEnumerable()));
|
||||
_schemaServiceMock.Setup(moq => moq.GetHierarchyNodesByHierarchyGuidAsync(It.IsAny<Guid>(),
|
||||
It.IsAny<HierarchyNodeParametersDto>(), It.IsAny<List<ExtraFilterDto>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_hierarchyNodes.AsEnumerable()));
|
||||
_schemaServiceMock.Setup(moq => moq.GetHierarchyNodesFromHierarchyPathsAsync(It.IsAny<Guid>(),
|
||||
It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(_hierarchyNodes.AsEnumerable()));
|
||||
|
||||
_backgroundJobClient.Setup(moq => moq.Create(It.IsAny<Job>(), It.IsAny<IState>()))
|
||||
.Returns(Guid.NewGuid().ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestFirstPage_Sorted_Asc()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var encounterOpportunityService = new ENOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
var filters = new ENFilters()
|
||||
{
|
||||
Cases = 0,
|
||||
OpportunityLevels = new List<string>(),
|
||||
EncounterGroups = EncounterGroup.Both,
|
||||
Seasonality = SeasonalityType.WithAndWithout,
|
||||
IdentifiedSavings = 0,
|
||||
ViewVisible = true,
|
||||
ExcludeInitiatives = false,
|
||||
OpportunitySearch = "",
|
||||
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid
|
||||
};
|
||||
|
||||
var pagingOptions = new PagingOptions("IdentifiedSavings", SortOrder.Asc);
|
||||
|
||||
// act
|
||||
var taskResult = await encounterOpportunityService
|
||||
.GetPagedAsync(pagingOptions, filters, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
taskResult.Data.Should().NotBeNullOrEmpty()
|
||||
.And.AllBeOfType<ENOpportunity>()
|
||||
.And.HaveCountGreaterThan(1);
|
||||
taskResult.Data.First().IdentifiedSavings
|
||||
.Should().BeLessThan(taskResult.Data.Last().IdentifiedSavings);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestFirstPage_Sorted_Desc()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var encounterOpportunityService = new ENOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
var filters = new ENFilters()
|
||||
{
|
||||
Cases = 0,
|
||||
OpportunityLevels = new List<string>(),
|
||||
EncounterGroups = EncounterGroup.Both,
|
||||
Seasonality = SeasonalityType.WithAndWithout,
|
||||
IdentifiedSavings = 0,
|
||||
ViewVisible = true,
|
||||
ExcludeInitiatives = false,
|
||||
OpportunitySearch = "",
|
||||
|
||||
ConfigurationGuid = JazzEntityFrameworkFactory.CurrentConfigurationGuid
|
||||
};
|
||||
|
||||
var pagingOptions = new PagingOptions("IdentifiedSavings", SortOrder.Desc);
|
||||
|
||||
// act
|
||||
var taskResult = await encounterOpportunityService
|
||||
.GetPagedAsync(pagingOptions, filters, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
taskResult.Data.Should().NotBeNullOrEmpty()
|
||||
.And.AllBeOfType<ENOpportunity>()
|
||||
.And.HaveCountGreaterThan(1);
|
||||
taskResult.Data.First().IdentifiedSavings
|
||||
.Should().BeGreaterThan(taskResult.Data.Last().IdentifiedSavings);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetFilterData))]
|
||||
public void TestValidateFilters(ENFilters filters)
|
||||
{
|
||||
if (filters == null) return;
|
||||
if (filters.OpportunityLevels.Any())
|
||||
filters.OpportunityLevels.Should()
|
||||
.AllBeOfType<string>()
|
||||
.And.NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetExcelWorkbook()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opp = framework.ENOpportunities.First();
|
||||
var filters = new ENFilters()
|
||||
{
|
||||
ConfigurationGuid = framework.Configurations.First().ConfigurationGuid,
|
||||
OpportunityLevels = new[] { opp.OpportunityLevel },
|
||||
};
|
||||
var service = new ENOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var wb = await service.ExportAsync(new SortOptions(), filters, CancellationToken.None);
|
||||
#pragma warning disable S125 // Sections of code should not be commented out
|
||||
//wb.SaveAs(System.IO.Path.Combine(@"C:\Git\continuousimprovement\tests\Strata.ContinuousImprovement.Biz.Test.Unit\Resource", "ExcelFilter.xlsx"));
|
||||
#pragma warning restore S125 // Sections of code should not be commented out
|
||||
|
||||
// assert
|
||||
wb.Worksheets.Should().HaveCount(1);
|
||||
var ws = wb.Worksheet(1);
|
||||
ws.Should().NotBeNull();
|
||||
|
||||
var expected = @"Examples.EncounterOpportunity.ExcelFilter.xlsx";
|
||||
using (var expectedStream = _extractor.ReadFileFromResourceToStream(expected))
|
||||
using (var actualStream = new MemoryStream())
|
||||
{
|
||||
wb.SaveAs(actualStream);
|
||||
actualStream.Compare(expectedStream, out var message).Should().BeTrue(message);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetExcelWorkbookNullFilter()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var service = new ENOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var wb = await service.ExportAsync(null, null, CancellationToken.None);
|
||||
#pragma warning disable S125 // Sections of code should not be commented out
|
||||
//wb.SaveAs(System.IO.Path.Combine(@"C:\Git\continuousimprovement\tests\Strata.ContinuousImprovement.Biz.Test.Unit\Resource", "ExcelNullFilter.xlsx"));
|
||||
#pragma warning restore S125 // Sections of code should not be commented out
|
||||
|
||||
// assert
|
||||
wb.Worksheets.Should().HaveCount(1);
|
||||
var ws = wb.Worksheet(1);
|
||||
ws.Should().NotBeNull();
|
||||
|
||||
var expected = @"Examples.EncounterOpportunity.ExcelNullFilter.xlsx";
|
||||
using (var expectedStream = _extractor.ReadFileFromResourceToStream(expected))
|
||||
using (var actualStream = new MemoryStream())
|
||||
{
|
||||
wb.SaveAs(actualStream);
|
||||
actualStream.Compare(expectedStream, out var message).Should().BeTrue(message);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestCreateAsyncCaseTypesOrganization()
|
||||
{
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opportunity = new ENOpportunityDataModelWithFilters
|
||||
{
|
||||
EncounterGroup = 0,
|
||||
CaseTypes = new List<FilterHelper>
|
||||
{
|
||||
new FilterHelper
|
||||
{
|
||||
Id = "CaseType|CaseType|1"
|
||||
}
|
||||
},
|
||||
EncounterOpportunityLevel = OpportunityLevel.Organization
|
||||
};
|
||||
opportunity.ConfigurationGuid = framework.Configurations.FirstOrDefault().ConfigurationGuid;
|
||||
var service = new ENOpportunityService(framework, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
|
||||
var results = await service.CreateAsync(opportunity.MapToDataModel(null), Guid.Empty, true, CancellationToken.None);
|
||||
results.Should().NotBe(Guid.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestCreateAsyncAllCaseTypesOrganization()
|
||||
{
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opportunity = new ENOpportunityDataModelWithFilters
|
||||
{
|
||||
EncounterGroup = 0,
|
||||
EncounterOpportunityLevel = OpportunityLevel.Organization
|
||||
};
|
||||
opportunity.ConfigurationGuid = framework.Configurations.FirstOrDefault().ConfigurationGuid;
|
||||
var service = new ENOpportunityService(framework, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var results = await service.CreateAsync(opportunity.MapToDataModel(null), Guid.Empty, true, CancellationToken.None);
|
||||
results.Should().NotBe(Guid.Empty);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetENOpportunityDataModel))]
|
||||
public async Task TestCreateAsync(ENOpportunityDataModelWithFilters opportunity)
|
||||
{
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
opportunity.ConfigurationGuid = framework.Configurations.FirstOrDefault().ConfigurationGuid;
|
||||
var service = new ENOpportunityService(framework, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var results = await service.CreateAsync(opportunity.MapToDataModel(null), Guid.Empty, true, CancellationToken.None);
|
||||
results.Should().NotBe(Guid.Empty);
|
||||
}
|
||||
|
||||
[Test, Ignore("Problem moq'ing GetEntityNodes")]
|
||||
public async Task TestCreateAsyncAllCaseTypesAllEntities()
|
||||
{
|
||||
var serviceMock = new Mock<ENOpportunityService>(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var service = serviceMock.Object;
|
||||
var opportunity = new ENOpportunityDataModelWithFilters
|
||||
{
|
||||
EncounterGroup = 0,
|
||||
EncounterOpportunityLevel = OpportunityLevel.Entity
|
||||
};
|
||||
var results = await service.CreateAsync(opportunity.MapToDataModel(null), Guid.Empty, true, CancellationToken.None);
|
||||
results.Should().NotBe(Guid.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetEntityNodes()
|
||||
{
|
||||
var service = new ENOpportunityService(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var configuration = new Biz.Configuration.Configuration
|
||||
{
|
||||
DepartmentRollupGlobalId = ""
|
||||
};
|
||||
|
||||
var results = await service.GetEntityNodes(configuration, CancellationToken.None);
|
||||
results.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetServiceLineNodesBySg2()
|
||||
{
|
||||
var service = new ENOpportunityService(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var configuration = new Biz.Configuration.Configuration
|
||||
{
|
||||
ServiceLineDimensionGuid = Guid.Empty
|
||||
};
|
||||
|
||||
var results = await service.GetServiceLineNodes(configuration, new List<string>(), CancellationToken.None);
|
||||
results.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetServiceLineNodes()
|
||||
{
|
||||
var service = new ENOpportunityService(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var configuration = new Biz.Configuration.Configuration
|
||||
{
|
||||
ServiceLineDimensionGuid = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var results = await service.GetServiceLineNodes(configuration, new List<string>(), CancellationToken.None);
|
||||
results.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetNodesByGlobalId()
|
||||
{
|
||||
var service = new ENOpportunityService(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var results = await service.GetNodesByGlobalId("entity", CancellationToken.None);
|
||||
results.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestGetNodesByHierarchyGuid()
|
||||
{
|
||||
var service = new ENOpportunityService(_dbContextFactoryMock.Object, null, null,
|
||||
_schemaServiceMock.Object, _hangfireServiceMock.Object, _configurationMock.Object, _fiscalMonthResolverMock.Object,
|
||||
_initiativeService, _updateFactOpportunitySavingsService, _backgroundJobClient.Object, _idServiceClient.Object, _notificationHubContext.Object, _authorizationServiceClient.Object);
|
||||
var results = await service.GetNodesByHierarchyGuid(Guid.NewGuid(), CancellationToken.None);
|
||||
results.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSetHidden()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opportunities = framework.ENOpportunities;
|
||||
var opportunity = opportunities.First(vo => !vo.IsHidden);
|
||||
var isHidden = opportunity.IsHidden;
|
||||
var service = new ENOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
await service.SetHiddenAsync(opportunity.OpportunityGuid, !isHidden, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
var result = await service.GetById(opportunity.OpportunityGuid, CancellationToken.None);
|
||||
result.IsHidden.Should().Be(!isHidden);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestGetFilterOptionsAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var opportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
var taskResult = await opportunityService
|
||||
.GetFilterOptionsAsync(new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345"), cancellationToken);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNull();
|
||||
return;
|
||||
}
|
||||
taskResult.Should()
|
||||
.BeOfType<ENFilterOptions>()
|
||||
.And.NotBeNull();
|
||||
taskResult.OpportunityLevels.Should().AllBeOfType<FilterMember>().And.NotBeNullOrEmpty()
|
||||
.And.NotContain(cd => string.IsNullOrEmpty(cd.Id))
|
||||
.And.NotContain(cd => string.IsNullOrEmpty(cd.Text));
|
||||
}
|
||||
catch (NotImplementedException nie)
|
||||
{
|
||||
nie.Should().BeOfType<NotImplementedException>();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Should().BeOfType<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetHiddenAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
try
|
||||
{
|
||||
var opportunities = encounterOpportunityService.GetAllAsync(cancellationToken).Result;
|
||||
var opportunity = opportunities.FirstOrDefault(o => o.IsHidden);
|
||||
if (opportunity == null) return;
|
||||
var isHidden = opportunity.IsHidden;
|
||||
|
||||
//act
|
||||
await encounterOpportunityService.SetHiddenAsync(opportunity.OpportunityGuid, !isHidden,
|
||||
|
||||
cancellationToken);
|
||||
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName)) return;
|
||||
|
||||
var result = encounterOpportunityService.GetById(opportunity.OpportunityGuid, cancellationToken).Result;
|
||||
|
||||
result.IsHidden.Should().Be(!isHidden);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
switch (e.GetType().Name)
|
||||
{
|
||||
case "AggregateException":
|
||||
case "OperationCanceledException":
|
||||
return;
|
||||
}
|
||||
e.Should().NotBeOfType<Exception>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetHiddenAsync_NoOpportunity(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var opportunity = jazzEntityFrameworkFactory.ENOpportunities.FirstOrDefault(o => o.IsHidden);
|
||||
if (opportunity == null) return;
|
||||
var isHidden = opportunity.IsHidden;
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
await encounterOpportunityService.SetHiddenAsync(Guid.Empty, !isHidden,
|
||||
cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Should().BeOfType<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetNoteAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
try
|
||||
{
|
||||
var opportunities = encounterOpportunityService.GetAllAsync(cancellationToken).Result;
|
||||
|
||||
var opportunity = opportunities.FirstOrDefault(o => o.Note == string.Empty);
|
||||
|
||||
if (opportunity == null) return;
|
||||
|
||||
var note = "new test note";
|
||||
// act
|
||||
await encounterOpportunityService.SetNoteAsync(opportunity.OpportunityGuid, note,
|
||||
CancellationToken.None);
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName)) return;
|
||||
var result = encounterOpportunityService.GetById(opportunity.OpportunityGuid, cancellationToken).Result;
|
||||
result.Note.Should().Be(note);
|
||||
|
||||
// assert
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
switch (e.GetType().Name)
|
||||
{
|
||||
case "AggregateException":
|
||||
case "OperationCanceledException":
|
||||
return;
|
||||
}
|
||||
e.Should().NotBeOfType<Exception>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSetNote()
|
||||
{
|
||||
// arrange
|
||||
var framework = new JazzEntityFrameworkFactory();
|
||||
await framework.CreateDbContextAsync(CancellationToken.None);
|
||||
var opportunities = framework.ENOpportunities;
|
||||
var opportunity = opportunities.First(vo => string.IsNullOrEmpty(vo.Note));
|
||||
var note = opportunity.Note;
|
||||
var expected = "Now is the time for all good engineers to write great code!";
|
||||
var service = new ENOpportunityService(framework, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
await service.SetNoteAsync(opportunity.OpportunityGuid, expected, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
var result = await service.GetById(opportunity.OpportunityGuid, CancellationToken.None);
|
||||
result.Note.Should().NotBeNullOrEmpty()
|
||||
.And.NotBe(note)
|
||||
.And.Be(expected);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworksWithCancellation))]
|
||||
public async Task TestSetNoteAsync_NoOpportunity(JazzEntityFrameworkFactory jazzEntityFrameworkFactory, CancellationToken cancellationToken)
|
||||
{
|
||||
// arrange
|
||||
var opportunity = jazzEntityFrameworkFactory.ENOpportunities.FirstOrDefault(o => o.Note == string.Empty);
|
||||
if (opportunity == null) return;
|
||||
var note = "new test note";
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
await encounterOpportunityService.SetNoteAsync(Guid.Empty, note, cancellationToken);
|
||||
|
||||
// assert
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Should().BeOfType<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworks))]
|
||||
public async Task TestGetAllAsync(JazzEntityFrameworkFactory jazzEntityFrameworkFactory)
|
||||
{
|
||||
// arrange
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var taskResult = await encounterOpportunityService.GetAllAsync(CancellationToken.None);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNullOrEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworks))]
|
||||
public async Task TestGetById(JazzEntityFrameworkFactory jazzEntityFrameworkFactory)
|
||||
{
|
||||
// arrange
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var tempResult = await encounterOpportunityService.GetAllAsync(CancellationToken.None);
|
||||
var taskResult =
|
||||
await encounterOpportunityService.GetById(tempResult.FirstOrDefault()?.OpportunityGuid ?? Guid.Empty, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNull()
|
||||
.And.BeOfType<ENOpportunity>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(JazzEntityFrameworkFactory), nameof(JazzEntityFrameworkFactory.GetFrameworks))]
|
||||
public async Task TestGetByIds(JazzEntityFrameworkFactory jazzEntityFrameworkFactory)
|
||||
{
|
||||
// arrange
|
||||
var encounterOpportunityService = new ENOpportunityService(jazzEntityFrameworkFactory, null, null, _initiativeService, _updateFactOpportunitySavingsService);
|
||||
|
||||
// act
|
||||
var tempResult = await encounterOpportunityService.GetAllAsync(CancellationToken.None);
|
||||
var ids = tempResult.Select(o => o.OpportunityGuid);
|
||||
var taskResult = await encounterOpportunityService.GetByIds(ids, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
if (string.IsNullOrEmpty(jazzEntityFrameworkFactory.TestName))
|
||||
{
|
||||
taskResult.Should().BeNullOrEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult.Should().NotBeNullOrEmpty()
|
||||
.And.AllBeOfType<ENOpportunity>()
|
||||
.And.HaveCount(ids.Count());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TestTearDown()
|
||||
{
|
||||
// Method intentionally left empty.
|
||||
}
|
||||
|
||||
#region TestCaseData
|
||||
|
||||
public static IEnumerable<TestCaseData> GetFilterData()
|
||||
{
|
||||
yield return new TestCaseData(new ENFilters { ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("No Filters");
|
||||
yield return new TestCaseData(new ENFilters { ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("View Visible");
|
||||
yield return new TestCaseData(new ENFilters { ViewVisible = false, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("Not View Visible");
|
||||
yield return new TestCaseData(new ENFilters { IdentifiedSavings = 1999, ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName(
|
||||
"By Identified Savings");
|
||||
yield return new TestCaseData(new ENFilters { IdentifiedSavings = 999, ViewVisible = false, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName(
|
||||
"By Identified Savings Not ViewVisible");
|
||||
yield return new TestCaseData(new ENFilters { OpportunityLevels = new[] { "Entity" }, ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") })
|
||||
.SetName("By OpportunityLevels");
|
||||
yield return new TestCaseData(new ENFilters { OpportunityLevels = new[] { "Provider" }, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345"), ViewVisible = false })
|
||||
.SetName("By OpportunityLevels Not ViewVisible");
|
||||
yield return new TestCaseData(new ENFilters { EncounterGroups = EncounterGroup.CaseType, ViewVisible = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") })
|
||||
.SetName("By EncounterGroups");
|
||||
yield return new TestCaseData(new ENFilters { EncounterGroups = EncounterGroup.PatientPopulation, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345"), ViewVisible = false })
|
||||
.SetName("By EncounterGroups Not ViewVisible");
|
||||
yield return new TestCaseData(new ENFilters { Seasonality = SeasonalityType.With, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") })
|
||||
.SetName("By Seaonality");
|
||||
yield return new TestCaseData(new ENFilters { OpportunitySearch = "EN - 101", ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("Search By Id");
|
||||
yield return new TestCaseData(new ENFilters { ExcludeInitiatives = true, ConfigurationGuid = new Guid("e96abe3e-1526-43ca-815e-ee30c5b34345") }).SetName("Exclude Opportunities with Initiatives");
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> GetENOpportunityDataModel()
|
||||
{
|
||||
return from configGuid in ConfigGuids
|
||||
from EncounterGroup encounterGroup in Enum.GetValues(typeof(EncounterGroup))
|
||||
where encounterGroup != EncounterGroup.Both
|
||||
from OpportunityLevel encounterOpportunityLevel in Enum.GetValues(typeof(OpportunityLevel))
|
||||
from encounterGroups in new[] { true, false }
|
||||
from opportunityLevels in new[] { true, false }
|
||||
select new TestCaseData(new ENOpportunityDataModelWithFilters
|
||||
{
|
||||
ConfigurationGuid = configGuid,
|
||||
EncounterGroup = (int)encounterGroup,
|
||||
EncounterOpportunityLevel = encounterOpportunityLevel,
|
||||
CaseTypes = encounterGroup == EncounterGroup.CaseType
|
||||
? new List<FilterHelper>
|
||||
{
|
||||
new() {Id = "Id"}
|
||||
}
|
||||
: new List<FilterHelper>(),
|
||||
PatientPopulations = encounterGroup == EncounterGroup.PatientPopulation
|
||||
? new List<FilterHelper>
|
||||
{
|
||||
new () {Id = "Id"}
|
||||
}
|
||||
: new List<FilterHelper>(),
|
||||
Entities = encounterOpportunityLevel == OpportunityLevel.Entity
|
||||
? new List<FilterHelper>
|
||||
{
|
||||
new () {Id = "Id"}
|
||||
}
|
||||
: new List<FilterHelper>(),
|
||||
ServiceLines = encounterOpportunityLevel == OpportunityLevel.ServiceLine
|
||||
? new List<FilterHelper>
|
||||
{
|
||||
new () {Id = "Id"}
|
||||
}
|
||||
: new List<FilterHelper>(),
|
||||
Specialties = encounterOpportunityLevel == OpportunityLevel.Specialty
|
||||
? new List<FilterHelper>
|
||||
{
|
||||
new () {Id = "Id"}
|
||||
}
|
||||
: new List<FilterHelper>(),
|
||||
Providers = encounterOpportunityLevel == OpportunityLevel.Provider
|
||||
? new List<FilterHelper>
|
||||
{
|
||||
new () {Id = "Id"}
|
||||
}
|
||||
: new List<FilterHelper>()
|
||||
}).SetName($"{nameof(TestCreateAsync)} {ConfigNames[ConfigGuids.IndexOf(configGuid)]} IsUsingCustomEntityDim: {ConfigGuids.IndexOf(configGuid) == 1} IsUsingSg2: {ConfigGuids.IndexOf(configGuid) == 3} {encounterGroup} {encounterOpportunityLevel}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Snowflake;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
|
||||
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.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.StrataSphereCompare.Client;
|
||||
using Strata.StrataSphereCompare.Client.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Strata.ContinuousImprovement.Biz.Exploration.ExplorationService;
|
||||
using ExplorationFilter = Strata.ContinuousImprovement.Biz.Exploration.Filters.Filter;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.Exploration
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class ExplorationServiceBenchmarkingUnitTests
|
||||
{
|
||||
private const string FeatureFlagKey = "cibenchmarkingenabled";
|
||||
private const string CaseTypeFamilyName = "Sample CTF";
|
||||
|
||||
private Mock<ISnowflakeDatabaseContext> _snowflakeDatabaseContextMock;
|
||||
private Mock<IFiscalMonthResolver> _fiscalMonthResolverMock;
|
||||
private Mock<IClaimsPrincipalAccessor> _claimsPrincipalAccessorMock;
|
||||
private Mock<IStrataSphereCompareService> _strataSphereCompareServiceMock;
|
||||
private Mock<ILogger<ExplorationService>> _explorationServiceLoggerMock;
|
||||
private Mock<IExplorationFilterService> _explorationFilterServiceMock;
|
||||
|
||||
private ExplorationInfo _explorationInfo;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetUp()
|
||||
{
|
||||
_snowflakeDatabaseContextMock = new Mock<ISnowflakeDatabaseContext>();
|
||||
_fiscalMonthResolverMock = new Mock<IFiscalMonthResolver>();
|
||||
_explorationServiceLoggerMock = new Mock<ILogger<ExplorationService>>();
|
||||
|
||||
var principal = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
var defaultUser = principal.GetCurrentClaimsPrincipal();
|
||||
_claimsPrincipalAccessorMock = new Mock<IClaimsPrincipalAccessor>();
|
||||
_claimsPrincipalAccessorMock.Setup(a => a.GetCurrentClaimsPrincipal()).Returns(defaultUser);
|
||||
|
||||
_strataSphereCompareServiceMock = new Mock<IStrataSphereCompareService>();
|
||||
_explorationFilterServiceMock = new Mock<IExplorationFilterService>();
|
||||
|
||||
// Setup ExplorationFilterService mock
|
||||
_explorationFilterServiceMock
|
||||
.Setup(s => s.GetFilterStringsAsync(It.IsAny<IEnumerable<FilterChipItem>>(), It.IsAny<IQueryParamBase>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(("", ""));
|
||||
_explorationFilterServiceMock
|
||||
.Setup(s => s.BuildDataFiltersAsync(It.IsAny<CaseTypeFamily>(), It.IsAny<string>(), It.IsAny<ExplorationFilter>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new CostDetailsFilterDto());
|
||||
|
||||
// Setup 1: For GetCaseTypeFamilySummariesAsync (no drill down filter)
|
||||
_strataSphereCompareServiceMock
|
||||
.Setup(s => s.GetCostDetailsAsync(
|
||||
It.IsAny<BaseComparisonParams>(),
|
||||
It.Is<CostDetailsFilterDto>(filter => filter.CostDetailsFilterDrillDown == null),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new[]
|
||||
{
|
||||
new CostDetailsDto
|
||||
{
|
||||
AprDrgCaseTypeFamilyName = CaseTypeFamilyName,
|
||||
P50th_VariableDirectCost = 250.75m,
|
||||
P50th_Alos = 5.2m
|
||||
}
|
||||
});
|
||||
|
||||
// Setup 2: For GetCaseTypeFamilySummaryDetailsAsync (with drill down filter)
|
||||
_strataSphereCompareServiceMock
|
||||
.Setup(s => s.GetCostDetailsAsync(
|
||||
It.IsAny<BaseComparisonParams>(),
|
||||
It.Is<CostDetailsFilterDto>(filter => filter.CostDetailsFilterDrillDown != null),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new[]
|
||||
{
|
||||
new CostDetailsDto
|
||||
{
|
||||
SphChargeCodeCostDriver = "LOS",
|
||||
P50th_VariableDirectCost = 150.50m,
|
||||
P50th_Alos = 3.2m
|
||||
},
|
||||
new CostDetailsDto
|
||||
{
|
||||
SphChargeCodeCostDriver = "Surgery",
|
||||
P50th_VariableDirectCost = 300.25m,
|
||||
P50th_Alos = 4.8m
|
||||
}
|
||||
});
|
||||
|
||||
// Summary Mock Data
|
||||
_snowflakeDatabaseContextMock
|
||||
.Setup(s => s.QueryAsync<CaseTypeFamilySummaryInfo>(
|
||||
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>(), It.IsAny<TimeSpan>()))
|
||||
.ReturnsAsync(new[]
|
||||
{
|
||||
new CaseTypeFamilySummaryInfo { CaseTypeFamilyName = CaseTypeFamilyName }
|
||||
});
|
||||
|
||||
// Summary Detail Mock Data - for GetCaseTypeFamilySummaryDetailsAsync
|
||||
_snowflakeDatabaseContextMock
|
||||
.Setup(s => s.QueryAsync<CaseTypeFamilyEncounterInfo>(
|
||||
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>(), It.IsAny<TimeSpan>()))
|
||||
.ReturnsAsync(new[]
|
||||
{
|
||||
new CaseTypeFamilyEncounterInfo
|
||||
{
|
||||
CaseTypeFamilyName = CaseTypeFamilyName,
|
||||
CostDriver = "LOS"
|
||||
},
|
||||
new CaseTypeFamilyEncounterInfo
|
||||
{
|
||||
CaseTypeFamilyName = CaseTypeFamilyName,
|
||||
CostDriver = "Surgery"
|
||||
}
|
||||
});
|
||||
|
||||
_explorationInfo = new ExplorationCostDriverInfo
|
||||
{
|
||||
CaseTypeFamilyId = 1,
|
||||
PeerGroupId = 1,
|
||||
ExclusionCriteria = new ExclusionCriteria
|
||||
{
|
||||
LosExclusionType = ExclusionCriteriaType.None,
|
||||
CostExclusionType = ExclusionCriteriaType.None
|
||||
},
|
||||
Filters = new ExplorationFilter // Use the alias
|
||||
{
|
||||
FilterChipItems = new List<FilterChipItem>
|
||||
{
|
||||
new FilterChipItem
|
||||
{
|
||||
ChipType = ChipType.DateRange,
|
||||
Key = ChipKey.DischargeDateID,
|
||||
DateRange = new List<DateTime>
|
||||
{
|
||||
new DateTime(2024, 1, 1),
|
||||
new DateTime(2024, 12, 31)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilySummariesAsync_FeatureFlagOn_PopulatesPeerMetrics()
|
||||
{
|
||||
// Arrange
|
||||
var jazzFactory = new JazzEntityFrameworkFactory();
|
||||
await jazzFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
|
||||
var featureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
|
||||
Mock.Get(featureFlagWrapper)
|
||||
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var service = new ExplorationService(
|
||||
jazzFactory,
|
||||
_snowflakeDatabaseContextMock.Object,
|
||||
_fiscalMonthResolverMock.Object,
|
||||
featureFlagWrapper,
|
||||
_strataSphereCompareServiceMock.Object,
|
||||
_explorationFilterServiceMock.Object,
|
||||
_explorationServiceLoggerMock.Object);
|
||||
|
||||
// Act
|
||||
var summaries = await service.GetCaseTypeFamilySummariesAsync(_explorationInfo, CancellationToken.None);
|
||||
var summary = summaries.Single(s => s.Name == CaseTypeFamilyName);
|
||||
|
||||
// Assert
|
||||
Assert.That(summary.Data.PeerMedianCost, Is.EqualTo(250.75d));
|
||||
Assert.That(summary.Data.PeerMedianALos, Is.EqualTo(5.2d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilySummariesAsync_FeatureFlagOff_DefaultPeerMetrics()
|
||||
{
|
||||
// Arrange
|
||||
var jazzFactory = new JazzEntityFrameworkFactory();
|
||||
await jazzFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
|
||||
var featureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
|
||||
Mock.Get(featureFlagWrapper)
|
||||
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var service = new ExplorationService(
|
||||
jazzFactory,
|
||||
_snowflakeDatabaseContextMock.Object,
|
||||
_fiscalMonthResolverMock.Object,
|
||||
featureFlagWrapper,
|
||||
_strataSphereCompareServiceMock.Object,
|
||||
_explorationFilterServiceMock.Object,
|
||||
_explorationServiceLoggerMock.Object);
|
||||
|
||||
// Act
|
||||
var summaries = await service.GetCaseTypeFamilySummariesAsync(_explorationInfo, CancellationToken.None);
|
||||
var summary = summaries.Single(s => s.Name == CaseTypeFamilyName);
|
||||
|
||||
// Assert
|
||||
Assert.That(summary.Data.PeerMedianCost, Is.EqualTo(0d));
|
||||
Assert.That(summary.Data.PeerMedianALos, Is.EqualTo(0d));
|
||||
}
|
||||
|
||||
[Test, Ignore("Feature flag is not enabled, this might need additional refactoring")]
|
||||
public async Task GetCaseTypeFamilySummaryDetailsAsync_FeatureFlagOn_PopulatesPeerMetrics()
|
||||
{
|
||||
// Arrange
|
||||
var jazzFactory = new JazzEntityFrameworkFactory();
|
||||
await jazzFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
|
||||
var featureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
|
||||
Mock.Get(featureFlagWrapper)
|
||||
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var service = new ExplorationService(
|
||||
jazzFactory,
|
||||
_snowflakeDatabaseContextMock.Object,
|
||||
_fiscalMonthResolverMock.Object,
|
||||
featureFlagWrapper,
|
||||
_strataSphereCompareServiceMock.Object,
|
||||
_explorationFilterServiceMock.Object,
|
||||
_explorationServiceLoggerMock.Object);
|
||||
|
||||
// Act
|
||||
var summaries = await service.GetCaseTypeFamilySummaryDetailsAsync(_explorationInfo, CancellationToken.None);
|
||||
var summary = summaries.Single();
|
||||
|
||||
// Assert
|
||||
Assert.That(summary, Is.Not.Null);
|
||||
Assert.That(summary.Name, Is.EqualTo(CaseTypeFamilyName));
|
||||
Assert.That(summary.Children, Is.Not.Null.And.Not.Empty);
|
||||
|
||||
var losChild = summary.Children.FirstOrDefault(c => c.Name == "LOS");
|
||||
if (losChild != null)
|
||||
{
|
||||
Assert.That(losChild.Data.PeerMedianCost, Is.EqualTo(150.50d));
|
||||
}
|
||||
|
||||
var surgeryChild = summary.Children.FirstOrDefault(c => c.Name == "Surgery");
|
||||
if (surgeryChild != null)
|
||||
{
|
||||
Assert.That(surgeryChild.Data.PeerMedianCost, Is.EqualTo(300.25d));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetCaseTypeFamilySummaryDetailsAsync_FeatureFlagOff_DefaultPeerMetrics()
|
||||
{
|
||||
// Arrange
|
||||
var jazzFactory = new JazzEntityFrameworkFactory();
|
||||
await jazzFactory.CreateDbContextAsync(CancellationToken.None);
|
||||
|
||||
var featureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
|
||||
Mock.Get(featureFlagWrapper)
|
||||
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var service = new ExplorationService(
|
||||
jazzFactory,
|
||||
_snowflakeDatabaseContextMock.Object,
|
||||
_fiscalMonthResolverMock.Object,
|
||||
featureFlagWrapper,
|
||||
_strataSphereCompareServiceMock.Object,
|
||||
_explorationFilterServiceMock.Object,
|
||||
_explorationServiceLoggerMock.Object);
|
||||
|
||||
// Act
|
||||
var summaries = await service.GetCaseTypeFamilySummaryDetailsAsync(_explorationInfo as ExplorationCostDriverInfo, CancellationToken.None);
|
||||
var summary = summaries.Single();
|
||||
|
||||
// Assert
|
||||
Assert.That(summary, Is.Not.Null);
|
||||
Assert.That(summary.Name, Is.EqualTo(CaseTypeFamilyName));
|
||||
if (summary.Children != null && summary.Children.Any())
|
||||
{
|
||||
foreach (var child in summary.Children)
|
||||
{
|
||||
Assert.That(child.Data.PeerMedianCost, Is.EqualTo(0d));
|
||||
Assert.That(child.Data.PeerMedianALos, Is.EqualTo(0d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using MoreLinq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.Configuration;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Snowflake;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.CaseTypeFamilies;
|
||||
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.ContinuousImprovement.JazzEntityFrameworkStub;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using Strata.StrataSphereCompare.Client;
|
||||
using Strata.StrataSphereCompare.Client.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
using VerifyTests;
|
||||
using static Strata.ContinuousImprovement.Biz.Exploration.ExplorationService;
|
||||
using ExplorationFilter = Strata.ContinuousImprovement.Biz.Exploration.Filters.Filter;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.Exploration
|
||||
{
|
||||
[TestFixture, Category("Unit")]
|
||||
public class ExplorationServiceTest
|
||||
{
|
||||
private const string SourceFilePath = @"Exploration/source/";
|
||||
|
||||
public IAsyncDbContextFactory<JazzDbContext> DbContextFactory { get; set; }
|
||||
public JazzEntityFrameworkFactory JAZZ_Framework { get; set; }
|
||||
public JazzDbContext JazzDbContext { get; set; }
|
||||
public ISqlGridReaderWrapper SqlGridReader { get; set; }
|
||||
public ISnowflakeDatabaseContext SnowflakeDatabaseContext { get; set; }
|
||||
public IConfigurationService ConfigurationService { get; set; }
|
||||
public IFiscalMonthResolver FiscalMonthResolver { get; set; }
|
||||
public ILogger<ExplorationService> Logger { get; set; }
|
||||
public VerifySettings VerifySettings { get; set; }
|
||||
public IFeatureFlagWrapper FeatureFlagWrapper { get; set; }
|
||||
public IStrataSphereCompareService StrataSphereCompareService { get; set; }
|
||||
public IExplorationFilterService ExplorationFilterService { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void ExplorationServiceTestOneTimeSetUp()
|
||||
{
|
||||
DbContextFactory = Mock.Of<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
JAZZ_Framework = new JazzEntityFrameworkFactory();
|
||||
|
||||
Mock.Get(DbContextFactory)
|
||||
.Setup(s => s.CreateDbContextAsync(It.IsAny<CancellationToken>()))
|
||||
.Returns(async () =>
|
||||
{
|
||||
JazzDbContext = await JAZZ_Framework.CreateDbContextAsync(CancellationToken.None);
|
||||
return JazzDbContext;
|
||||
});
|
||||
|
||||
SqlGridReader = Mock.Of<ISqlGridReaderWrapper>();
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read(It.IsAny<bool>()))
|
||||
.Returns(Enumerable.Empty<dynamic>());
|
||||
SnowflakeDatabaseContext = Mock.Of<ISnowflakeDatabaseContext>();
|
||||
Mock.Get(SnowflakeDatabaseContext)
|
||||
.Setup(s => s.QueryMultipleAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>(), It.IsAny<TimeSpan>()))
|
||||
.Returns(Task.FromResult(SqlGridReader));
|
||||
|
||||
ConfigurationService = Mock.Of<IConfigurationService>();
|
||||
FiscalMonthResolver = Mock.Of<IFiscalMonthResolver>();
|
||||
Logger = Mock.Of<ILogger<ExplorationService>>();
|
||||
VerifySettings = TestExtensions.TestSettings();
|
||||
FeatureFlagWrapper = Mock.Of<IFeatureFlagWrapper>();
|
||||
StrataSphereCompareService = Mock.Of<IStrataSphereCompareService>();
|
||||
ExplorationFilterService = Mock.Of<IExplorationFilterService>();
|
||||
|
||||
// Setup the mock to return empty results
|
||||
Mock.Get(ExplorationFilterService)
|
||||
.Setup(s => s.GetFilterStringsAsync(It.IsAny<IEnumerable<FilterChipItem>>(), It.IsAny<IQueryParamBase>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(("", ""));
|
||||
Mock.Get(ExplorationFilterService)
|
||||
.Setup(s => s.BuildDataFiltersAsync(It.IsAny<CaseTypeFamily>(), It.IsAny<string>(), It.IsAny<ExplorationFilter>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new CostDetailsFilterDto());
|
||||
Mock.Get(ExplorationFilterService)
|
||||
.Setup(s => s.CreateCostDriverFilter())
|
||||
.Returns(new FilterChipItem());
|
||||
Mock.Get(FeatureFlagWrapper)
|
||||
.Setup(f => f.IsCiBenchmarkingEnabled(It.IsAny<bool>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetCaseTypeFamilyDetailsTestCases))]
|
||||
public async Task GetCaseTypeFamilyDetailsTest(GetCaseTypeFamilyDetailsTestData data)
|
||||
{
|
||||
// arrange
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<CTFGroupCalcs>(It.IsAny<bool>()))
|
||||
.Returns(data.CTFGroupCalcsList);
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<EntityGroupCalcs>(It.IsAny<bool>()))
|
||||
.Returns(data.EntityGroupCalcsList);
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<EntityCodeGroupCalcs>(It.IsAny<bool>()))
|
||||
.Returns(data.EntityCodeGroupCalcsList);
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<EntityCodePhysicianGroupCalcs>(It.IsAny<bool>()))
|
||||
.Returns(data.EntityCodePhysicianGroupCalcsList);
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<CTFEncounterDetail>(It.IsAny<bool>()))
|
||||
.Returns(data.CTFEncounterDetailList);
|
||||
|
||||
var explorationService = new ExplorationService(
|
||||
DbContextFactory,
|
||||
SnowflakeDatabaseContext,
|
||||
FiscalMonthResolver,
|
||||
FeatureFlagWrapper,
|
||||
StrataSphereCompareService,
|
||||
ExplorationFilterService,
|
||||
Logger);
|
||||
|
||||
// act
|
||||
var taskResult = await explorationService.GetCaseTypeFamilyDetailsAsync(data.ExplorationDetailInfoData, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
await Verifier.Verify(taskResult, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GetEncountersTestCases))]
|
||||
public async Task GetEncountersTest(GetEncountersTestData data)
|
||||
{
|
||||
// arrange
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<PhysicianInfo>(It.IsAny<bool>()))
|
||||
.Returns(data.PhysicianInfoList);
|
||||
Mock.Get(SqlGridReader)
|
||||
.Setup(s => s.Read<EncounterInfoBase>(It.IsAny<bool>()))
|
||||
.Returns(data.EncounterInfoBaseList);
|
||||
var configurationGuid = Guid.Empty;
|
||||
|
||||
var explorationService = new ExplorationService(
|
||||
DbContextFactory,
|
||||
SnowflakeDatabaseContext,
|
||||
FiscalMonthResolver,
|
||||
FeatureFlagWrapper,
|
||||
StrataSphereCompareService,
|
||||
ExplorationFilterService,
|
||||
Logger);
|
||||
|
||||
// act
|
||||
var taskResult = await explorationService.GetEncountersAsync(data.ExplorationDetailInfoData, configurationGuid, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
await Verifier.Verify(taskResult, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> GetCaseTypeFamilyDetailsTestCases()
|
||||
{
|
||||
var testName = "GetCaseTypeFamilyDetailsTest";
|
||||
|
||||
var explorationDetailInfoDataJson = GetSourceFile("ExplorationDetailInfo.txt");
|
||||
var ctfGroupCalcsListJson = GetSourceFile("CTFGroupCalcs.txt");
|
||||
var entityGroupCalcsListJson = GetSourceFile("EntityGroupCalcs.txt");
|
||||
var entityCodeGroupCalcsListJson = GetSourceFile("EntityCodeGroupCalcs.txt");
|
||||
var entityCodePhysicianGroupCalcsListJson = GetSourceFile("EntityCodePhysicianGroupCalcs.txt");
|
||||
var ctfEncounterDetailListJson = GetSourceFile("CTFEncounterDetail.txt");
|
||||
|
||||
var dataAllMatch = new GetCaseTypeFamilyDetailsTestData()
|
||||
{
|
||||
ExplorationDetailInfoData = JsonSerializer.Deserialize<ExplorationDetailInfo>(explorationDetailInfoDataJson),
|
||||
CTFGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<CTFGroupCalcs>>(ctfGroupCalcsListJson),
|
||||
EntityGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<EntityGroupCalcs>>(entityGroupCalcsListJson),
|
||||
EntityCodeGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<EntityCodeGroupCalcs>>(entityCodeGroupCalcsListJson),
|
||||
EntityCodePhysicianGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<EntityCodePhysicianGroupCalcs>>(entityCodePhysicianGroupCalcsListJson),
|
||||
CTFEncounterDetailList = JsonSerializer.Deserialize<IEnumerable<CTFEncounterDetail>>(ctfEncounterDetailListJson)
|
||||
};
|
||||
dataAllMatch.ExplorationDetailInfoData.Filters = new ExplorationFilter() // FIXED: Changed from Biz.Exploration.Filters.Filter to ExplorationFilter
|
||||
{
|
||||
FilterChipItems = new List<FilterChipItem>()
|
||||
{
|
||||
new FilterChipItem {
|
||||
ChipType = ChipType.DateRange, Key = ChipKey.DischargeDateID,
|
||||
DateRange = new List<DateTime>()
|
||||
{
|
||||
new DateTime(2024, 1, 1),
|
||||
new DateTime(2024, 12, 31)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
yield return new TestCaseData(dataAllMatch)
|
||||
.SetName($"{testName} All Match");
|
||||
|
||||
var dataPhysicianIdNotMatch = new GetCaseTypeFamilyDetailsTestData()
|
||||
{
|
||||
ExplorationDetailInfoData = dataAllMatch.ExplorationDetailInfoData,
|
||||
CTFGroupCalcsList = dataAllMatch.CTFGroupCalcsList,
|
||||
EntityGroupCalcsList = dataAllMatch.EntityGroupCalcsList,
|
||||
EntityCodeGroupCalcsList = dataAllMatch.EntityCodeGroupCalcsList,
|
||||
EntityCodePhysicianGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<EntityCodePhysicianGroupCalcs>>(entityCodePhysicianGroupCalcsListJson),
|
||||
CTFEncounterDetailList = dataAllMatch.CTFEncounterDetailList
|
||||
};
|
||||
dataPhysicianIdNotMatch.EntityCodePhysicianGroupCalcsList.First().PhysicianId = 0;
|
||||
yield return new TestCaseData(dataPhysicianIdNotMatch)
|
||||
.SetName($"{testName} PhysicianId Not Match");
|
||||
|
||||
var dataCodeNotMatch = new GetCaseTypeFamilyDetailsTestData()
|
||||
{
|
||||
ExplorationDetailInfoData = dataAllMatch.ExplorationDetailInfoData,
|
||||
CTFGroupCalcsList = dataAllMatch.CTFGroupCalcsList,
|
||||
EntityGroupCalcsList = dataAllMatch.EntityGroupCalcsList,
|
||||
EntityCodeGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<EntityCodeGroupCalcs>>(entityCodeGroupCalcsListJson),
|
||||
EntityCodePhysicianGroupCalcsList = dataAllMatch.EntityCodePhysicianGroupCalcsList,
|
||||
CTFEncounterDetailList = dataAllMatch.CTFEncounterDetailList
|
||||
};
|
||||
dataCodeNotMatch.EntityCodeGroupCalcsList.First().Code = "";
|
||||
yield return new TestCaseData(dataCodeNotMatch)
|
||||
.SetName($"{testName} Code Not Match");
|
||||
|
||||
var dataEntityNotMatch = new GetCaseTypeFamilyDetailsTestData()
|
||||
{
|
||||
ExplorationDetailInfoData = dataAllMatch.ExplorationDetailInfoData,
|
||||
CTFGroupCalcsList = dataAllMatch.CTFGroupCalcsList,
|
||||
EntityGroupCalcsList = JsonSerializer.Deserialize<IEnumerable<EntityGroupCalcs>>(entityGroupCalcsListJson),
|
||||
EntityCodeGroupCalcsList = dataAllMatch.EntityCodeGroupCalcsList,
|
||||
EntityCodePhysicianGroupCalcsList = dataAllMatch.EntityCodePhysicianGroupCalcsList,
|
||||
CTFEncounterDetailList = dataAllMatch.CTFEncounterDetailList
|
||||
};
|
||||
dataEntityNotMatch.EntityGroupCalcsList.First().EntityId = 0;
|
||||
yield return new TestCaseData(dataEntityNotMatch)
|
||||
.SetName($"{testName} Entity Not Match");
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> GetEncountersTestCases()
|
||||
{
|
||||
var testName = "GetEncountersTest";
|
||||
|
||||
var explorationDetailInfoDataJson = GetSourceFile("ExplorationDetailInfo.txt");
|
||||
var physicianInfoJson = GetSourceFile("PhysicianInfo.txt");
|
||||
var encounterInfoBaseJson = GetSourceFile("EncounterInfoBase.txt");
|
||||
|
||||
var dataAllMatch = new GetEncountersTestData()
|
||||
{
|
||||
ExplorationDetailInfoData = JsonSerializer.Deserialize<ExplorationDetailInfo>(explorationDetailInfoDataJson),
|
||||
PhysicianInfoList = JsonSerializer.Deserialize<IEnumerable<PhysicianInfo>>(physicianInfoJson),
|
||||
EncounterInfoBaseList = JsonSerializer.Deserialize<IEnumerable<EncounterInfoBase>>(encounterInfoBaseJson)
|
||||
};
|
||||
|
||||
dataAllMatch.ExplorationDetailInfoData.Filters = new ExplorationFilter() // FIXED: Changed from Biz.Exploration.Filters.Filter to ExplorationFilter
|
||||
{
|
||||
FilterChipItems = new List<FilterChipItem>()
|
||||
{
|
||||
new FilterChipItem {
|
||||
ChipType = ChipType.DateRange, Key = ChipKey.DischargeDateID,
|
||||
DateRange = new List<DateTime>()
|
||||
{
|
||||
new DateTime(2024, 1, 1),
|
||||
new DateTime(2024, 12, 31)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
yield return new TestCaseData(dataAllMatch)
|
||||
.SetName($"{testName} All Match");
|
||||
|
||||
var dataPhysicianIdNotMatch = new GetEncountersTestData()
|
||||
{
|
||||
ExplorationDetailInfoData = dataAllMatch.ExplorationDetailInfoData,
|
||||
PhysicianInfoList = JsonSerializer.Deserialize<IEnumerable<PhysicianInfo>>(physicianInfoJson),
|
||||
EncounterInfoBaseList = dataAllMatch.EncounterInfoBaseList,
|
||||
};
|
||||
dataPhysicianIdNotMatch.PhysicianInfoList.First().PhysicianID = 0;
|
||||
yield return new TestCaseData(dataPhysicianIdNotMatch)
|
||||
.SetName($"{testName} PhysicianId Not Match");
|
||||
}
|
||||
|
||||
private static string GetSourceFile(string file)
|
||||
{
|
||||
string filePath = Path.Combine(TestHelper.TestsRootDirectory, SourceFilePath, file);
|
||||
string jsonData = File.ReadAllText(filePath);
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
public class GetCaseTypeFamilySummariesTestData
|
||||
{
|
||||
public ExplorationInfo ExplorationInfoData { get; set; }
|
||||
public IEnumerable<CaseTypeFamilyEncounterInfo> CaseTypeFamilyEncounterInfoList { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class GetCaseTypeFamilyDetailsTestData
|
||||
{
|
||||
public ExplorationDetailInfo ExplorationDetailInfoData { get; set; }
|
||||
public IEnumerable<CTFGroupCalcs> CTFGroupCalcsList { get; set; }
|
||||
public IEnumerable<EntityGroupCalcs> EntityGroupCalcsList { get; set; }
|
||||
public IEnumerable<EntityCodeGroupCalcs> EntityCodeGroupCalcsList { get; set; }
|
||||
public IEnumerable<EntityCodePhysicianGroupCalcs> EntityCodePhysicianGroupCalcsList { get; set; }
|
||||
public IEnumerable<CTFEncounterDetail> CTFEncounterDetailList { get; set; }
|
||||
}
|
||||
|
||||
public class GetEncountersTestData
|
||||
{
|
||||
public ExplorationDetailInfo ExplorationDetailInfoData { get; set; }
|
||||
public IEnumerable<PhysicianInfo> PhysicianInfoList { get; set; }
|
||||
public IEnumerable<EncounterInfoBase> EncounterInfoBaseList { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
[
|
||||
{
|
||||
"EncounterId": 24375411,
|
||||
"EncounterRecordNumber": "1024375411",
|
||||
"CTFType": 0,
|
||||
"EntityId": 93,
|
||||
"Code": "880",
|
||||
"PhysicianId": 3339,
|
||||
"LengthOfStay": 5,
|
||||
"VariableDirectCost": 208.1365,
|
||||
"IdentifiedVariation": 148.89435,
|
||||
"Charges": 4045.0814,
|
||||
"IsExpired": 0,
|
||||
"IsExcluded": false,
|
||||
"DischargeStatusCode": "06",
|
||||
"DischargeStatusDescription": "Discharged/transferred to home under care of an organized home health service organization in anticipation of covered skilled care"
|
||||
},
|
||||
{
|
||||
"EncounterId": 26713064,
|
||||
"EncounterRecordNumber": "1026713064",
|
||||
"CTFType": 0,
|
||||
"EntityId": 93,
|
||||
"Code": "880",
|
||||
"PhysicianId": 3474,
|
||||
"LengthOfStay": 3,
|
||||
"VariableDirectCost": 71.8018,
|
||||
"IdentifiedVariation": 12.559649999999998,
|
||||
"Charges": 985.8658,
|
||||
"IsExpired": 0,
|
||||
"IsExcluded": false,
|
||||
"DischargeStatusCode": "01",
|
||||
"DischargeStatusDescription": "Discharged to home or self care (routine discharge)"
|
||||
},
|
||||
{
|
||||
"EncounterId": 27090900,
|
||||
"EncounterRecordNumber": "1027090900",
|
||||
"CTFType": 0,
|
||||
"EntityId": 93,
|
||||
"Code": "880",
|
||||
"PhysicianId": 794,
|
||||
"LengthOfStay": 15,
|
||||
"VariableDirectCost": 5.0037,
|
||||
"IdentifiedVariation": -54.23845,
|
||||
"Charges": 384.65,
|
||||
"IsExpired": 0,
|
||||
"IsExcluded": true,
|
||||
"DischargeStatusCode": "03",
|
||||
"DischargeStatusDescription": "Discharged/transferred to Skilled Nursing Facility (SNF) with Medicare certification"
|
||||
},
|
||||
{
|
||||
"EncounterId": 28432227,
|
||||
"EncounterRecordNumber": "1028432227",
|
||||
"CTFType": 0,
|
||||
"EntityId": 93,
|
||||
"Code": "880",
|
||||
"PhysicianId": 3896,
|
||||
"LengthOfStay": 7,
|
||||
"VariableDirectCost": 46.6825,
|
||||
"IdentifiedVariation": -12.559650000000005,
|
||||
"Charges": 996.8704,
|
||||
"IsExpired": 0,
|
||||
"IsExcluded": true,
|
||||
"DischargeStatusCode": "03",
|
||||
"DischargeStatusDescription": "Discharged/transferred to Skilled Nursing Facility (SNF) with Medicare certification"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"CTFId": 206,
|
||||
"CTFName": "Acute Adjustment Reaction And Psychosocial Dysfunction",
|
||||
"MedianVDC": 59.24215
|
||||
}
|
||||
]
|
||||
+596
@@ -0,0 +1,596 @@
|
||||
[
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "Laboratory",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 270.5061,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 283.5443,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 554.0504,
|
||||
"Charges": 11265.2698,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "Excluded",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 0.2644,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 0.2644,
|
||||
"Charges": 49.0752,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "OR Time",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": -15.31945,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 1066.93575,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 1051.6163,
|
||||
"Charges": 17348.9676,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "Supplies",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": -74.0283,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 275.0648,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 201.0365,
|
||||
"Charges": 908.8934,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "Pharmacy",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": -15.66025,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 120.94545,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 105.2852,
|
||||
"Charges": 1801.1414,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 0,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 0,
|
||||
"Charges": 0,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "Cardiovascular",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 5.0037,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 5.0037,
|
||||
"Charges": 393.3321,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 6654,
|
||||
"CaseTypeFamilyName": "Endoscopy Procedures on the Mediastinum",
|
||||
"Vdc": 2038.5242,
|
||||
"MedianVdc": 1931.0259,
|
||||
"TwentyFivePercentileVdc": 1877.27675,
|
||||
"SeventyFivePercentileVdc": 1984.77505,
|
||||
"TwentyFivePercentileLos": 0,
|
||||
"SeventyFivePercentileLos": 0,
|
||||
"CostDriver": "Therapeutic Services",
|
||||
"EncounterId": 2562780,
|
||||
"DischargeDateTime": "2023-01-17T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": -60.63385,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 0,
|
||||
"MedianLengthOfStay": 0,
|
||||
"MedianVariableDirectCost": 181.90155,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 121.2677,
|
||||
"Charges": 320.4913,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 0,
|
||||
"IqrVdc": 107.4983,
|
||||
"MadLos": 0,
|
||||
"MadVdc": 159.37698,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Pharmacy",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 373.0302,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 373.0302,
|
||||
"Charges": 11782.8149,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Supplies",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": -19.7357,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 87.3104,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 67.5747,
|
||||
"Charges": 254.2575,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "OR Time",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 680.5711,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 680.5711,
|
||||
"Charges": 12771.463,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "LOS",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 357.1267,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 1747.5269,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 2104.6536,
|
||||
"Charges": 29186.0674,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Other Diagnostic Services",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 37.18375,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 113.54845,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 150.7322,
|
||||
"Charges": 3224.1159,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 0,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 0,
|
||||
"Charges": 0,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Laboratory",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 123.2207,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 123.2207,
|
||||
"Charges": 5503.8051,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Therapeutic Services",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 245.3111,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 473.0037,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 718.3148,
|
||||
"Charges": 7026.8907,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Imaging",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": 0,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 37.5413,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 37.5413,
|
||||
"Charges": 690.5777,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
},
|
||||
{
|
||||
"CaseTypeFamilyId": 592,
|
||||
"CaseTypeFamilyName": "Skin Debridement",
|
||||
"Vdc": 4353.6024,
|
||||
"MedianVdc": 4353.6024,
|
||||
"TwentyFivePercentileVdc": 2983.74705,
|
||||
"SeventyFivePercentileVdc": 4525.49485,
|
||||
"TwentyFivePercentileLos": 4.5,
|
||||
"SeventyFivePercentileLos": 9.5,
|
||||
"CostDriver": "Excluded",
|
||||
"EncounterId": 2565425,
|
||||
"DischargeDateTime": "2023-02-02T00:00:00Z",
|
||||
"EncounterRecordNumber": null,
|
||||
"EntityId": 0,
|
||||
"IdentifiedVariation": -49.1141,
|
||||
"IsExpired": 0,
|
||||
"LengthOfStay": 12,
|
||||
"MedianLengthOfStay": 7,
|
||||
"MedianVariableDirectCost": 147.0779,
|
||||
"MedianGrossCharges": 0,
|
||||
"VariableDirectCost": 97.9638,
|
||||
"Charges": 1718.5171,
|
||||
"IsExcluded": false,
|
||||
"IqrLos": 5,
|
||||
"IqrVdc": 1541.7478,
|
||||
"MadLos": 7.413,
|
||||
"MadVdc": 509.6955,
|
||||
"DischargeStatusCode": null,
|
||||
"DischargeStatusDescription": null,
|
||||
"MortalityRate": 0,
|
||||
"ROM": 0,
|
||||
"SOI": 0
|
||||
}
|
||||
]
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
[
|
||||
{
|
||||
"EncounterId": 3490618,
|
||||
"EncounterRecordNumber": "1003490618",
|
||||
"PhysicianID": 3845,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 15,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-02-18T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 21268418,
|
||||
"EncounterRecordNumber": "1021268418",
|
||||
"PhysicianID": 3939,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 2,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-04-07T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 21269053,
|
||||
"EncounterRecordNumber": "1021269053",
|
||||
"PhysicianID": 2986,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 14,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-01-28T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 21366384,
|
||||
"EncounterRecordNumber": "1021366384",
|
||||
"PhysicianID": 1370,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 12,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-03-01T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 21549774,
|
||||
"EncounterRecordNumber": "1021549774",
|
||||
"PhysicianID": 3153,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 13,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 1,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-02-15T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 22931559,
|
||||
"EncounterRecordNumber": "1022931559",
|
||||
"PhysicianID": 4446,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 11,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-05-21T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 23497650,
|
||||
"EncounterRecordNumber": "1023497650",
|
||||
"PhysicianID": 842,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 8,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-05-14T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 24240851,
|
||||
"EncounterRecordNumber": "1024240851",
|
||||
"PhysicianID": 720,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 3,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-06-24T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 25272685,
|
||||
"EncounterRecordNumber": "1025272685",
|
||||
"PhysicianID": 3481,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 3,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-07-13T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 25954564,
|
||||
"EncounterRecordNumber": "1025954564",
|
||||
"PhysicianID": 842,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 2,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-08-04T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 25971328,
|
||||
"EncounterRecordNumber": "1025971328",
|
||||
"PhysicianID": 3343,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 2,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-07-25T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 27006329,
|
||||
"EncounterRecordNumber": "1027006329",
|
||||
"PhysicianID": 4269,
|
||||
"AgeCohortID": 3,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 2,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-10-01T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 29625457,
|
||||
"EncounterRecordNumber": "1029625457",
|
||||
"PhysicianID": 842,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 2,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 9,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-11-24T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
},
|
||||
{
|
||||
"EncounterId": 30059663,
|
||||
"EncounterRecordNumber": "1030059663",
|
||||
"PhysicianID": 24764,
|
||||
"AgeCohortID": 4,
|
||||
"EntityID": 13,
|
||||
"ServiceLineID": 13,
|
||||
"LengthOfStay": 6,
|
||||
"IsQVIEncounter": false,
|
||||
"DischargeDateTime": "2023-11-19T00:00:00Z",
|
||||
"IsExcluded": false
|
||||
}
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user