示例#1
0
            public void ShouldBeAbleToExplicitlyStart()
            {
                // Arrange
                using var arena = new TestArena("-e", "mysql");
                // Act
                arena.WaitForProgramToListen();
                var interestingLine = arena.StdOut.FirstOrDefault(
                    line => line.StartsWith(
                        "connection string",
                        StringComparison.InvariantCultureIgnoreCase
                        ));

                Expect(interestingLine)
                .Not.To.Be.Null(
                    "TempDb runner should emit the connection string on stdout"
                    );
                var connectionString = interestingLine.Split(':')
                                       .Skip(1)
                                       .JoinWith(":")
                                       .Trim();

                using (var connection = new MySqlConnection(connectionString))
                {
                    // Assert
                    Expect(() => connection.Open())
                    .Not.To.Throw();
                }
            }
示例#2
0
                    public void ShouldNotReWriteTheFileWhenInHistoryButNotOnDisk()
                    {
                        // Arrange
                        using var arena     = new TestArena();
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath = GetRandomString(2);
                        var data    = GetRandomBytes(100);

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            data);
                        var historyRepo = Substitute.For <ITargetHistoryRepository>();

                        historyRepo.Exists(relPath)
                        .Returns(true);
                        var targetFilePath = Path.Combine(arena.TargetPath, relPath);
                        var historyItem    = new HistoryItem(
                            relPath,
                            data.Length);

                        historyRepo.Find(relPath)
                        .Returns(historyItem);

                        var sut = Create(targetHistoryRepository: historyRepo);

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        Expect(targetFilePath)
                        .Not.To.Be.A.File();
                    }
示例#3
0
                    public void ShouldCopyTheSourceFileToTarget()
                    {
                        // Arrange
                        using var arena     = new TestArena();
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath = "some-file.ext";
                        var data    = GetRandomBytes();

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            data);
                        var sut = Create(filters: CreatePermissiveFilter());

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        var inTarget = target.ListResourcesRecursive();

                        Expect(inTarget)
                        .To.Contain.Only(1)
                        .Item();
                        var copied = inTarget.Single();

                        Expect(copied.RelativePath)
                        .To.Equal(relPath);
                        Expect(copied)
                        .To.Have.Data(data);
                    }
示例#4
0
                    public void ShouldNotReWriteTheFile()
                    {
                        // Arrange
                        using var arena     = new TestArena();
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath        = GetRandomString(2);
                        var data           = GetRandomBytes(100);
                        var targetFilePath = arena.CreateResource(
                            arena.TargetPath,
                            relPath,
                            data);

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            data);
                        var beforeTest = DateTime.Now;
                        var lastWrite  = beforeTest.AddMinutes(GetRandomInt(-100, -1));

                        File.SetLastWriteTime(targetFilePath, lastWrite);

                        var sut = Create();

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        Expect(targetFilePath)
                        .To.Be.A.File();
                        var targetInfo = new FileInfo(targetFilePath);

                        Expect(targetInfo.LastWriteTime)
                        .To.Be.Less.Than(beforeTest);
                    }
示例#5
0
                    public void ShouldUpsertHistory()
                    {
                        // Arrange
                        using var arena     = new TestArena();
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath = "some-file.ext";
                        var data    = GetRandomBytes();

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            data);
                        var historyRepo = Substitute.For <ITargetHistoryRepository>();
                        var sut         = Create(
                            targetHistoryRepository: historyRepo,
                            filters: CreatePermissiveFilter());

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        Expect(historyRepo)
                        .To.Have.Received(1)
                        .Upsert(Arg.Is <IHistoryItem>(
                                    o => o.Path == relPath &&
                                    o.Size == data.Length
                                    ));
                    }
示例#6
0
                    public void ShouldPassThroughAllProvidedIntermediatesInOrder()
                    {
                        // Arrange
                        using var arena = new TestArena();
                        var resumeStrategy = Substitute.For <IResumeStrategy>();

                        resumeStrategy.CanResume(
                            Arg.Any <IFileResource>(),
                            Arg.Any <IFileResource>(),
                            Arg.Any <Stream>(),
                            Arg.Any <Stream>())
                        .Returns(false);
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath = GetRandomString();
                        var allData = GetRandomBytes(100, 200); // small buffer, likely to be read in one pass

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            allData);
                        var intermediateCalls = new List <string>();
                        var endCalls          = new List <string>();
                        var intermediate1     = new GenericPassThrough(
                            (data, count) => intermediateCalls.Add("first"),
                            () => endCalls.Add("first")
                            );
                        var intermediate2 = new GenericPassThrough(
                            (data, count) => intermediateCalls.Add("second"),
                            () => endCalls.Add("second")
                            );
                        var intermediate3 = new GenericPassThrough(
                            (data, count) => intermediateCalls.Add("third"),
                            () => endCalls.Add("third")
                            );
                        var expected = new[]
                        {
                            "first",
                            "second",
                            "third"
                        };
                        var sut = Create(
                            resumeStrategy,
                            new IPassThrough[]
                        {
                            intermediate1, intermediate2, intermediate3
                        },
                            // this test is about notifiers, so we'll
                            // just pretend that all sources are to be required
                            // at the target
                            filters: CreatePermissiveFilter());

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        Expect(intermediateCalls)
                        .To.Equal(expected);
                        Expect(endCalls)
                        .To.Equal(expected);
                    }
示例#7
0
                    public void ShouldResumeWhenResumeStrategySaysYes()
                    {
                        // Arrange
                        using var arena = new TestArena();
                        var resumeStrategy = Substitute.For <IResumeStrategy>();

                        resumeStrategy.CanResume(
                            Arg.Any <IFileResource>(),
                            Arg.Any <IFileResource>(),
                            Arg.Any <Stream>(),
                            Arg.Any <Stream>()
                            )
                        .Returns(true);
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath     = GetRandomString();
                        var allData     = GetRandomBytes(1024);
                        var partialSize = GetRandomInt(384, 768);
                        var partialData = allData.Take(partialSize)
                                          .ToArray();
                        var expected = allData.Skip(partialSize)
                                       .ToArray();
                        var targetFilePath = arena.CreateResource(
                            arena.TargetPath,
                            relPath,
                            partialData);

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            allData);
                        var captured     = new List <byte>();
                        var ended        = false;
                        var intermediate = new GenericPassThrough(
                            (data, count) => captured.AddRange(data.Take(count)),
                            () => ended = true
                            );
                        var sut = Create(
                            resumeStrategy,
                            new IPassThrough[] { intermediate }
                            );

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        var targetData = File.ReadAllBytes(targetFilePath);

                        Expect(targetData)
                        .To.Equal(allData);
                        var transferred = captured.ToArray();

                        Expect(transferred)
                        .To.Equal(expected, "unexpected transferred data");
                        Expect(ended)
                        .To.Be.True();
                    }
示例#8
0
            public void ShouldRetryOnError()
            {
                // Arrange
                using var arena = new TestArena();
                var options     = Substitute.For <IOptions>();

                options.Retries.Returns(3);

                using var resetter = new AutoResetter <int>(() =>
                {
                    var original           = StreamSource.MaxBuffer;
                    StreamSource.MaxBuffer = 1;
                    return(original);
                },
                                                            original => StreamSource.MaxBuffer = original);
                var haveErrored      = false;
                var errorPassThrough = new GenericPassThrough(
                    (data, size) =>
                {
                    if (!haveErrored)
                    {
                        haveErrored = true;
                        throw new DirectoryNotFoundException("foo");
                    }
                },
                    () =>
                {
                }
                    );
                // keep the size small since we're forcing a flush at every byte
                var sourceFile = arena.CreateSourceFile(data: GetRandomBytes(10, 20));
                var expected   = arena.TargetPathFor(sourceFile);
                var sut        = Create(
                    intermediatePipes: new IPassThrough[] { errorPassThrough },
                    options: options);

                // Act
                sut.Synchronize(arena.SourceFileSystem, arena.TargetFileSystem);
                // Assert
                Expect(expected)
                .To.Exist();
                Expect(File.ReadAllBytes(expected))
                .To.Equal(sourceFile.Data);
            }
示例#9
0
                    public void ShouldUpsertHistory()
                    {
                        // Arrange
                        using var arena     = new TestArena();
                        var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem);
                        var relPath        = GetRandomString(2);
                        var data           = GetRandomBytes(100);
                        var targetFilePath = arena.CreateResource(
                            arena.TargetPath,
                            relPath,
                            data);

                        arena.CreateResource(
                            arena.SourcePath,
                            relPath,
                            data);
                        var beforeTest = DateTime.Now;
                        var lastWrite  = beforeTest.AddMinutes(GetRandomInt(-100, -1));

                        File.SetLastWriteTime(targetFilePath, lastWrite);
                        var historyRepo = Substitute.For <ITargetHistoryRepository>();

                        var sut = Create(targetHistoryRepository: historyRepo);

                        // Act
                        sut.Synchronize(source, target);
                        // Assert
                        Expect(historyRepo)
                        .To.Have.Received(1)
                        .Upsert(Arg.Is <IEnumerable <IHistoryItem> >(
                                    o => o.Single()
                                    .Path ==
                                    relPath &&
                                    o.Single()
                                    .Size ==
                                    data.Length
                                    ));
                    }
示例#10
0
            public void ShouldArchiveOnlyThoseFiles()
            {
                using var arena = new TestArena();
                // Arrange
                var toArchive    = $"{GetRandomFileName()}";
                var archiveData  = GetRandomBytes();
                var sourceFile   = arena.CreateSourceFile(toArchive, archiveData);
                var targetFile   = arena.CreateTargetFile(toArchive, archiveData);
                var targetMarker = arena.CreateTargetFile($"{sourceFile.Name}.t", new byte[0]);
                var archive      = arena.ArchiveFileSystem;
                var target       = arena.TargetFileSystem;
                var source       = arena.SourceFileSystem;

                var sut = Create();

                // Act
                sut.RunArchiveOperations(
                    target,
                    archive,
                    source);
                // Assert
                // archive target should have archived file
                // -> should not be at target
                Expect(arena.TargetFileSystem.Exists(toArchive))
                .To.Be.False("Target file should not exist any more");
                // -> should not be at source either
                Expect(arena.SourceFileSystem.Exists(toArchive))
                .To.Be.False("Source file should not exist any more");
                // archive marker should no longer exist
                Expect(arena.TargetFileSystem.Exists($"{toArchive}.t"))
                .To.Be.False("Target file marker should not exist any more");
                // resource file _should_ exist at archive
                Expect(arena.ArchiveFileSystem.Exists(toArchive))
                .To.Be.True("Resource should be archived");
                // marker file _should not_ exist at archive
                Expect(arena.ArchiveFileSystem.Exists($"{toArchive}.t"))
                .To.Be.False("Marker should not be present in archive");
            }
示例#11
0
            public void ShouldBeAbleToStopViaStdIn()
            {
                // Arrange
                using (var arena = new TestArena("-e", "localdb"))
                {
                    // Act
                    arena.WaitForProgramToListen();
                    var interestingLine = arena.StdOut.FirstOrDefault(
                        line => line.StartsWith(
                            "connection string",
                            StringComparison.InvariantCultureIgnoreCase
                            ));
                    Expect(interestingLine)
                    .Not.To.Be.Null(
                        "TempDb runner should emit the connection string on stdout"
                        );
                    var connectionString = interestingLine.Split(':')
                                           .Skip(1)
                                           .JoinWith(":")
                                           .Trim();
                    using (var connection = new SqlConnection(connectionString))
                    {
                        // Assert
                        Expect(() => connection.Open())
                        .Not.To.Throw();
                    }

                    arena.WriteStdIn("stop");
                    arena.WaitForProgramToExit();

                    using (var dead = new SqlConnection(connectionString))
                    {
                        Expect(() => dead.Open())
                        .To.Throw <SqlException>();
                    }
                }
            }
示例#12
0
            public void ShouldNotifyNotifiablePipesWithPossibleResumeSizes()
            {
                // Arrange
                using var arena = new TestArena();
                var missingData = GetRandomBytes(100);

                arena.CreateSourceResource(
                    "missing",
                    missingData);
                var partialFileAllData = "hello world".AsBytes();
                var partialTargetData  = "hello".AsBytes();

                arena.CreateSourceResource("partial", partialFileAllData);
                arena.CreateTargetResource("partial", partialTargetData);
                var existingData = GetRandomBytes(100);

                arena.CreateSourceResource("existing", existingData);
                arena.CreateTargetResource("existing", existingData);
                var intermediate1 = new GenericPassThrough(
                    (data, count) =>
                {
                },
                    () =>
                {
                });
                var notifiable = new NotifiableGenericPassThrough(
                    (data, count) =>
                {
                },
                    () =>
                {
                });
                var options = Substitute.For <IOptions>();

                options.ResumeCheckBytes.Returns(512);
                var sut = Create(
                    new SimpleResumeStrategy(options, Substitute.For <IMessageWriter>()),
                    new IPassThrough[] { notifiable, intermediate1 }
                    .Randomize()
                    .ToArray()
                    );

                // Act
                sut.Synchronize(
                    arena.SourceFileSystem,
                    arena.TargetFileSystem);
                // Assert
                var partialResultData = arena.TargetFileSystem.ReadAllBytes(
                    "partial");

                Expect(partialResultData)
                .To.Equal(partialFileAllData,
                          () => $@"Expected {
                                partialFileAllData.Length
                            } bytes, but got {
                                partialResultData.Length
                            }: '{
                                partialFileAllData.AsString()
                            }' vs '{
                                partialResultData.AsString()
                            }'");
                Expect(arena.TargetFileSystem.ReadAllBytes("existing"))
                .To.Equal(existingData);
                Expect(arena.TargetFileSystem.ReadAllBytes("missing"))
                .To.Equal(missingData);
                Expect(notifiable.BatchStartedNotifications)
                .To.Contain.Only(1)
                .Item();     // should only have one initial notification
                var initial = notifiable.BatchStartedNotifications.Single();

                Expect(initial.resources)
                .To.Contain
                .Only(2)
                .Items();     // should only have partial and missing in sync queue
                Expect(initial.resources)
                .To.Contain.Exactly(1)
                .Matched.By(
                    resource => resource.RelativePath == "missing"
                    );
                Expect(initial.resources)
                .To.Contain.Exactly(1)
                .Matched.By(
                    resource => resource.RelativePath == "partial"
                    );

                var batchComplete = notifiable.BatchCompletedNotifications.Single();

                Expect(batchComplete.resources)
                .To.Contain
                .Only(2)
                .Items();     // should only have partial and missing in sync queue
                Expect(batchComplete.resources)
                .To.Contain.Exactly(1)
                .Matched.By(
                    resource => resource.RelativePath == "missing"
                    );
                Expect(batchComplete.resources)
                .To.Contain.Exactly(1)
                .Matched.By(
                    resource => resource.RelativePath == "partial"
                    );

                Expect(notifiable.ResourceNotifications)
                .To.Contain.Exactly(2)
                .Items();
                var missingResource = notifiable.ResourceNotifications.First();

                Expect(missingResource.source.RelativePath)
                .To.Equal("missing");
                Expect(missingResource.source.Size)
                .To.Equal(missingData.Length);
                Expect(missingResource.target)
                .To.Be.Null();
                var partialResource = notifiable.ResourceNotifications.Second();

                Expect(partialResource.source.RelativePath)
                .To.Equal("partial");
                Expect(partialResource.source.Size)
                .To.Equal(partialFileAllData.Length);
                Expect(partialResource.target.RelativePath)
                .To.Equal("partial");
                Expect(partialResource.target.Size)
                .To.Equal(partialTargetData.Length);

                Expect(notifiable.CompletedNotifications)
                .To.Contain.Exactly(2)
                .Items();
                missingResource = notifiable.CompletedNotifications.First();
                Expect(missingResource.source.RelativePath)
                .To.Equal("missing");
                Expect(missingResource.source.Size)
                .To.Equal(missingData.Length);
                Expect(missingResource.target)
                .Not.To.Be.Null();
                Expect(missingResource.target.RelativePath)
                .To.Equal("missing");
                Expect(missingResource.target.Size)
                .To.Equal(missingData.Length);
                partialResource = notifiable.CompletedNotifications.Second();
                Expect(partialResource.source.RelativePath)
                .To.Equal("partial");
                Expect(partialResource.source.Size)
                .To.Equal(partialFileAllData.Length);
                Expect(partialResource.target.RelativePath)
                .To.Equal("partial");
                Expect(partialResource.target.Size)
                .To.Equal(partialTargetData.Length);
            }