Esempio n. 1
0
        public async Task Get_by_id_returns_job_when_it_exists()
        {
            var fixture = new Fixture().Customize(new DownloaderCustomization());
            var job     = fixture.Create <DownloadJob>();
            var jobs    = new DownloadJobsDictionary {
                [job.Id] = job
            };

            using var client =
                      _factory
                      .WithServices(services =>
                                    services.ReplaceAllWithSingleton(jobs))
                      .CreateDefaultClient();

            using var response = await client.GetAsync($"{ApiDownloadRoute}/{job.Id}");

            response.IsSuccessStatusCode.Should().BeTrue();
            var content = await response.Content.ReadFromJsonAsync <DownloadController.GetResponseDto>();

            content.Should()
            .NotBeNull()
            .And.Be(
                new DownloadController.GetResponseDto(
                    job.Id,
                    job.Link,
                    job.SaveAsFile.Name,
                    job.Status.ToString(),
                    job.CreatedTicks,
                    job.TotalBytes,
                    job.BytesDownloaded));
        }
Esempio n. 2
0
        private HttpClient CreateClient(
            DownloadJob.JobId newDownloadId,
            DownloadJobsDictionary downloadJobsDictionary,
            IFileSystem fileSystem,
            DelegatingHandler downloadsHttpClient,
            IHubContext <NotificationsHub, NotificationsHub.IClient> notificationsHubContext)
        {
            return
                (_factory
                 .WithSettings(
                     ("DownloadDirectories:Incomplete", "/incomplete"),
                     ("DownloadDirectories:Completed", "/completed"),

                     /* One millisecond interval to give ProgressNotificationsBackgroundService a chance to iterate
                      * through dictionary while the test is executing - this can potentially cause a flaky test.
                      * See SignalRMessagesShouldBeSent() verification for SendProgress in the Assert block below. */
                     ("PushNotifications:ProgressIntervalInMilliseconds", "1"))
                 .WithServices(services =>
                               services
                               .ReplaceAllWithSingleton(new DownloadManager.DownloadIdGenerator(() => newDownloadId))
                               .ReplaceAllWithSingleton(new DownloadManager.DateTimeUtcNowTicks(() => 42))
                               .ReplaceAllWithSingleton(downloadJobsDictionary)
                               .ReplaceAllWithSingleton(fileSystem)
                               .ReplaceAllWithSingleton(notificationsHubContext)
                               .ReplaceHttpMessageHandlerFor <DownloadStarter>(downloadsHttpClient))
                 .CreateDefaultClient());
        }
Esempio n. 3
0
        public async Task Delete_removes_completed_and_failed_jobs()
        {
            /* Arrange */
            var fixture                = new Fixture().Customize(new DownloaderCustomization());
            var runningJob             = fixture.Create <DownloadJob>();
            var downloadingHandlerStub = fixture.Create <Mock <DelegatingHandler> >();

            downloadingHandlerStub
            .Protected()
            .As <IProtectedDelegatingHandler>()
            .Setup(h => h.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()))
            .Returns(async() => {
                const int forever = -1;
                await Task.Delay(forever);
                throw new InvalidOperationException("Unreachable code");
            });
            using var httpClient1 = new HttpClient(downloadingHandlerStub.Object);
            runningJob.Start(httpClient1);
            var completedJob         = fixture.Create <DownloadJob>();
            var completedHandlerStub = fixture.Create <Mock <DelegatingHandler> >();

            completedHandlerStub
            .Protected()
            .As <IProtectedDelegatingHandler>()
            .Setup(h => h.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage());
            using var completedJobHttpClient = new HttpClient(completedHandlerStub.Object);
            completedJob.Start(completedJobHttpClient);
            await completedJob.DownloadTask;
            var failedJob         = fixture.Create <DownloadJob>();
            var failedHandlerStub = fixture.Create <Mock <DelegatingHandler> >();

            failedHandlerStub
            .Protected()
            .As <IProtectedDelegatingHandler>()
            .Setup(h => h.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()))
            .ThrowsAsync(new Exception());
            using var failedJobHttpClient = new HttpClient(failedHandlerStub.Object);
            failedJob.Start(failedJobHttpClient);
            await failedJob.DownloadTask.AwaitIgnoringExceptions();

            var jobs = new DownloadJobsDictionary {
                [runningJob.Id] = runningJob, [completedJob.Id] = completedJob
            };

            using var client =
                      _factory
                      .WithServices(services =>
                                    services.ReplaceAllWithSingleton(jobs))
                      .CreateDefaultClient();

            /* Act */
            using var response = await client.DeleteAsync(ApiDownloadRoute);

            /* Assert */
            response.IsSuccessStatusCode.Should().BeTrue();
            jobs.Should()
            .NotContainKeys(completedJob.Id, failedJob.Id)
            .And.ContainKey(runningJob.Id);
        }
Esempio n. 4
0
 public DownloadManager(
     DownloadIdGenerator newGuid,
     DateTimeUtcNowTicks getTicks,
     DownloadJobsDictionary jobs,
     DownloadTaskFactory downloadTaskFactory)
 {
     _newGuid             = newGuid;
     _getTicks            = getTicks;
     _jobs                = jobs;
     _downloadTaskFactory = downloadTaskFactory;
 }
Esempio n. 5
0
 public DownloadController(
     DownloadManager downloadManager,
     NotificationsManager notificationsManager,
     DownloadStarter downloadStarter,
     CompletedDownloadsDirectory completedDownloadsDirectory,
     DownloadJobsDictionary jobs)
 {
     _downloadManager             = downloadManager;
     _notificationsManager        = notificationsManager;
     _downloadStarter             = downloadStarter;
     _completedDownloadsDirectory = completedDownloadsDirectory;
     _jobs = jobs;
 }
Esempio n. 6
0
        public async Task Post_sends_failure_notification_if_download_task_fails()
        {
            /* Arrange */
            var fixture                     = new Fixture().Customize(new AutoMoqCustomization());
            var newDownloadId               = fixture.Create <DownloadJob.JobId>();
            var downloadJobsDictionary      = new DownloadJobsDictionary();
            var fileSystemStub              = CreateAndSetupFileSystemMock(fixture, newDownloadId);
            var notificationsHubContextMock =
                fixture.Create <Mock <IHubContext <NotificationsHub, NotificationsHub.IClient> > >();
            var downloadHttpClientStub = fixture.Create <Mock <DelegatingHandler> >();

            downloadHttpClientStub
            .Protected()
            .As <IProtectedDelegatingHandler>()
            .Setup(h =>
                   h.SendAsync(
                       It.Is <HttpRequestMessage>(r =>
                                                  r.Method == HttpMethod.Get &&
                                                  r.RequestUri !.OriginalString == "https://download.stuff/file.iso"),
                       It.IsAny <CancellationToken>()))
            .ThrowsAsync(new Exception("Something went wrong!"));
            using var client =
                      CreateClient(
                          newDownloadId,
                          downloadJobsDictionary,
                          fileSystemStub.Object,
                          downloadHttpClientStub.Object,
                          notificationsHubContextMock.Object);

            /* Act */
            var response =
                await client.PostAsync(
                    ApiDownloadRoute,
                    JsonContent.Create(
                        new DownloadController.PostRequestDto(
                            "https://download.stuff/file.iso",
                            "saveAsFile.iso")));

            /* Assert */
            response.IsSuccessStatusCode.Should().BeTrue();
            var newDownload = downloadJobsDictionary[newDownloadId];
            /* To observe the results we need to simulate completion of the downloading task */
            await newDownload.DownloadTask.AwaitIgnoringExceptions();

            notificationsHubContextMock.Verify(h =>
                                               h.Clients.All.SendFailed(
                                                   It.Is <NotificationsHub.FailedMessage>(m =>
                                                                                          m.Id == newDownloadId &&
                                                                                          m.Reason == "Something went wrong!")));
        }
Esempio n. 7
0
        public async Task Get_returns_contents_of_DownloadJobsDictionary()
        {
            var fixture = new Fixture().Customize(new DownloaderCustomization());
            var job1    = fixture.Create <DownloadJob>();
            var job2    = fixture.Create <DownloadJob>();
            var jobs    = new DownloadJobsDictionary {
                [job1.Id] = job1, [job2.Id] = job2
            };

            using var client =
                      _factory
                      .WithServices(services =>
                                    services.ReplaceAllWithSingleton(jobs))
                      .CreateDefaultClient();

            using var response = await client.GetAsync(ApiDownloadRoute);

            response.IsSuccessStatusCode.Should().BeTrue();
            var content = await response.Content.ReadFromJsonAsync <DownloadController.GetResponseDto[]>();

            content.Should()
            .NotBeNullOrEmpty()
            .And.Contain(
                new DownloadController.GetResponseDto(
                    job1.Id,
                    job1.Link,
                    job1.SaveAsFile.Name,
                    job1.Status.ToString(),
                    job1.CreatedTicks))
            .And.Contain(
                new DownloadController.GetResponseDto(
                    job2.Id,
                    job2.Link,
                    job2.SaveAsFile.Name,
                    job2.Status.ToString(),
                    job2.CreatedTicks))
            .And.Subject.Count().Should().Be(2);
        }
Esempio n. 8
0
        public async Task Post_starts_download_and_saves_to_file_and_sends_notifications()
        {
            /* Arrange */
            var fixture = new Fixture().Customize(new AutoMoqCustomization());
            var downloadHttpClientStub      = CreateAndSetupDownloadHttpClientStub(fixture);
            var newDownloadId               = fixture.Create <DownloadJob.JobId>();
            var fileSystemMock              = CreateAndSetupFileSystemMock(fixture, newDownloadId);
            var downloadJobsDictionary      = new DownloadJobsDictionary();
            var notificationsHubContextMock =
                fixture.Create <Mock <IHubContext <NotificationsHub, NotificationsHub.IClient> > >();

            using var client =
                      CreateClient(
                          newDownloadId,
                          downloadJobsDictionary,
                          fileSystemMock.Object,
                          downloadHttpClientStub.Object,
                          notificationsHubContextMock.Object);

            /* Act */
            var response =
                await client.PostAsync(
                    ApiDownloadRoute,
                    JsonContent.Create(
                        new DownloadController.PostRequestDto(
                            "https://download.stuff/file.iso",
                            "saveAsFile.iso")));

            /* Assert */
            response.IsSuccessStatusCode.Should().BeTrue();
            await ResponseShouldContainNewDownloadId();

            downloadJobsDictionary[newDownloadId].CreatedTicks.Should().Be(42);
            await DownloadContentsShouldBeSavedToTemporaryFile();

            TemporaryFileShouldBeMovedToSaveAsFile();
            SignalRMessagesShouldBeSent();

            async Task ResponseShouldContainNewDownloadId()
            {
                var responseContent = await response.Content.ReadFromJsonAsync <Guid>();

                responseContent.Should().Be(newDownloadId.Value);
            }

            async Task DownloadContentsShouldBeSavedToTemporaryFile()
            {
                var newDownload = downloadJobsDictionary[newDownloadId];
                /* To observe the results we need to simulate completion of the downloading task */
                await newDownload.DownloadTask;

                fileSystemMock.Verify(fs =>
                                      fs.FileStream
                                      .Create($"/incomplete/{newDownloadId}", FileMode.CreateNew)
                                      .WriteAsync(
                                          It.Is <ReadOnlyMemory <byte> >(b =>
                                                                         Encoding.Default.GetString(b.ToArray()).Equals("file contents")),
                                          It.IsAny <CancellationToken>()));
            }

            void TemporaryFileShouldBeMovedToSaveAsFile()
            {
                fileSystemMock.Verify(fs =>
                                      fs.File.Move(
                                          $"/incomplete/{newDownloadId}",
                                          /* Test that name of the file has incremented index because saveAsFile.iso already exists */
                                          "/completed/saveAsFile(1).iso",
                                          false));
            }

            void SignalRMessagesShouldBeSent()
            {
                notificationsHubContextMock.Verify(h =>
                                                   h.Clients.All.SendTotalBytes(
                                                       It.Is <NotificationsHub.TotalBytesMessage>(m =>
                                                                                                  m.Id == newDownloadId &&
                                                                                                  m.TotalBytes == 42)));
                notificationsHubContextMock.Verify(h =>
                                                   h.Clients.All.SendFinished(
                                                       It.Is <NotificationsHub.FinishedMessage>(m =>
                                                                                                m.Id == newDownloadId &&
                                                                                                m.FileName == "saveAsFile(1).iso")));
                notificationsHubContextMock.Verify(h =>
                                                   h.Clients.All.SendProgress(
                                                       It.Is <IEnumerable <NotificationsHub.ProgressMessage> >(m =>
                                                                                                               m.Any(p => p.Id == newDownloadId)),
                                                       It.IsAny <CancellationToken>()));
            }
        }