public void PullKeysParams_PullFirstTime_ConsentGiven()
        {
            //Given the app never pulled before, but consent was given to pull EU keys.
            _preferences.Clear();
            OnboardingStatusHelper.Status = OnboardingStatus.CountriesOnboardingCompleted;

            //When the app pulls for the first time
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //We will request all keys for today but only NO keys (consent was not given yet)
            Assert.Equal(SystemTime.Now().Date, pullParams.Date);
            Assert.Equal(1, pullParams.BatchNumber);
            Assert.Equal(BatchType.ALL, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
        public void PullKeysParams_PullFirstTime()
        {
            //Given the app never pulled before (preferences will be empty)
            _preferences.Clear();
            DateTime today = new DateTime(2020, 4, 4, 8, 8, 8);

            SystemTime.SetDateTime(today);

            //When the app pulls for the first time
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //We will request all keys for today
            Assert.Equal(today.Date, pullParams.Date);
            Assert.Equal(1, pullParams.BatchNumber);
            Assert.Equal(BatchType.ALL, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
        public void PullKeysParams_LastPullWas15DaysAgo()
        {
            //Given that last time we pulled keys was 20 days ago and the last batch was number 2 for that day
            DateTime lastPullDate = SystemTime.Now().AddDays(-20).Date;

            _preferences.Clear();
            _preferences.Set(PULL_DATE_KEY, lastPullDate.ToUniversalTime());
            _preferences.Set(PULL_BATCH_KEY, 2);

            //When the app pulls
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //Then it pulls only for the last 14 days, incl. today.
            Assert.Equal(SystemTime.Now().AddDays(-13).Date, pullParams.Date);
            Assert.Equal(1, pullParams.BatchNumber);
            Assert.Equal(BatchType.ALL, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
Пример #4
0
        public void PullKeysParams_NotFirstPullOfDay()
        {
            //Given that we previously successfully pulled keys today and the last batch was number 3.
            DateTime lastPullDate = SystemTime.Now().Date;

            _preferences.Clear();
            _preferences.Set(PULL_DATE_KEY, lastPullDate.ToUniversalTime());
            _preferences.Set(PULL_BATCH_KEY, 3);

            //When the app pulls
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //Then the next batch number for today will be requested
            Assert.Equal(lastPullDate, pullParams.Date);
            Assert.Equal(4, pullParams.BatchNumber);
            Assert.Equal(BatchType.NO, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
Пример #5
0
        public void PullKeysParams_LastPullWasThreeDaysAgo()
        {
            //Given that last time we pulled keys was three days ago and the last batch was number 2 for that day
            DateTime lastPullDate = SystemTime.Now().AddDays(-3).Date;

            _preferences.Clear();
            _preferences.Set(PULL_DATE_KEY, lastPullDate.ToUniversalTime());
            _preferences.Set(PULL_BATCH_KEY, 2);

            //When the app pulls
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //Then the next batch number for three days ago will be requested
            Assert.Equal(lastPullDate, pullParams.Date);
            Assert.Equal(3, pullParams.BatchNumber);
            Assert.Equal(BatchType.NO, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
        public void PullKeysParams_NotFirstPull()
        {
            //App was just updated to pull EU keys, and the last pull was NO keys.
            DateTime lastPullDate = SystemTime.Now().Date;

            _preferences.Clear();
            _preferences.Set(PULL_DATE_KEY, lastPullDate.ToUniversalTime());
            _preferences.Set(PULL_BATCH_KEY, 3);
            _preferences.Set(PULL_BATCH_TYPE, "no");

            //When the app pulls for the first time
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //We will request all keys for today
            Assert.Equal(SystemTime.Now().Date, pullParams.Date);
            Assert.Equal(1, pullParams.BatchNumber);
            Assert.Equal(BatchType.ALL, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
Пример #7
0
        public void PullKeysParams_NotFirstPull_ConsentGiven()
        {
            //Given consent was just given to pull EU keys, and the last pull was NO keys.
            DateTime lastPullDate = SystemTime.Now().Date;

            _preferences.Clear();
            OnboardingStatusHelper.Status = OnboardingStatus.CountriesOnboardingCompleted;
            _preferences.Set(PULL_DATE_KEY, lastPullDate.ToUniversalTime());
            _preferences.Set(PULL_BATCH_KEY, 3);
            _preferences.Set(PULL_BATCH_TYPE, "no");

            //When the app pulls for the first time
            PullKeysParams pullParams = PullKeysParams.GenerateParams();

            //We will request all keys for today but only NO keys (consent was not given yet)
            Assert.Equal(SystemTime.Now().Date, pullParams.Date);
            Assert.Equal(1, pullParams.BatchNumber);
            Assert.Equal(BatchType.ALL, pullParams.BatchType);

            //Reset
            SystemTime.ResetDateTime();
        }
        public void PullKeysParams_PullAtTimes(string time)
        {
            //Given that the current time is as follows, and the app never pulled before
            DateTime today      = SystemTime.Now().Date;
            string   dateString = today.ToString("yyyy-MM-dd", CultureInfo.GetCultureInfo("nn-NO"));
            DateTime newNow     = DateTime.ParseExact($"{dateString} {time}", "yyyy-MM-dd HH:mm z", CultureInfo.GetCultureInfo("nn-NO"));

            SystemTime.SetDateTime(newNow);

            _preferences.Clear();

            //When the app pulls
            PullKeysParams pullParams      = PullKeysParams.GenerateParams();
            string         pullQueryParams = pullParams.ToBatchFileRequest();

            //The date will be parsed right (UTC)
            string expectedDateString = SystemTime.Now().ToUniversalTime().Date.ToString("yyyy-MM-dd");

            Assert.Equal($"{expectedDateString}_1_all.zip", pullQueryParams);

            //Reset
            SystemTime.ResetDateTime();
        }
Пример #9
0
        public void PullKeysParams_LastPullWasAtTimes(string time, int daysAgo, int expectedDaysAgo)
        {
            //Given that last time we pulled keys was at given time
            DateTime today        = SystemTime.Now();
            string   dateString   = today.AddDays(daysAgo).ToString("yyyy-MM-dd", CultureInfo.GetCultureInfo("nn-NO"));
            DateTime lastPullDate = DateTime.ParseExact($"{dateString} {time}", "yyyy-MM-dd HH:mm z", CultureInfo.GetCultureInfo("nn-NO"));

            _preferences.Clear();
            _preferences.Set(PULL_DATE_KEY, lastPullDate.ToUniversalTime());
            _preferences.Set(PULL_BATCH_KEY, 2);

            //When the app pulls
            PullKeysParams pullParams      = PullKeysParams.GenerateParams();
            string         pullQueryParams = pullParams.ToBatchFileRequest();

            //The date will be parsed right (UTC time)
            string expectedDateString = today.AddDays(expectedDaysAgo).ToString("yyyy-MM-dd");

            Assert.Equal($"{expectedDateString}_3_no.zip", pullQueryParams);

            //Reset
            SystemTime.ResetDateTime();
        }
Пример #10
0
        /// <summary>
        /// Fetches the new keys if any.
        /// </summary>
        /// <returns>Paths to the temporary location where the zips are stored</returns>
        public async Task <IEnumerable <string> > PullNewKeys(ExposureNotificationWebService service, CancellationToken cancellationToken)
        {
            PullKeysParams requestParams = PullKeysParams.GenerateParams();

            LocalPreferencesHelper.DidFirstFileOfTheDayEndedWith204 = false;

            List <string> zipLocations      = new List <string>();
            bool          lastPull          = false;
            int?          lastBatchReceived = null;
            int?          lastReceivedStatusCodeFromRequest = null;

            while (!lastPull)
            {
                string requestUrl = requestParams.ToBatchFileRequest();

                ApiResponse <Stream> response = await service.GetDiagnosisKeys(requestUrl, cancellationToken);

                HttpHeaders headers = response.Headers;
                lastReceivedStatusCodeFromRequest = response.StatusCode;
                bool headersAreValid = true;

                if (response == null || (!response.IsSuccessfull))
                {
                    if (response?.StatusCode == 410)
                    {
                        NotificationsHelper.CreateNotification(NotificationsEnum.ApiDeprecated, 0);
                        string warning = "410 Api was deprecated";
                        _developerTools.AddToPullHistoryRecord(warning, requestUrl);
                        LogUtils.LogMessage(LogSeverity.WARNING, $"{_logPrefix}.{nameof(DownloadZips)}: {warning}");
                    }
                    else
                    {
                        //Failed to fetch new keys due to server error. This is already logged in the webservice.
                        _developerTools.AddToPullHistoryRecord($"{response.StatusCode} Server Error", requestUrl);
                    }
                    break; //Abort pulling
                }

                // If the server says 204: No Content, it means that there were no new keys (I.e. the request batch does not exist)
                if (response.StatusCode == 204)
                {
                    if (requestParams.Date.Date < SystemTime.Now().Date)
                    {
                        //If there were no new keys for a day which is not today, then move on to fetch keys for the next date.
                        requestParams.Date        = requestParams.Date.AddDays(1);
                        requestParams.BatchNumber = 1;
                        lastPull = false;
                    }
                    else
                    {
                        //There were no new keys to fetch for today
                        _developerTools.AddToPullHistoryRecord($"204 No Content - No new keys", requestUrl);
                        string warning = $"API {response.Endpoint} returned 204 No Content - No new keys since last pull";
                        LogUtils.LogMessage(LogSeverity.WARNING, $"{_logPrefix}.{nameof(DownloadZips)}: {warning}");
                        lastPull = true;
                    }
                }
                else
                {
                    try
                    {
                        int  lastBatchReceivedValue = int.Parse(headers.GetValues(LastBatchReturnedHeader).First());
                        bool moreBatchesExist       = bool.Parse(headers.GetValues(MoreBatchesExistHeader).First());

                        //If both headers parse (no exceptions), then save lastBatchValue to be persisted
                        lastBatchReceived = lastBatchReceivedValue;

                        if (moreBatchesExist)
                        {
                            //There are still more batches to fetch for the given date
                            requestParams.BatchNumber = (int)lastBatchReceived + 1;
                            lastPull = false;
                        }
                        else if (requestParams.Date.Date < SystemTime.Now().Date)
                        {
                            //If there were no new keys for a day which is not today, then move on to fetch keys for the next date.
                            requestParams.Date        = requestParams.Date.AddDays(1);
                            requestParams.BatchNumber = 1;
                            lastPull = false;
                        }
                        else
                        {
                            //There are no more batches to fetch for today. Try again in some hours.
                            lastPull = true;
                        }
                    }
                    catch (Exception e)
                    {
                        headersAreValid = false;
                        HandleErrorWhenPulling(e, $"Failed to parse {MoreBatchesExistHeader} or {LastBatchReturnedHeader} header.", requestUrl);
                        break; //Abort pulling
                    }
                }

                // Copy the zip stream in the response into a temp file
                if (response.StatusCode == 200 && headersAreValid)
                {
                    try
                    {
                        _developerTools.AddToPullHistoryRecord("200 OK", requestUrl);
                        string tmpFile = Path.Combine(ServiceLocator.Current.GetInstance <IFileSystem>().CacheDirectory, Guid.NewGuid() + ".zip");

                        FileStream tmpFileStream = File.Create(tmpFile);
                        await response.Data.CopyToAsync(tmpFileStream);

                        tmpFileStream.Close();

                        zipLocations.Add(tmpFile);
                    }
                    catch (Exception e)
                    {
                        HandleErrorWhenPulling(e, "Failed to save zip locally", requestUrl);
                        break; //Abort pulling
                    }
                }
            }

            if (zipLocations.Any() && lastBatchReceived != null)
            {
                //Persist the last batch that was fetched, to know which one to fetch next time the background task runs.
                LocalPreferencesHelper.LastPullKeysBatchNumberNotSubmitted = (int)lastBatchReceived;

                //Also save the last batchtype fetched
                LocalPreferencesHelper.LastPulledBatchType = requestParams.BatchType;
            }

            // Edge case for when pulling across multiple days ends up in 204 for the first file
            if (requestParams.Date.Date == SystemTime.Now().Date &&
                requestParams.BatchNumber == 1 &&
                lastReceivedStatusCodeFromRequest == 204)
            {
                LocalPreferencesHelper.DidFirstFileOfTheDayEndedWith204 = true;
            }

            return(zipLocations);
        }
Пример #11
0
        public async void PullKeys_NewTermsWereApproved()
        {
            int lastBatchNumNO  = 4;
            int lastBatchNumALL = 5;

            //Both NO and EU keys exist for the last 3 days
            ExposureNotificationWebService mockedService = _helper.MockedService(new List <PullKeysMockData>
            {
                new PullKeysMockData(day1, 1, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(1).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day1, 2, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(2).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day1, 3, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(3).WithMoreBatchesExistHeader(false),
                new PullKeysMockData(day1, 4, BatchType.NO).HttpStatusCode(204),
                new PullKeysMockData(day1, 1, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(1).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day1, 2, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(2).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day1, 3, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(3).WithMoreBatchesExistHeader(false),
                new PullKeysMockData(day1, 4, BatchType.ALL).HttpStatusCode(204),

                new PullKeysMockData(day2, 1, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(1).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day2, 2, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(2).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day2, 3, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(3).WithMoreBatchesExistHeader(false),
                new PullKeysMockData(day2, 4, BatchType.NO).HttpStatusCode(204),
                new PullKeysMockData(day2, 1, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(1).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day2, 2, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(2).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day2, 3, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(3).WithMoreBatchesExistHeader(false),
                new PullKeysMockData(day2, 4, BatchType.ALL).HttpStatusCode(204),

                new PullKeysMockData(day3, 1, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(1).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day3, 2, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(2).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day3, 3, BatchType.NO).HttpStatusCode(200).WithLastBatchHeader(lastBatchNumNO).WithMoreBatchesExistHeader(false),
                new PullKeysMockData(day3, 4, BatchType.NO).HttpStatusCode(204),
                new PullKeysMockData(day3, 5, BatchType.NO).HttpStatusCode(204),
                new PullKeysMockData(day3, 1, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(1).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day3, 2, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(2).WithMoreBatchesExistHeader(true),
                new PullKeysMockData(day3, 3, BatchType.ALL).HttpStatusCode(200).WithLastBatchHeader(lastBatchNumALL).WithMoreBatchesExistHeader(false),
                new PullKeysMockData(day3, 4, BatchType.ALL).HttpStatusCode(204),
                new PullKeysMockData(day3, 5, BatchType.NO).HttpStatusCode(204),
            });

            //Given today is day2 AND that the new terms have not been approved.
            SystemTime.SetDateTime(day3);

            //Given last time we pulled was day1, batch1.
            _helper.SetLastPulledDate(day1, 1);

            //When pulling keys
            List <string> zipLocations = (await new ZipDownloader().PullNewKeys(mockedService, new CancellationToken())).ToList();

            //Then we pull all NO keys
            Assert.Equal(8, zipLocations.Count);
            Assert.Equal(lastBatchNumNO, LocalPreferencesHelper.LastPullKeysBatchNumberNotSubmitted); //The last batch number is saved from header
            Assert.False((await _logManager.GetLogs(10)).Any());                                      //And no errors were logged

            //Prepare for new pull
            LocalPreferencesHelper.UpdateLastPullKeysSucceededDateTime();
            await _logManager.DeleteAll();

            _developerTools.ClearAllFields();

            //When accepting the term
            OnboardingStatusHelper.Status = OnboardingStatus.CountriesOnboardingCompleted;

            //And then pull again
            zipLocations = (await new ZipDownloader().PullNewKeys(mockedService, new CancellationToken())).ToList();

            //Then we pull all EU keys but only for today
            Assert.Equal(3, zipLocations.Count);
            Assert.Equal(lastBatchNumALL, LocalPreferencesHelper.LastPullKeysBatchNumberNotSubmitted); //The last batch number is saved from header
            Assert.False((await _logManager.GetLogs(10)).Any());                                       //And no errors were logged

            //Next time it will also pull EU keys:
            LocalPreferencesHelper.UpdateLastPullKeysSucceededDateTime();
            PullKeysParams newParams = PullKeysParams.GenerateParams();

            Assert.Equal(BatchType.ALL, newParams.BatchType);
        }