static CsvVenueRow CreateRow(Action <CsvVenueRow> configureRow)
            {
                var row = DataManagementFileHelper.CreateVenueUploadRows(rowCount: 1).Single();

                configureRow(row);
                return(row);
            }
        public async Task FileMatchesSchema_HeaderHasMissingColumn_ReturnsInvalidHeaderResult()
        {
            // Arrange
            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                Mock.Of <BlobServiceClient>(),
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var stream = DataManagementFileHelper.CreateCsvStream(csvWriter =>
            {
                // Miss out VENUE_NAME, POSTCODE
                csvWriter.WriteField("YOUR_VENUE_REFERENCE");
                csvWriter.WriteField("ADDRESS_LINE_1");
                csvWriter.WriteField("ADDRESS_LINE_2");
                csvWriter.WriteField("TOWN_OR_CITY");
                csvWriter.WriteField("COUNTY");
                csvWriter.WriteField("EMAIL");
                csvWriter.WriteField("PHONE");
                csvWriter.WriteField("WEBSITE");
                csvWriter.NextRecord();
            });

            // Act
            var(result, missingHeaders) = await fileUploadProcessor.FileMatchesSchema <CsvVenueRow>(stream);

            // Assert
            result.Should().Be(FileMatchesSchemaResult.InvalidHeader);
            missingHeaders.Should().BeEquivalentTo(new[]
            {
                "VENUE_NAME",
                "POSTCODE"
            });
        }
        [InlineData(1)]  // Less than valid row
        //[InlineData(99]  // More than valid row - we don't have a way of checking this currently
        public async Task FileMatchesSchema_RowHasIncorrectColumnCount_ReturnsInvalidRows(int columnCount)
        {
            // Arrange
            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                Mock.Of <BlobServiceClient>(),
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var stream = DataManagementFileHelper.CreateVenueUploadCsvStream(csvWriter =>
            {
                for (int i = 0; i < columnCount; i++)
                {
                    csvWriter.WriteField("value");
                }

                csvWriter.NextRecord();
            });

            // Act
            var(result, missingHeaders) = await fileUploadProcessor.FileMatchesSchema <CsvVenueRow>(stream);

            // Assert
            result.Should().Be(FileMatchesSchemaResult.InvalidRows);
        }
        public async Task ProcessCourseFile_RowHasErrors_SetStatusToProcessedWithErrors()
        {
            // Arrange
            var blobServiceClient = new Mock <BlobServiceClient>();

            blobServiceClient.Setup(mock => mock.GetBlobContainerClient(It.IsAny <string>())).Returns(Mock.Of <BlobContainerClient>());

            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                blobServiceClient.Object,
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var provider = await TestData.CreateProvider();

            var user = await TestData.CreateUser(providerId : provider.ProviderId);

            var learnAimRef = (await TestData.CreateLearningDelivery()).LearnAimRef;

            var(courseUpload, _) = await TestData.CreateCourseUpload(provider.ProviderId, user, UploadStatus.Created);

            var stream = DataManagementFileHelper.CreateCourseUploadCsvStream(
                // Empty record will always yield errors (but we will always have a valid LARS code at this point)
                new CsvCourseRow()
            {
                LearnAimRef = learnAimRef
            });

            // Act
            await fileUploadProcessor.ProcessCourseFile(courseUpload.CourseUploadId, stream);

            // Assert
            courseUpload = await WithSqlQueryDispatcher(
                dispatcher => dispatcher.ExecuteQuery(new GetCourseUpload()
            {
                CourseUploadId = courseUpload.CourseUploadId
            }));

            using (new AssertionScope())
            {
                courseUpload.UploadStatus.Should().Be(UploadStatus.ProcessedWithErrors);
                courseUpload.ProcessingCompletedOn.Should().Be(Clock.UtcNow);
                courseUpload.ProcessingStartedOn.Should().NotBeNull();
            }
        }
        public async Task ProcessVenueFile_AllRecordsValid_SetStatusToProcessedSuccessfully()
        {
            // Arrange
            var blobServiceClient = new Mock <BlobServiceClient>();

            blobServiceClient.Setup(mock => mock.GetBlobContainerClient(It.IsAny <string>())).Returns(Mock.Of <BlobContainerClient>());

            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                blobServiceClient.Object,
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var provider = await TestData.CreateProvider();

            var user = await TestData.CreateUser(providerId : provider.ProviderId);

            var(venueUpload, _) = await TestData.CreateVenueUpload(provider.ProviderId, user, UploadStatus.Created);

            var uploadRows = DataManagementFileHelper.CreateVenueUploadRows(rowCount: 3).ToArray();

            await WithSqlQueryDispatcher(dispatcher => AddPostcodeInfoForRows(dispatcher, uploadRows.ToDataUploadRowCollection()));

            var stream = DataManagementFileHelper.CreateVenueUploadCsvStream(uploadRows);

            // Act
            await fileUploadProcessor.ProcessVenueFile(venueUpload.VenueUploadId, stream);

            // Assert
            venueUpload = await WithSqlQueryDispatcher(
                dispatcher => dispatcher.ExecuteQuery(new GetVenueUpload()
            {
                VenueUploadId = venueUpload.VenueUploadId
            }));

            using (new AssertionScope())
            {
                venueUpload.UploadStatus.Should().Be(UploadStatus.ProcessedSuccessfully);
                venueUpload.LastValidated.Should().Be(Clock.UtcNow);
                venueUpload.ProcessingCompletedOn.Should().Be(Clock.UtcNow);
                venueUpload.ProcessingStartedOn.Should().NotBeNull();
            }
        }
        public async Task ProcessVenueFile_RowHasErrors_SetStatusToProcessedWithErrors()
        {
            // Arrange
            var blobServiceClient = new Mock <BlobServiceClient>();

            blobServiceClient.Setup(mock => mock.GetBlobContainerClient(It.IsAny <string>())).Returns(Mock.Of <BlobContainerClient>());

            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                blobServiceClient.Object,
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var provider = await TestData.CreateProvider();

            var user = await TestData.CreateUser(providerId : provider.ProviderId);

            var(venueUpload, _) = await TestData.CreateVenueUpload(provider.ProviderId, user, UploadStatus.Created);

            var stream = DataManagementFileHelper.CreateVenueUploadCsvStream(
                // Empty record will always yield errors
                new CsvVenueRow());

            // Act
            await fileUploadProcessor.ProcessVenueFile(venueUpload.VenueUploadId, stream);

            // Assert
            venueUpload = await WithSqlQueryDispatcher(
                dispatcher => dispatcher.ExecuteQuery(new GetVenueUpload()
            {
                VenueUploadId = venueUpload.VenueUploadId
            }));

            using (new AssertionScope())
            {
                venueUpload.UploadStatus.Should().Be(UploadStatus.ProcessedWithErrors);
                venueUpload.LastValidated.Should().Be(Clock.UtcNow);
                venueUpload.ProcessingCompletedOn.Should().Be(Clock.UtcNow);
                venueUpload.ProcessingStartedOn.Should().NotBeNull();
            }
        }
        public async Task ProcessCourseFile_LearnAimRefWithMissingLeadingZero_HasLeadingZeroAdded()
        {
            // Arrange
            var blobServiceClient = new Mock <BlobServiceClient>();

            blobServiceClient.Setup(mock => mock.GetBlobContainerClient(It.IsAny <string>())).Returns(Mock.Of <BlobContainerClient>());

            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                blobServiceClient.Object,
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var provider = await TestData.CreateProvider();

            var user = await TestData.CreateUser(providerId : provider.ProviderId);

            var(courseUpload, _) = await TestData.CreateCourseUpload(provider.ProviderId, user, UploadStatus.Created);

            var learningDelivery = await TestData.CreateLearningDelivery(learnAimRef : "01234567");

            var uploadRows = DataManagementFileHelper.CreateCourseUploadRows(learningDelivery.LearnAimRef.TrimStart('0'), rowCount: 1).ToArray();

            uploadRows[0].CourseName = string.Empty;

            var stream = DataManagementFileHelper.CreateCourseUploadCsvStream(uploadRows);

            // Act
            await fileUploadProcessor.ProcessCourseFile(courseUpload.CourseUploadId, stream);

            // Assert
            var rows = await WithSqlQueryDispatcher(async dispatcher =>
                                                    (await dispatcher.ExecuteQuery(new GetCourseUploadRows()
            {
                CourseUploadId = courseUpload.CourseUploadId,
                WithErrorsOnly = false
            })).Rows);

            rows.Single().LearnAimRef.Should().Be(learningDelivery.LearnAimRef);
        }
        public async Task ValidateLearnAimRefs_ReturnsExpectedResult()
        {
            // Arrange
            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                Mock.Of <BlobServiceClient>(),
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            // Add missing lars
            var learnAimRef = (await TestData.CreateLearningDelivery()).LearnAimRef;
            List <CsvCourseRow> courseUploadRows = DataManagementFileHelper.CreateCourseUploadRows(learnAimRef, 1).ToList();

            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows("", 1).ToList());
            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows(learnAimRef, 1).ToList());
            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows("    ", 1).ToList());

            // Add invalid and expired lars
            var expiredLearnAimRef        = (await TestData.CreateLearningDelivery(effectiveTo: DateTime.Now.AddDays(-1))).LearnAimRef;
            var expiredOperationalEndDate = (await TestData.CreateLearningDelivery(operationalEndDate: DateTime.Now.AddDays(-1))).LearnAimRef;

            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows("ABCDEFGH", 1).ToList());
            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows(expiredLearnAimRef, 1).ToList());
            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows("GHFEDCBA", 1).ToList());
            courseUploadRows.AddRange(DataManagementFileHelper.CreateCourseUploadRows(expiredOperationalEndDate, 1).ToList());

            var stream = DataManagementFileHelper.CreateCourseUploadCsvStream(courseUploadRows.ToArray());

            // Act
            var(missing, invalid, expired) = await fileUploadProcessor.ValidateLearnAimRefs(stream);

            // Assert
            missing.Should().BeEquivalentTo(new[]
            {
                3,
                5
            });
            invalid.Should().BeEquivalentTo(new[]
        public async Task ValidateCourseUploadRows_RowsHaveNoLarsCode_AreNotGrouped()
        {
            // Arrange
            var provider = await TestData.CreateProvider();

            var user = await TestData.CreateUser(providerId : provider.ProviderId);

            var(courseUpload, _) = await TestData.CreateCourseUpload(provider.ProviderId, createdBy : user, null);

            var learnAimRef = (await TestData.CreateLearningDelivery()).LearnAimRef;

            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                Mock.Of <BlobServiceClient>(),
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var rows = DataManagementFileHelper.CreateCourseUploadRows(learnAimRef, rowCount: 2).ToArray();

            rows[0].LearnAimRef = string.Empty;
            rows[1].LearnAimRef = string.Empty;

            var uploadRows = rows.ToDataUploadRowCollection();

            await WithSqlQueryDispatcher(async dispatcher =>
            {
                // Act
                var(_, rows) = await fileUploadProcessor.ValidateCourseUploadRows(
                    dispatcher,
                    courseUpload.CourseUploadId,
                    provider.ProviderId,
                    uploadRows);

                // Assert
                rows.First().CourseId.Should().NotBe(rows.Last().CourseId);
            });
        }
        public async Task ValidateVenueUploadFile_PostcodeIsNotInEngland_InsertsExpectedOutsideOfEnglandValueIntoDb()
        {
            // Arrange
            var provider = await TestData.CreateProvider();

            var user = await TestData.CreateUser(providerId : provider.ProviderId);

            var(venueUpload, _) = await TestData.CreateVenueUpload(provider.ProviderId, createdBy : user, UploadStatus.Processing);

            var fileUploadProcessor = new FileUploadProcessor(
                SqlQueryDispatcherFactory,
                Mock.Of <BlobServiceClient>(),
                Clock,
                new RegionCache(SqlQueryDispatcherFactory),
                new ExecuteImmediatelyBackgroundWorkScheduler(Fixture.ServiceScopeFactory));

            var uploadRows = DataManagementFileHelper.CreateVenueUploadRows(rowCount: 1).ToDataUploadRowCollection();

            await WithSqlQueryDispatcher(async dispatcher =>
            {
                await AddPostcodeInfoForRows(dispatcher, uploadRows, inEngland: false);

                // Act
                await fileUploadProcessor.ValidateVenueUploadFile(
                    dispatcher,
                    venueUpload.VenueUploadId,
                    venueUpload.ProviderId,
                    uploadRows);

                var(rows, _) = await dispatcher.ExecuteQuery(new GetVenueUploadRows()
                {
                    VenueUploadId = venueUpload.VenueUploadId
                });

                rows.First().IsValid.Should().BeTrue();
                rows.First().OutsideOfEngland.Should().BeTrue();
            });
        }