diff --git a/Covid19Radar/Covid19Radar/Repository/ExposureDataRepository.cs b/Covid19Radar/Covid19Radar/Repository/ExposureDataRepository.cs index 632c2503f..0d5bc95df 100644 --- a/Covid19Radar/Covid19Radar/Repository/ExposureDataRepository.cs +++ b/Covid19Radar/Covid19Radar/Repository/ExposureDataRepository.cs @@ -17,7 +17,7 @@ namespace Covid19Radar.Repository public interface IExposureDataRepository { // ExposureWindow mode - Task SetExposureDataAsync( + Task<(List, List)> SetExposureDataAsync( List dailySummaryList, List exposueWindowList ); @@ -69,7 +69,7 @@ ILoggerService loggerService private readonly DailySummary.Comparer _dailySummaryComparer = new DailySummary.Comparer(); private readonly ExposureWindow.Comparer _exposureWindowComparer = new ExposureWindow.Comparer(); - public async Task SetExposureDataAsync( + public async Task<(List, List)> SetExposureDataAsync( List dailySummaryList, List exposueWindowList ) @@ -79,15 +79,19 @@ List exposueWindowList List existDailySummaryList = await GetDailySummariesAsync(); List existExposureWindowList = await GetExposureWindowsAsync(); - List newDailySummaryList = existDailySummaryList.Union(dailySummaryList).ToList(); - newDailySummaryList.Sort(_dailySummaryComparer); + List unionDailySummaryList = existDailySummaryList.Union(dailySummaryList).ToList(); + List unionExposureWindowList = existExposureWindowList.Union(exposueWindowList).ToList(); + unionDailySummaryList.Sort(_dailySummaryComparer); + unionExposureWindowList.Sort(_exposureWindowComparer); - List newExposureWindowList = existExposureWindowList.Union(exposueWindowList).ToList(); - newExposureWindowList.Sort(_exposureWindowComparer); + await SaveExposureDataAsync(unionDailySummaryList, unionExposureWindowList); - await SaveExposureDataAsync(newDailySummaryList, newExposureWindowList); + List newDailySummaryList = unionDailySummaryList.Except(existDailySummaryList).ToList(); + List newExposureWindowList = unionExposureWindowList.Except(existExposureWindowList).ToList(); _loggerService.EndMethod(); + + return (newDailySummaryList, newExposureWindowList); } private Task SaveExposureDataAsync(IList dailySummaryList, IList exposureWindowList) diff --git a/Covid19Radar/Covid19Radar/Services/ExposureDetectionService.cs b/Covid19Radar/Covid19Radar/Services/ExposureDetectionService.cs index 04692e8d5..a57c8b05d 100644 --- a/Covid19Radar/Covid19Radar/Services/ExposureDetectionService.cs +++ b/Covid19Radar/Covid19Radar/Services/ExposureDetectionService.cs @@ -98,13 +98,16 @@ public async Task ExposureDetectedAsync(ExposureConfiguration exposureConfigurat var enVersionStr = enVersion.ToString(); - await _exposureDataRepository.SetExposureDataAsync( + var (newDailySummaries, newExposureWindows) = await _exposureDataRepository.SetExposureDataAsync( dailySummaries.ToList(), exposureWindows.ToList() ); - bool isHighRiskExposureDetected = dailySummaries - .Select(dailySummary => _exposureRiskCalculationService.CalcRiskLevel(dailySummary)) + bool isHighRiskExposureDetected = newDailySummaries + .Select(ds => _exposureRiskCalculationService.CalcRiskLevel( + ds, + newExposureWindows.Where(ew => ew.DateMillisSinceEpoch == ds.DateMillisSinceEpoch).ToList()) + ) .Any(riskLevel => riskLevel >= RiskLevel.High); if (isHighRiskExposureDetected) @@ -122,7 +125,7 @@ public async Task ExposureDetectedAsync(ExposureConfiguration exposureConfigurat exposureConfiguration, _deviceInfoUtility.Model, enVersionStr, - dailySummaries, exposureWindows + newDailySummaries, newExposureWindows ); } catch (Exception e) @@ -138,7 +141,7 @@ public async Task ExposureDetectedAsync(ExposureConfiguration exposureConfigurat exposureConfiguration, _deviceInfoUtility.Model, enVersionStr, - dailySummaries, exposureWindows + newDailySummaries, newExposureWindows ); } catch (Exception e) diff --git a/Covid19Radar/Covid19Radar/Services/ExposureRiskCalculationService.cs b/Covid19Radar/Covid19Radar/Services/ExposureRiskCalculationService.cs index aa48cc472..a0ad59784 100644 --- a/Covid19Radar/Covid19Radar/Services/ExposureRiskCalculationService.cs +++ b/Covid19Radar/Covid19Radar/Services/ExposureRiskCalculationService.cs @@ -2,19 +2,29 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +using System.Collections.Generic; using Chino; namespace Covid19Radar.Services { public interface IExposureRiskCalculationService { - RiskLevel CalcRiskLevel(DailySummary dailySummary); + RiskLevel CalcRiskLevel(DailySummary dailySummary, List exposureWindowList); } public class ExposureRiskCalculationService : IExposureRiskCalculationService { - // TODO: We should make consideration later. - public RiskLevel CalcRiskLevel(DailySummary dailySummary) - => RiskLevel.High; + // TODO: refine + private const double THRESHOLD_SCORE_SUM = 2000.0; + + public RiskLevel CalcRiskLevel(DailySummary dailySummary, List exposureWindowList) + { + if (dailySummary.DaySummary.ScoreSum >= THRESHOLD_SCORE_SUM) + { + return RiskLevel.High; + } + return RiskLevel.Low; + } + } } diff --git a/Covid19Radar/Tests/Covid19Radar.UnitTests/Repository/ExposureDataRepositoryTests.cs b/Covid19Radar/Tests/Covid19Radar.UnitTests/Repository/ExposureDataRepositoryTests.cs index f14a8f69f..ed6c1c39c 100644 --- a/Covid19Radar/Tests/Covid19Radar.UnitTests/Repository/ExposureDataRepositoryTests.cs +++ b/Covid19Radar/Tests/Covid19Radar.UnitTests/Repository/ExposureDataRepositoryTests.cs @@ -11,6 +11,7 @@ using Covid19Radar.Services; using Covid19Radar.Services.Logs; using Moq; +using Newtonsoft.Json; using Xunit; namespace Covid19Radar.UnitTests.Repository @@ -37,6 +38,173 @@ private IExposureDataRepository CreateRepository() mockLoggerService.Object ); + [Fact] + public async void SetExposureDataTests_Once() + { + var existDailySummaries = new List() { + }; + var existExposureWindows = new List() + { + }; + + var addDailySummaries = new List() { + new DailySummary() + { + DateMillisSinceEpoch = 10, + DaySummary = new ExposureSummaryData(), + ConfirmedClinicalDiagnosisSummary = new ExposureSummaryData(), + ConfirmedTestSummary = new ExposureSummaryData(), + RecursiveSummary = new ExposureSummaryData(), + SelfReportedSummary = new ExposureSummaryData() + } + }; + var addExposureWindows = new List() + { + new ExposureWindow() + { + CalibrationConfidence = CalibrationConfidence.High, + DateMillisSinceEpoch = 0, + Infectiousness = Infectiousness.High, + ReportType = ReportType.Unknown, + ScanInstances = new List() + }, + new ExposureWindow() + { + CalibrationConfidence = CalibrationConfidence.Medium, + DateMillisSinceEpoch = 0, + Infectiousness = Infectiousness.High, + ReportType = ReportType.ConfirmedTest, + ScanInstances = new List() + } + }; + + // Mock Setup + mockPreferencesService + .Setup(x => x.GetValue(It.Is(x => x == "DailySummaries"), It.IsAny())) + .Returns(JsonConvert.SerializeObject(existDailySummaries)); + mockPreferencesService + .Setup(x => x.GetValue(It.Is(x => x == "ExposureWindows"), It.IsAny())) + .Returns(JsonConvert.SerializeObject(existExposureWindows)); + + var unitUnderTest = CreateRepository(); + + var (newDailySummaryList, newExposureWindowList) = await unitUnderTest.SetExposureDataAsync( + addDailySummaries, + addExposureWindows + ); + + var expectedDailySummariesJson = JsonConvert.SerializeObject(addDailySummaries); + var expectedExposureWindowsJson = JsonConvert.SerializeObject(addExposureWindows); + + // Assert + Assert.Equal(addDailySummaries, newDailySummaryList); + Assert.Equal(addExposureWindows, newExposureWindowList); + mockPreferencesService.Verify(x => x.SetValue("DailySummaries", expectedDailySummariesJson), Times.Once); + mockPreferencesService.Verify(x => x.SetValue("ExposureWindows", expectedExposureWindowsJson), Times.Once); + + } + + [Fact] + public async void SetExposureDataTests_Append() + { + var existDailySummaries = new List() { + new DailySummary() + { + DateMillisSinceEpoch = 0, + DaySummary = new ExposureSummaryData(), + ConfirmedClinicalDiagnosisSummary = new ExposureSummaryData(), + ConfirmedTestSummary = new ExposureSummaryData(), + RecursiveSummary = new ExposureSummaryData(), + SelfReportedSummary = new ExposureSummaryData() + } + }; + var existExposureWindows = new List() + { + new ExposureWindow() + { + CalibrationConfidence = CalibrationConfidence.High, + DateMillisSinceEpoch = 0, + Infectiousness = Infectiousness.High, + ReportType = ReportType.Unknown, + ScanInstances = new List() + } + }; + + var addDailySummaries = new List() { + new DailySummary() + { + DateMillisSinceEpoch = 10, + DaySummary = new ExposureSummaryData(), + ConfirmedClinicalDiagnosisSummary = new ExposureSummaryData(), + ConfirmedTestSummary = new ExposureSummaryData(), + RecursiveSummary = new ExposureSummaryData(), + SelfReportedSummary = new ExposureSummaryData() + } + }; + var addExposureWindows = new List() + { + new ExposureWindow() + { + CalibrationConfidence = CalibrationConfidence.High, + DateMillisSinceEpoch = 0, + Infectiousness = Infectiousness.High, + ReportType = ReportType.Unknown, + ScanInstances = new List() + }, + new ExposureWindow() + { + CalibrationConfidence = CalibrationConfidence.Medium, + DateMillisSinceEpoch = 0, + Infectiousness = Infectiousness.High, + ReportType = ReportType.ConfirmedTest, + ScanInstances = new List() + } + }; + + var expectedDailySummaries = new List() { + existDailySummaries[0], + addDailySummaries[0] + }; + var expectedExposureWindows = new List() + { + existExposureWindows[0], + addExposureWindows[1] + }; + + var expectedNewDailySummaries = new List() { + addDailySummaries[0] + }; + var expectedNewExposureWindows = new List() + { + addExposureWindows[1] + }; + + // Mock Setup + mockPreferencesService + .Setup(x => x.GetValue(It.Is(x => x == "DailySummaries"), It.IsAny())) + .Returns(JsonConvert.SerializeObject(existDailySummaries)); + mockPreferencesService + .Setup(x => x.GetValue(It.Is(x => x == "ExposureWindows"), It.IsAny())) + .Returns(JsonConvert.SerializeObject(existExposureWindows)); + + var unitUnderTest = CreateRepository(); + + var (newDailySummaryList, newExposureWindowList) = await unitUnderTest.SetExposureDataAsync( + addDailySummaries, + addExposureWindows + ); + + var expectedDailySummariesJson = JsonConvert.SerializeObject(expectedDailySummaries); + var expectedExposureWindowsJson = JsonConvert.SerializeObject(expectedExposureWindows); + + // Assert + Assert.Equal(expectedNewDailySummaries, newDailySummaryList); + Assert.Equal(expectedNewExposureWindows, newExposureWindowList); + mockPreferencesService.Verify(x => x.SetValue("DailySummaries", expectedDailySummariesJson), Times.Once); + mockPreferencesService.Verify(x => x.SetValue("ExposureWindows", expectedExposureWindowsJson), Times.Once); + + } + [Fact] public void GetExposureInformationListTests_Success() { diff --git a/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureDetectionServiceTests.cs b/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureDetectionServiceTests.cs index 0cba78674..df4553816 100644 --- a/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureDetectionServiceTests.cs +++ b/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureDetectionServiceTests.cs @@ -24,6 +24,7 @@ public class ExposureDetectionServiceTests: IDisposable private readonly Mock loggerService; private readonly Mock localNotificationService; private readonly Mock exposureDataCollectServer; + private readonly Mock exposureRiskCalculationService; private readonly Mock eventLogService; private readonly Mock dateTimeUtility; private readonly Mock deviceInfoUtility; @@ -43,6 +44,7 @@ public ExposureDetectionServiceTests() loggerService = mockRepository.Create(); localNotificationService = mockRepository.Create(); exposureDataCollectServer = mockRepository.Create(); + exposureRiskCalculationService = mockRepository.Create(); eventLogService = mockRepository.Create(); clientService = mockRepository.Create(); @@ -89,14 +91,12 @@ private ExposureDetectionService CreateService() loggerService.Object ); - var exposureRiskCalculationService = new ExposureRiskCalculationService(); - return new ExposureDetectionService( loggerService.Object, userDataRepository, exposureDataRepository, localNotificationService.Object, - exposureRiskCalculationService, + exposureRiskCalculationService.Object, exposureConfigurationRepository, eventLogService.Object, exposureDataCollectServer.Object, @@ -180,13 +180,12 @@ public void DiagnosisKeysDataMappingApplied_ConfigurationNotUpdated() #region ExposureWindowsDetected [Fact] - public async void ExposureDetected_ExposureWindowHighRiskExposureDetected() + public async void ExposureDetected_HighRiskExposureDetected() { // Test Data var exposureConfiguration = new ExposureConfiguration(); var enVersion = 2; - // TODO under consideration var dailySummaries = new List() { new DailySummary() { @@ -230,6 +229,10 @@ public async void ExposureDetected_ExposureWindowHighRiskExposureDetected() It.IsAny>())); deviceInfoUtility.Setup(x => x.Model).Returns("UnitTest"); + exposureRiskCalculationService + .Setup(x => x.CalcRiskLevel(It.IsAny(), It.IsAny>())) + .Returns(RiskLevel.High); + // Test Case var unitUnderTest = CreateService(); @@ -241,121 +244,12 @@ public async void ExposureDetected_ExposureWindowHighRiskExposureDetected() } [Fact] - public async void ExposureDetected_Multiple() + public async void ExposureDetected_HighRiskExposureNotDetected() { // Test Data var exposureConfiguration = new ExposureConfiguration(); var enVersion = 2; - var existDailySummaries = new List() { - new DailySummary() - { - DateMillisSinceEpoch = 0, - DaySummary = new ExposureSummaryData(), - ConfirmedClinicalDiagnosisSummary = new ExposureSummaryData(), - ConfirmedTestSummary = new ExposureSummaryData(), - RecursiveSummary = new ExposureSummaryData(), - SelfReportedSummary = new ExposureSummaryData() - } - }; - var existExposureWindows = new List() - { - new ExposureWindow() - { - CalibrationConfidence = CalibrationConfidence.High, - DateMillisSinceEpoch = 0, - Infectiousness = Infectiousness.High, - ReportType = ReportType.Unknown, - ScanInstances = new List() - } - }; - - var newDailySummaries = new List() { - new DailySummary() - { - DateMillisSinceEpoch = 10, - DaySummary = new ExposureSummaryData(), - ConfirmedClinicalDiagnosisSummary = new ExposureSummaryData(), - ConfirmedTestSummary = new ExposureSummaryData(), - RecursiveSummary = new ExposureSummaryData(), - SelfReportedSummary = new ExposureSummaryData() - } - }; - var newExposureWindows = new List() - { - new ExposureWindow() - { - CalibrationConfidence = CalibrationConfidence.High, - DateMillisSinceEpoch = 0, - Infectiousness = Infectiousness.High, - ReportType = ReportType.Unknown, - ScanInstances = new List() - }, - new ExposureWindow() - { - CalibrationConfidence = CalibrationConfidence.Medium, - DateMillisSinceEpoch = 0, - Infectiousness = Infectiousness.High, - ReportType = ReportType.ConfirmedTest, - ScanInstances = new List() - } - }; - - // Mock Setup - preferencesService - .Setup(x => x.GetValue(It.Is(x => x == "IsExposureConfigurationUpdated"), false)) - .Returns(false); - exposureDataCollectServer - .Setup(x => x.UploadExposureDataAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>(), - It.IsAny>())); - deviceInfoUtility.Setup(x => x.Model).Returns("UnitTest"); - - preferencesService - .Setup(x => x.GetValue(It.Is(x => x == "DailySummaries"), It.IsAny())) - .Returns(JsonConvert.SerializeObject(existDailySummaries)); - preferencesService - .Setup(x => x.GetValue(It.Is(x => x == "ExposureWindows"), It.IsAny())) - .Returns(JsonConvert.SerializeObject(existExposureWindows)); - - - // Test Case - var unitUnderTest = CreateService(); - await unitUnderTest.ExposureDetectedAsync(exposureConfiguration, enVersion, newDailySummaries, newExposureWindows); - - - var expectedDailySummaries = new List() { - existDailySummaries[0], - newDailySummaries[0] - }; - var expectedExposureWindows = new List() - { - existExposureWindows[0], - newExposureWindows[1] - }; - - var expectedDailySummariesJson = JsonConvert.SerializeObject(expectedDailySummaries); - var expectedExposureWindowsJson = JsonConvert.SerializeObject(expectedExposureWindows); - - // Assert - preferencesService.Verify(x => x.SetValue("DailySummaries", expectedDailySummariesJson), Times.Once); - preferencesService.Verify(x => x.SetValue("ExposureWindows", expectedExposureWindowsJson), Times.Once); - localNotificationService.Verify(x => x.ShowExposureNotificationAsync(), Times.Once); - - - } - - [Fact(Skip = "always failed")] - public async void ExposureDetected_ExposureWindowHighRiskExposureNotDetected() - { - // Test Data - var exposureConfiguration = new ExposureConfiguration(); - var enVersion = 2; - - // TODO under consideration var dailySummaries = new List() { new DailySummary() { @@ -384,6 +278,12 @@ public async void ExposureDetected_ExposureWindowHighRiskExposureNotDetected() preferencesService. Setup(x => x.GetValue(It.Is(x => x == "IsExposureConfigurationUpdated"), false)) .Returns(true); + preferencesService + .Setup(x => x.GetValue(It.Is(x => x == "DailySummaries"), It.IsAny())) + .Returns("[]"); + preferencesService + .Setup(x => x.GetValue(It.Is(x => x == "ExposureWindows"), It.IsAny())) + .Returns("[]"); exposureDataCollectServer .Setup(x => x.UploadExposureDataAsync( It.IsAny(), @@ -393,6 +293,9 @@ public async void ExposureDetected_ExposureWindowHighRiskExposureNotDetected() It.IsAny>())); deviceInfoUtility.Setup(x => x.Model).Returns("UnitTest"); + exposureRiskCalculationService + .Setup(x => x.CalcRiskLevel(It.IsAny(), It.IsAny>())) + .Returns(RiskLevel.Low); // Test Case var unitUnderTest = CreateService(); @@ -446,6 +349,10 @@ public async void ExposureDetected_ExposureInformationHighRiskExposureDetected() It.IsAny>())); deviceInfoUtility.Setup(x => x.Model).Returns("UnitTest"); + exposureRiskCalculationService + .Setup(x => x.CalcRiskLevel(It.IsAny(), It.IsAny>())) + .Returns(RiskLevel.High); + // Test Case var unitUnderTest = CreateService(); diff --git a/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureRiskCalculationServiceTests.cs b/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureRiskCalculationServiceTests.cs new file mode 100644 index 000000000..8adac7f4d --- /dev/null +++ b/Covid19Radar/Tests/Covid19Radar.UnitTests/Services/ExposureRiskCalculationServiceTests.cs @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System.Collections.Generic; +using Chino; +using Covid19Radar.Services; +using Xunit; + +namespace Covid19Radar.UnitTests.Services +{ + public class ExposureRiskCalculationServiceTests + { + + public ExposureRiskCalculationServiceTests() + { + } + + private IExposureRiskCalculationService CreateService() + { + return new ExposureRiskCalculationService(); + } + + [Theory] + [InlineData(1999, RiskLevel.Low)] + [InlineData(2000, RiskLevel.High)] + public void RiskExposureTest(double scoreSum, RiskLevel expected) + { + + var dailySummary = new DailySummary() + { + DateMillisSinceEpoch = 0, + DaySummary = new ExposureSummaryData() + { + ScoreSum = scoreSum + }, + ConfirmedClinicalDiagnosisSummary = new ExposureSummaryData(), + ConfirmedTestSummary = new ExposureSummaryData(), + RecursiveSummary = new ExposureSummaryData(), + SelfReportedSummary = new ExposureSummaryData() + }; + + var exposureWindows = new List() + { + new ExposureWindow() + { + CalibrationConfidence = CalibrationConfidence.High, + DateMillisSinceEpoch = 0, + Infectiousness = Infectiousness.High, + ReportType = ReportType.Unknown, + ScanInstances = new List() + } + }; + + IExposureRiskCalculationService service = CreateService(); + + RiskLevel result = service.CalcRiskLevel(dailySummary, exposureWindows); + + Assert.Equal(expected, result); + } + } +}