From 5639786505aa20449ec63c7aa104ae2ddff8fb8b Mon Sep 17 00:00:00 2001 From: Wim Van Laer Date: Thu, 6 Feb 2025 22:23:17 +0100 Subject: [PATCH 1/4] feat: implemented SFTP container atmoz/sftp --- Directory.Packages.props | 3 +- Testcontainers.sln | 14 ++ src/Testcontainers.Sftp/.editorconfig | 1 + src/Testcontainers.Sftp/SftpBuilder.cs | 126 ++++++++++++++++++ src/Testcontainers.Sftp/SftpConfiguration.cs | 78 +++++++++++ src/Testcontainers.Sftp/SftpContainer.cs | 13 ++ .../Testcontainers.Sftp.csproj | 13 ++ src/Testcontainers.Sftp/Usings.cs | 6 + tests/Testcontainers.Sftp.Tests/.editorconfig | 1 + .../SftpContainerTest.cs | 33 +++++ .../Testcontainers.Sftp.Tests.csproj | 20 +++ tests/Testcontainers.Sftp.Tests/Usings.cs | 5 + 12 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/Testcontainers.Sftp/.editorconfig create mode 100644 src/Testcontainers.Sftp/SftpBuilder.cs create mode 100644 src/Testcontainers.Sftp/SftpConfiguration.cs create mode 100644 src/Testcontainers.Sftp/SftpContainer.cs create mode 100644 src/Testcontainers.Sftp/Testcontainers.Sftp.csproj create mode 100644 src/Testcontainers.Sftp/Usings.cs create mode 100644 tests/Testcontainers.Sftp.Tests/.editorconfig create mode 100644 tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs create mode 100644 tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj create mode 100644 tests/Testcontainers.Sftp.Tests/Usings.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 8c825fa6c..1cb4bfb43 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -67,7 +67,8 @@ + - + \ No newline at end of file diff --git a/Testcontainers.sln b/Testcontainers.sln index 444bdb1c3..5806c9617 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -95,6 +95,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda", "src\Testcontainers.Redpanda\Testcontainers.Redpanda.csproj", "{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp", "src\Testcontainers.Sftp\Testcontainers.Sftp.csproj", "{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus", "src\Testcontainers.ServiceBus\Testcontainers.ServiceBus.csproj", "{2E39E532-B81E-4B48-A004-FAE18EDF9E79}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Weaviate", "src\Testcontainers.Weaviate\Testcontainers.Weaviate.csproj", "{68F8600D-24E9-4E03-9E25-5F6EB338EAC1}" @@ -201,6 +203,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ResourceReap EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus.Tests", "tests\Testcontainers.ServiceBus.Tests\Testcontainers.ServiceBus.Tests.csproj", "{232DD918-46ED-4BA8-B383-1A9146D83064}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp.Tests", "tests\Testcontainers.Sftp.Tests\Testcontainers.Sftp.Tests.csproj", "{B73C3CC0-9F16-4B34-92BE-6EC0853912C5}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tests\Testcontainers.Tests\Testcontainers.Tests.csproj", "{27CDB869-A150-4593-958F-6F26E5391E7C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Weaviate.Tests", "tests\Testcontainers.Weaviate.Tests\Testcontainers.Weaviate.Tests.csproj", "{DDB41BC8-5826-4D97-9C5F-001151E3FFD6}" @@ -610,6 +614,14 @@ Global {E901DF14-6F05-4FC2-825A-3055FAD33561}.Debug|Any CPU.Build.0 = Debug|Any CPU {E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.ActiveCfg = Release|Any CPU {E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.Build.0 = Release|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.Build.0 = Release|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {5365F780-0E6C-41F0-B1B9-7DC34368F80C} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -710,5 +722,7 @@ Global {DDB41BC8-5826-4D97-9C5F-001151E3FFD6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {E901DF14-6F05-4FC2-825A-3055FAD33561} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} EndGlobalSection EndGlobal diff --git a/src/Testcontainers.Sftp/.editorconfig b/src/Testcontainers.Sftp/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/src/Testcontainers.Sftp/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/src/Testcontainers.Sftp/SftpBuilder.cs b/src/Testcontainers.Sftp/SftpBuilder.cs new file mode 100644 index 000000000..037e14d77 --- /dev/null +++ b/src/Testcontainers.Sftp/SftpBuilder.cs @@ -0,0 +1,126 @@ +using System.Text.RegularExpressions; + +namespace Testcontainers.Sftp; + +/// +[PublicAPI] +public sealed class SftpBuilder : ContainerBuilder +{ + public const string SftpImage = "atmoz/sftp"; + + public const ushort SftpPort = 22; + + public const string DefaultUsername = "foo"; + + public const string DefaultPassword = "bar"; + + public const string DefaultUploadDirectory = "upload"; + + /// + /// Initializes a new instance of the class. + /// + public SftpBuilder() + : this(new SftpConfiguration()) + { + DockerResourceConfiguration = Init().DockerResourceConfiguration; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + private SftpBuilder(SftpConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + DockerResourceConfiguration = resourceConfiguration; + } + + /// + protected override SftpConfiguration DockerResourceConfiguration { get; } + + /// + /// Sets the Sftp username. + /// + /// The Sftp username. + /// A configured instance of . + public SftpBuilder WithUsername(string username) + { + return Merge(DockerResourceConfiguration, new SftpConfiguration(username: username)); + } + + /// + /// Sets the Sftp password. + /// + /// The Sftp password. + /// A configured instance of . + public SftpBuilder WithPassword(string password) + { + return Merge(DockerResourceConfiguration, new SftpConfiguration(password: password)); + } + + /// + /// Sets the directory to which files should be uploaded on the server. + /// + /// The Sftp password. + /// A configured instance of . + public SftpBuilder WithUploadDirectory(string uploadDirectory) + { + return Merge(DockerResourceConfiguration, new SftpConfiguration(uploadDirectory: uploadDirectory)); + } + + /// + public override SftpContainer Build() + { + var builder = WithCommand($"{DockerResourceConfiguration.Username}:{DockerResourceConfiguration.Password}:::{DockerResourceConfiguration.UploadDirectory}"); + builder.Validate(); + return new SftpContainer(builder.DockerResourceConfiguration); + } + + /// + protected override SftpBuilder Init() + { + return base.Init() + .WithImage(SftpImage) + .WithPortBinding(SftpPort, true) + .WithUsername(DefaultUsername) + .WithPassword(DefaultPassword) + .WithUploadDirectory(DefaultUploadDirectory) + .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(new Regex("Server listening on .+"))); + } + + /// + protected override void Validate() + { + base.Validate(); + + _ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username)) + .NotNull() + .NotEmpty(); + + _ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password)) + .NotNull() + .NotEmpty(); + + _ = Guard.Argument(DockerResourceConfiguration.UploadDirectory, nameof(DockerResourceConfiguration.UploadDirectory)) + .NotNull() + .NotEmpty(); + } + + /// + protected override SftpBuilder Clone(IResourceConfiguration resourceConfiguration) + { + return Merge(DockerResourceConfiguration, new SftpConfiguration(resourceConfiguration)); + } + + /// + protected override SftpBuilder Clone(IContainerConfiguration resourceConfiguration) + { + return Merge(DockerResourceConfiguration, new SftpConfiguration(resourceConfiguration)); + } + + /// + protected override SftpBuilder Merge(SftpConfiguration oldValue, SftpConfiguration newValue) + { + return new SftpBuilder(new SftpConfiguration(oldValue, newValue)); + } +} \ No newline at end of file diff --git a/src/Testcontainers.Sftp/SftpConfiguration.cs b/src/Testcontainers.Sftp/SftpConfiguration.cs new file mode 100644 index 000000000..800485f7c --- /dev/null +++ b/src/Testcontainers.Sftp/SftpConfiguration.cs @@ -0,0 +1,78 @@ +namespace Testcontainers.Sftp; + +/// +[PublicAPI] +public sealed class SftpConfiguration : ContainerConfiguration +{ + /// + /// Initializes a new instance of the class. + /// + /// The Sftp username. + /// The Sftp password. + /// The directory to which files should be uploaded on the server. + public SftpConfiguration(string username = null, string password = null, string uploadDirectory = null) + { + Username = username; + Password = password; + UploadDirectory = uploadDirectory; + } + + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public SftpConfiguration(IResourceConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public SftpConfiguration(IContainerConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public SftpConfiguration(SftpConfiguration resourceConfiguration) + : this(new SftpConfiguration(), resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The old Docker resource configuration. + /// The new Docker resource configuration. + public SftpConfiguration(SftpConfiguration oldValue, SftpConfiguration newValue) + : base(oldValue, newValue) + { + Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username); + Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password); + UploadDirectory = BuildConfiguration.Combine(oldValue.UploadDirectory, newValue.UploadDirectory); + } + + /// + /// Gets the sftp username. + /// + public string Username { get; } + + /// + /// Gets the sftp password. + /// + public string Password { get; } + + /// + /// Gets the directory to which files should be uploaded on the server. + /// + public string UploadDirectory { get; } +} \ No newline at end of file diff --git a/src/Testcontainers.Sftp/SftpContainer.cs b/src/Testcontainers.Sftp/SftpContainer.cs new file mode 100644 index 000000000..13aaf150b --- /dev/null +++ b/src/Testcontainers.Sftp/SftpContainer.cs @@ -0,0 +1,13 @@ +namespace Testcontainers.Sftp; + +[PublicAPI] +public sealed class SftpContainer : DockerContainer +{ + private readonly SftpConfiguration _configuration; + + public SftpContainer(SftpConfiguration configuration) + : base(configuration) + { + _configuration = configuration; + } +} \ No newline at end of file diff --git a/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj b/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj new file mode 100644 index 000000000..bdb00acf8 --- /dev/null +++ b/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj @@ -0,0 +1,13 @@ + + + net8.0;net9.0;netstandard2.0;netstandard2.1 + latest + Testcsontainers.Sftp + + + + + + + + \ No newline at end of file diff --git a/src/Testcontainers.Sftp/Usings.cs b/src/Testcontainers.Sftp/Usings.cs new file mode 100644 index 000000000..fa3a104a1 --- /dev/null +++ b/src/Testcontainers.Sftp/Usings.cs @@ -0,0 +1,6 @@ +global using Docker.DotNet.Models; +global using DotNet.Testcontainers; +global using DotNet.Testcontainers.Builders; +global using DotNet.Testcontainers.Configurations; +global using DotNet.Testcontainers.Containers; +global using JetBrains.Annotations; \ No newline at end of file diff --git a/tests/Testcontainers.Sftp.Tests/.editorconfig b/tests/Testcontainers.Sftp.Tests/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/tests/Testcontainers.Sftp.Tests/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs b/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs new file mode 100644 index 000000000..5a01ae21a --- /dev/null +++ b/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs @@ -0,0 +1,33 @@ + +namespace Testcontainers.Sftp; + +public sealed class SftpContainerTest : IAsyncLifetime +{ + private readonly SftpContainer _sftpContainer = new SftpBuilder().Build(); + + public Task InitializeAsync() + { + return _sftpContainer.StartAsync(); + } + + public Task DisposeAsync() + { + return _sftpContainer.DisposeAsync().AsTask(); + } + + [Fact] + [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] + public async Task IsConnectedReturnsTrue() + { + // Given + var hostname = _sftpContainer.Hostname; + var port = _sftpContainer.GetMappedPublicPort(SftpBuilder.SftpPort); + var client = new SftpClient(hostname, port, SftpBuilder.DefaultUsername, SftpBuilder.DefaultPassword); + + // When + await client.ConnectAsync(CancellationToken.None); + + // Then + Assert.True(client.IsConnected); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj b/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj new file mode 100644 index 000000000..a020b3d90 --- /dev/null +++ b/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj @@ -0,0 +1,20 @@ + + + net9.0 + false + false + + + + + + + + + + + + + + + diff --git a/tests/Testcontainers.Sftp.Tests/Usings.cs b/tests/Testcontainers.Sftp.Tests/Usings.cs new file mode 100644 index 000000000..9e93730ce --- /dev/null +++ b/tests/Testcontainers.Sftp.Tests/Usings.cs @@ -0,0 +1,5 @@ +global using System.Threading; +global using System.Threading.Tasks; +global using DotNet.Testcontainers.Commons; +global using Renci.SshNet; +global using Xunit; \ No newline at end of file From 524ea992f2cbe45957ec85148b29f209a9af08d8 Mon Sep 17 00:00:00 2001 From: Wim Van Laer Date: Thu, 6 Feb 2025 22:38:22 +0100 Subject: [PATCH 2/4] Update cicd.yml --- .github/workflows/cicd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 642ce1a33..80f7cbee3 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -79,6 +79,7 @@ jobs: { name: "Testcontainers.Redis", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Redpanda", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.ServiceBus", runs-on: "ubuntu-22.04" }, + { name: "Testcontainers.Sftp", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Weaviate", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.WebDriver", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Xunit", runs-on: "ubuntu-22.04" } From 15b36a993ece311c5526c84f1b7ab0251f1dc983 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Mon, 10 Feb 2025 16:42:40 +0100 Subject: [PATCH 3/4] chore: Align repo standards --- Directory.Packages.props | 3 +- Testcontainers.sln | 24 ++++++------- src/Testcontainers.Sftp/SftpBuilder.cs | 28 +++++++++------ src/Testcontainers.Sftp/SftpConfiguration.cs | 16 +++++---- src/Testcontainers.Sftp/SftpContainer.cs | 8 +++-- .../Testcontainers.Sftp.csproj | 3 +- .../SftpContainerTest.cs | 14 ++++---- .../Testcontainers.Sftp.Tests.csproj | 35 +++++++++---------- 8 files changed, 69 insertions(+), 62 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1cb4bfb43..8c825fa6c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -67,8 +67,7 @@ - - \ No newline at end of file + diff --git a/Testcontainers.sln b/Testcontainers.sln index 5806c9617..4afa799e9 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -95,10 +95,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda", "src\Testcontainers.Redpanda\Testcontainers.Redpanda.csproj", "{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp", "src\Testcontainers.Sftp\Testcontainers.Sftp.csproj", "{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus", "src\Testcontainers.ServiceBus\Testcontainers.ServiceBus.csproj", "{2E39E532-B81E-4B48-A004-FAE18EDF9E79}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp", "src\Testcontainers.Sftp\Testcontainers.Sftp.csproj", "{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Weaviate", "src\Testcontainers.Weaviate\Testcontainers.Weaviate.csproj", "{68F8600D-24E9-4E03-9E25-5F6EB338EAC1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver", "src\Testcontainers.WebDriver\Testcontainers.WebDriver.csproj", "{64A87DE5-29B0-4A54-9E74-560484D8C7C0}" @@ -390,6 +390,10 @@ Global {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Release|Any CPU.Build.0 = Release|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.Build.0 = Release|Any CPU {68F8600D-24E9-4E03-9E25-5F6EB338EAC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {68F8600D-24E9-4E03-9E25-5F6EB338EAC1}.Debug|Any CPU.Build.0 = Debug|Any CPU {68F8600D-24E9-4E03-9E25-5F6EB338EAC1}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -598,6 +602,10 @@ Global {232DD918-46ED-4BA8-B383-1A9146D83064}.Debug|Any CPU.Build.0 = Debug|Any CPU {232DD918-46ED-4BA8-B383-1A9146D83064}.Release|Any CPU.ActiveCfg = Release|Any CPU {232DD918-46ED-4BA8-B383-1A9146D83064}.Release|Any CPU.Build.0 = Release|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.Build.0 = Release|Any CPU {27CDB869-A150-4593-958F-6F26E5391E7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27CDB869-A150-4593-958F-6F26E5391E7C}.Debug|Any CPU.Build.0 = Debug|Any CPU {27CDB869-A150-4593-958F-6F26E5391E7C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -614,14 +622,6 @@ Global {E901DF14-6F05-4FC2-825A-3055FAD33561}.Debug|Any CPU.Build.0 = Debug|Any CPU {E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.ActiveCfg = Release|Any CPU {E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.Build.0 = Release|Any CPU - {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.Build.0 = Release|Any CPU - {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {5365F780-0E6C-41F0-B1B9-7DC34368F80C} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -666,6 +666,7 @@ Global {BFDA179A-40EB-4CEB-B8E9-0DF32C65E2C5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {45D6F69C-4D87-4130-AA90-0DB2F7460DAE} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {2E39E532-B81E-4B48-A004-FAE18EDF9E79} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} + {7D5C6816-0DD2-4E13-A585-033B5D3C80D5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {68F8600D-24E9-4E03-9E25-5F6EB338EAC1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {64A87DE5-29B0-4A54-9E74-560484D8C7C0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {380BB29B-F556-404D-B13B-CA250599C565} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -718,11 +719,10 @@ Global {867BD04E-4670-4FBA-98D5-9F83220E6DFB} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {9E8E6AA5-65D1-498F-BEAB-BA34723A0050} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {232DD918-46ED-4BA8-B383-1A9146D83064} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} + {B73C3CC0-9F16-4B34-92BE-6EC0853912C5} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {DDB41BC8-5826-4D97-9C5F-001151E3FFD6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {E901DF14-6F05-4FC2-825A-3055FAD33561} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} - {7D5C6816-0DD2-4E13-A585-033B5D3C80D5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} - {B73C3CC0-9F16-4B34-92BE-6EC0853912C5} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} EndGlobalSection EndGlobal diff --git a/src/Testcontainers.Sftp/SftpBuilder.cs b/src/Testcontainers.Sftp/SftpBuilder.cs index 037e14d77..f294c55a2 100644 --- a/src/Testcontainers.Sftp/SftpBuilder.cs +++ b/src/Testcontainers.Sftp/SftpBuilder.cs @@ -1,18 +1,16 @@ -using System.Text.RegularExpressions; - namespace Testcontainers.Sftp; /// [PublicAPI] public sealed class SftpBuilder : ContainerBuilder { - public const string SftpImage = "atmoz/sftp"; + public const string SftpImage = "atmoz/sftp:alpine"; public const ushort SftpPort = 22; - public const string DefaultUsername = "foo"; + public const string DefaultUsername = "sftp"; - public const string DefaultPassword = "bar"; + public const string DefaultPassword = "sftp"; public const string DefaultUploadDirectory = "upload"; @@ -59,9 +57,9 @@ public SftpBuilder WithPassword(string password) } /// - /// Sets the directory to which files should be uploaded on the server. + /// Sets the directory to which files are uploaded. /// - /// The Sftp password. + /// The upload directory. /// A configured instance of . public SftpBuilder WithUploadDirectory(string uploadDirectory) { @@ -71,9 +69,17 @@ public SftpBuilder WithUploadDirectory(string uploadDirectory) /// public override SftpContainer Build() { - var builder = WithCommand($"{DockerResourceConfiguration.Username}:{DockerResourceConfiguration.Password}:::{DockerResourceConfiguration.UploadDirectory}"); - builder.Validate(); - return new SftpContainer(builder.DockerResourceConfiguration); + Validate(); + + var sftpContainer = WithCommand(string.Join( + ":", + DockerResourceConfiguration.Username, + DockerResourceConfiguration.Password, + string.Empty, + string.Empty, + DockerResourceConfiguration.UploadDirectory)); + + return new SftpContainer(sftpContainer.DockerResourceConfiguration); } /// @@ -85,7 +91,7 @@ protected override SftpBuilder Init() .WithUsername(DefaultUsername) .WithPassword(DefaultPassword) .WithUploadDirectory(DefaultUploadDirectory) - .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(new Regex("Server listening on .+"))); + .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Server listening on .+")); } /// diff --git a/src/Testcontainers.Sftp/SftpConfiguration.cs b/src/Testcontainers.Sftp/SftpConfiguration.cs index 800485f7c..9dfb00c44 100644 --- a/src/Testcontainers.Sftp/SftpConfiguration.cs +++ b/src/Testcontainers.Sftp/SftpConfiguration.cs @@ -9,14 +9,16 @@ public sealed class SftpConfiguration : ContainerConfiguration /// /// The Sftp username. /// The Sftp password. - /// The directory to which files should be uploaded on the server. - public SftpConfiguration(string username = null, string password = null, string uploadDirectory = null) + /// The directory to which files are uploaded. + public SftpConfiguration( + string username = null, + string password = null, + string uploadDirectory = null) { Username = username; Password = password; UploadDirectory = uploadDirectory; } - /// /// Initializes a new instance of the class. @@ -62,17 +64,17 @@ public SftpConfiguration(SftpConfiguration oldValue, SftpConfiguration newValue) } /// - /// Gets the sftp username. + /// Gets the Sftp username. /// public string Username { get; } /// - /// Gets the sftp password. + /// Gets the Sftp password. /// public string Password { get; } - + /// - /// Gets the directory to which files should be uploaded on the server. + /// Gets the directory to which files are uploaded. /// public string UploadDirectory { get; } } \ No newline at end of file diff --git a/src/Testcontainers.Sftp/SftpContainer.cs b/src/Testcontainers.Sftp/SftpContainer.cs index 13aaf150b..d93956f73 100644 --- a/src/Testcontainers.Sftp/SftpContainer.cs +++ b/src/Testcontainers.Sftp/SftpContainer.cs @@ -1,13 +1,15 @@ namespace Testcontainers.Sftp; +/// [PublicAPI] public sealed class SftpContainer : DockerContainer { - private readonly SftpConfiguration _configuration; - + /// + /// Initializes a new instance of the class. + /// + /// The container configuration. public SftpContainer(SftpConfiguration configuration) : base(configuration) { - _configuration = configuration; } } \ No newline at end of file diff --git a/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj b/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj index bdb00acf8..9a25b9c4d 100644 --- a/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj +++ b/src/Testcontainers.Sftp/Testcontainers.Sftp.csproj @@ -1,8 +1,7 @@ - + net8.0;net9.0;netstandard2.0;netstandard2.1 latest - Testcsontainers.Sftp diff --git a/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs b/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs index 5a01ae21a..72c2b8bcf 100644 --- a/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs +++ b/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs @@ -1,5 +1,4 @@ - -namespace Testcontainers.Sftp; +namespace Testcontainers.Sftp; public sealed class SftpContainerTest : IAsyncLifetime { @@ -20,14 +19,17 @@ public Task DisposeAsync() public async Task IsConnectedReturnsTrue() { // Given - var hostname = _sftpContainer.Hostname; + var host = _sftpContainer.Hostname; + var port = _sftpContainer.GetMappedPublicPort(SftpBuilder.SftpPort); - var client = new SftpClient(hostname, port, SftpBuilder.DefaultUsername, SftpBuilder.DefaultPassword); + + using var sftpClient = new SftpClient(host, port, SftpBuilder.DefaultUsername, SftpBuilder.DefaultPassword); // When - await client.ConnectAsync(CancellationToken.None); + await sftpClient.ConnectAsync(CancellationToken.None) + .ConfigureAwait(true); // Then - Assert.True(client.IsConnected); + Assert.True(sftpClient.IsConnected); } } \ No newline at end of file diff --git a/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj b/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj index a020b3d90..521044a5e 100644 --- a/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj +++ b/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj @@ -1,20 +1,17 @@  - - net9.0 - false - false - - - - - - - - - - - - - - - + + net9.0 + false + false + + + + + + + + + + + + \ No newline at end of file From f49c3a51b1fb8229995796f253e28446ada23520 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Mon, 10 Feb 2025 17:38:30 +0100 Subject: [PATCH 4/4] chore: Remove BOM --- tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs | 2 +- .../Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs b/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs index 72c2b8bcf..e064280f8 100644 --- a/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs +++ b/tests/Testcontainers.Sftp.Tests/SftpContainerTest.cs @@ -1,4 +1,4 @@ -namespace Testcontainers.Sftp; +namespace Testcontainers.Sftp; public sealed class SftpContainerTest : IAsyncLifetime { diff --git a/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj b/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj index 521044a5e..f9cfbbac6 100644 --- a/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj +++ b/tests/Testcontainers.Sftp.Tests/Testcontainers.Sftp.Tests.csproj @@ -1,4 +1,4 @@ - + net9.0 false