diff --git a/test/E2ETest/DockerAssert.cs b/test/E2ETest/DockerAssert.cs index dfaf3dd5..6f5d3ed7 100644 --- a/test/E2ETest/DockerAssert.cs +++ b/test/E2ETest/DockerAssert.cs @@ -18,13 +18,9 @@ namespace E2ETest // wierd and confusing. public static async Task AssertImageExistsAsync(ITestOutputHelper output, string repository) { - if (repository is null) - { - throw new ArgumentNullException(nameof(repository)); - } - var builder = new StringBuilder(); + output.WriteLine($"> docker images \"{repository}\" --format \"{{{{.Repository}}}}\""); var exitCode = await Process.ExecuteAsync( "docker", $"images \"{repository}\" --format \"{{{{.Repository}}}}\"", @@ -35,7 +31,7 @@ namespace E2ETest throw new XunitException($"Running `docker images \"{repository}\"` failed." + Environment.NewLine + builder.ToString()); } - var lines = builder.ToString().Split(new[] { '\r', '\n', }); + var lines = builder.ToString().Split(new[] { '\r', '\n', }, StringSplitOptions.RemoveEmptyEntries); if (lines.Any(line => line == repository)) { return; @@ -49,5 +45,63 @@ namespace E2ETest output.WriteLine(text); } } + + public static async Task DeleteDockerImagesAsync(ITestOutputHelper output, string repository) + { + var ids = await ListDockerImagesIdsAsync(output, repository); + + var builder = new StringBuilder(); + + foreach (var id in ids) + { + + output.WriteLine($"> docker rmi \"{id}\""); + var exitCode = await Process.ExecuteAsync( + "docker", + $"rmi \"{id}\"", + stdOut: OnOutput, + stdErr: OnOutput); + if (exitCode != 0) + { + throw new XunitException($"Running `docker rmi \"{id}\"` failed." + Environment.NewLine + builder.ToString()); + } + + builder.Clear(); + } + + void OnOutput(string text) + { + builder.AppendLine(text); + output.WriteLine(text); + } + } + + private static async Task ListDockerImagesIdsAsync(ITestOutputHelper output, string repository) + { + // docker images -q '{repository}' returns just the ID of the image (one per line) + // It does not fail if there are no matches, just returns empty output. + + var builder = new StringBuilder(); + + output.WriteLine($"> docker images -q \"{repository}\""); + var exitCode = await Process.ExecuteAsync( + "docker", + $"images -q \"{repository}\"", + stdOut: OnOutput, + stdErr: OnOutput); + if (exitCode != 0) + { + throw new XunitException($"Running `docker images -q \"{repository}\"` failed." + Environment.NewLine + builder.ToString()); + } + + var lines = builder.ToString().Split(new[] { '\r', '\n', }, StringSplitOptions.RemoveEmptyEntries); + return lines; + + void OnOutput(string text) + { + builder.AppendLine(text); + output.WriteLine(text); + } + } } } diff --git a/test/E2ETest/TyeBuildTests.cs b/test/E2ETest/TyeBuildTests.cs index 57ae44ca..aaa4e6ed 100644 --- a/test/E2ETest/TyeBuildTests.cs +++ b/test/E2ETest/TyeBuildTests.cs @@ -26,6 +26,8 @@ namespace E2ETest [SkipIfDockerNotRunning] public async Task SingleProjectBuildTest() { + await DockerAssert.DeleteDockerImagesAsync(output, "test/test-project"); + var projectName = "single-project"; var environment = "production"; @@ -39,15 +41,25 @@ namespace E2ETest application.Registry = "test"; - await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + try + { + await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - await DockerAssert.AssertImageExistsAsync(output, "test/test-project"); + await DockerAssert.AssertImageExistsAsync(output, "test/test-project"); + } + finally + { + await DockerAssert.DeleteDockerImagesAsync(output, "test/test-project"); + } } [ConditionalFact] [SkipIfDockerNotRunning] public async Task FrontendBackendBuildTest() { + await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); + var projectName = "frontend-backend"; var environment = "production"; @@ -61,16 +73,27 @@ namespace E2ETest application.Registry = "test"; - await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - - await DockerAssert.AssertImageExistsAsync(output, "test/backend"); - await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + try + { + await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + + await DockerAssert.AssertImageExistsAsync(output, "test/backend"); + await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + } + finally + { + await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); + } } [ConditionalFact] [SkipIfDockerNotRunning] public async Task MultipleProjectBuildTest() { + await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/worker"); var projectName = "multi-project"; var environment = "production"; @@ -85,17 +108,28 @@ namespace E2ETest application.Registry = "test"; - await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - - await DockerAssert.AssertImageExistsAsync(output, "test/backend"); - await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); - await DockerAssert.AssertImageExistsAsync(output, "test/worker"); + try + { + await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + + await DockerAssert.AssertImageExistsAsync(output, "test/backend"); + await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + await DockerAssert.AssertImageExistsAsync(output, "test/worker"); + } + finally + { + await DockerAssert.AssertImageExistsAsync(output, "test/backend"); + await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + await DockerAssert.AssertImageExistsAsync(output, "test/worker"); + } } [ConditionalFact] [SkipIfDockerNotRunning] public async Task BuildDoesNotRequireRegistry() { + await DockerAssert.DeleteDockerImagesAsync(output, "test-project"); + var projectName = "single-project"; var environment = "production"; @@ -107,9 +141,16 @@ namespace E2ETest var application = ConfigFactory.FromFile(projectFile); - await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + try + { + await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - await DockerAssert.AssertImageExistsAsync(output, "test-project"); + await DockerAssert.AssertImageExistsAsync(output, "test-project"); + } + finally + { + await DockerAssert.DeleteDockerImagesAsync(output, "test-project"); + } } } } diff --git a/test/E2ETest/TyeGenerateTests.cs b/test/E2ETest/TyeGenerateTests.cs index 21ec0494..84dcfeda 100644 --- a/test/E2ETest/TyeGenerateTests.cs +++ b/test/E2ETest/TyeGenerateTests.cs @@ -26,6 +26,8 @@ namespace E2ETest [SkipIfDockerNotRunning] public async Task SingleProjectGenerateTest() { + await DockerAssert.DeleteDockerImagesAsync(output, "test/test-project"); + var projectName = "single-project"; var environment = "production"; @@ -40,19 +42,30 @@ namespace E2ETest // Need to add docker registry for generate application.Registry = "test"; - await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - - // name of application is the folder - var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); - var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); - - Assert.Equal(expectedContent, content); + try + { + await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + + // name of application is the folder + var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); + var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); + + Assert.Equal(expectedContent, content); + await DockerAssert.AssertImageExistsAsync(output, "test/test-project"); + } + finally + { + await DockerAssert.DeleteDockerImagesAsync(output, "test/test-project"); + } } [ConditionalFact] [SkipIfDockerNotRunning] public async Task FrontendBackendGenerateTest() { + await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); + var projectName = "frontend-backend"; var environment = "production"; @@ -67,19 +80,36 @@ namespace E2ETest // Need to add docker registry for generate application.Registry = "test"; - await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + try + { + await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + + // name of application is the folder + var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); + var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); + + Assert.Equal(expectedContent, content); - // name of application is the folder - var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); - var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); + await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - Assert.Equal(expectedContent, content); + await DockerAssert.AssertImageExistsAsync(output, "test/backend"); + await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + } + finally + { + await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); + } } [ConditionalFact] [SkipIfDockerNotRunning] public async Task MultipleProjectGenerateTest() { + await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); + await DockerAssert.DeleteDockerImagesAsync(output, "test/worker"); + var projectName = "multi-project"; var environment = "production"; @@ -94,19 +124,34 @@ namespace E2ETest // Need to add docker registry for generate application.Registry = "test"; - await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - - // name of application is the folder - var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); - var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); - - Assert.Equal(expectedContent, content); + try + { + await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + + // name of application is the folder + var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); + var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); + + Assert.Equal(expectedContent, content); + + await DockerAssert.AssertImageExistsAsync(output, "test/backend"); + await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + await DockerAssert.AssertImageExistsAsync(output, "test/worker"); + } + finally + { + await DockerAssert.AssertImageExistsAsync(output, "test/backend"); + await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); + await DockerAssert.AssertImageExistsAsync(output, "test/worker"); + } } [ConditionalFact] [SkipIfDockerNotRunning] public async Task GenerateWorksWithoutRegistry() { + await DockerAssert.DeleteDockerImagesAsync(output, "test-project"); + var projectName = "single-project"; var environment = "production"; @@ -118,13 +163,24 @@ namespace E2ETest var application = ConfigFactory.FromFile(projectFile); - await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + try + { + await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); + + // name of application is the folder + var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); + var expectedContent = File.ReadAllText($"testassets/generate/{projectName}-noregistry.yaml"); + + Assert.Equal(expectedContent, content); - // name of application is the folder - var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); - var expectedContent = File.ReadAllText($"testassets/generate/{projectName}-noregistry.yaml"); + await BuildHost.ExecuteBuildAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); - Assert.Equal(expectedContent, content); + await DockerAssert.AssertImageExistsAsync(output, "test-project"); + } + finally + { + await DockerAssert.DeleteDockerImagesAsync(output, "test-project"); + } } } }