From 40227bffc295e4e046eba3350b66375c13f5d852 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sun, 2 Feb 2025 11:41:57 +0100 Subject: [PATCH] feat: Add Weaviate module (#1356) Co-authored-by: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> --- .github/workflows/cicd.yml | 1 + Testcontainers.dic | 3 +- Testcontainers.sln | 14 +++++ Testcontainers.sln.DotSettings | 11 ++-- src/Testcontainers.Weaviate/.editorconfig | 1 + .../Testcontainers.Weaviate.csproj | 12 ++++ src/Testcontainers.Weaviate/Usings.cs | 6 ++ .../WeaviateBuilder.cs | 61 +++++++++++++++++++ .../WeaviateConfiguration.cs | 53 ++++++++++++++++ .../WeaviateContainer.cs | 15 +++++ .../.editorconfig | 1 + .../Testcontainers.Weaviate.Tests.csproj | 17 ++++++ tests/Testcontainers.Weaviate.Tests/Usings.cs | 6 ++ .../WeaviateContainerTest.cs | 26 ++++++++ 14 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 src/Testcontainers.Weaviate/.editorconfig create mode 100644 src/Testcontainers.Weaviate/Testcontainers.Weaviate.csproj create mode 100644 src/Testcontainers.Weaviate/Usings.cs create mode 100644 src/Testcontainers.Weaviate/WeaviateBuilder.cs create mode 100644 src/Testcontainers.Weaviate/WeaviateConfiguration.cs create mode 100644 src/Testcontainers.Weaviate/WeaviateContainer.cs create mode 100644 tests/Testcontainers.Weaviate.Tests/.editorconfig create mode 100644 tests/Testcontainers.Weaviate.Tests/Testcontainers.Weaviate.Tests.csproj create mode 100644 tests/Testcontainers.Weaviate.Tests/Usings.cs create mode 100644 tests/Testcontainers.Weaviate.Tests/WeaviateContainerTest.cs diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index f2e1e531b..642ce1a33 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.Weaviate", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.WebDriver", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Xunit", runs-on: "ubuntu-22.04" } ] diff --git a/Testcontainers.dic b/Testcontainers.dic index 041996980..df1f16915 100644 --- a/Testcontainers.dic +++ b/Testcontainers.dic @@ -34,4 +34,5 @@ testcontainer testcontainers tlsverify toml -vstest \ No newline at end of file +vstest +weaviate \ No newline at end of file diff --git a/Testcontainers.sln b/Testcontainers.sln index 58609127f..444bdb1c3 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -97,6 +97,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda", " 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}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver", "src\Testcontainers.WebDriver\Testcontainers.WebDriver.csproj", "{64A87DE5-29B0-4A54-9E74-560484D8C7C0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Xunit", "src\Testcontainers.Xunit\Testcontainers.Xunit.csproj", "{380BB29B-F556-404D-B13B-CA250599C565}" @@ -201,6 +203,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus.T 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}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Xunit.Tests", "tests\Testcontainers.Xunit.Tests\Testcontainers.Xunit.Tests.csproj", "{E901DF14-6F05-4FC2-825A-3055FAD33561}" @@ -382,6 +386,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 + {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 + {68F8600D-24E9-4E03-9E25-5F6EB338EAC1}.Release|Any CPU.Build.0 = Release|Any CPU {64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -590,6 +598,10 @@ Global {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 {27CDB869-A150-4593-958F-6F26E5391E7C}.Release|Any CPU.Build.0 = Release|Any CPU + {DDB41BC8-5826-4D97-9C5F-001151E3FFD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDB41BC8-5826-4D97-9C5F-001151E3FFD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDB41BC8-5826-4D97-9C5F-001151E3FFD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDB41BC8-5826-4D97-9C5F-001151E3FFD6}.Release|Any CPU.Build.0 = Release|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -642,6 +654,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} + {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} {84911C93-C2A9-46E9-AE5E-D567306589E5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -694,6 +707,7 @@ Global {9E8E6AA5-65D1-498F-BEAB-BA34723A0050} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {232DD918-46ED-4BA8-B383-1A9146D83064} = {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} EndGlobalSection diff --git a/Testcontainers.sln.DotSettings b/Testcontainers.sln.DotSettings index c9af9ff53..ff93162f2 100644 --- a/Testcontainers.sln.DotSettings +++ b/Testcontainers.sln.DotSettings @@ -6,9 +6,9 @@ BlockScoped True True - True True True + True True True True @@ -42,10 +42,11 @@ True True True - True + True True - DO_NOT_SHOW - DO_NOT_SHOW - DO_NOT_SHOW + True + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW diff --git a/src/Testcontainers.Weaviate/.editorconfig b/src/Testcontainers.Weaviate/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/src/Testcontainers.Weaviate/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/src/Testcontainers.Weaviate/Testcontainers.Weaviate.csproj b/src/Testcontainers.Weaviate/Testcontainers.Weaviate.csproj new file mode 100644 index 000000000..9a25b9c4d --- /dev/null +++ b/src/Testcontainers.Weaviate/Testcontainers.Weaviate.csproj @@ -0,0 +1,12 @@ + + + net8.0;net9.0;netstandard2.0;netstandard2.1 + latest + + + + + + + + \ No newline at end of file diff --git a/src/Testcontainers.Weaviate/Usings.cs b/src/Testcontainers.Weaviate/Usings.cs new file mode 100644 index 000000000..79fd3af9b --- /dev/null +++ b/src/Testcontainers.Weaviate/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using Docker.DotNet.Models; +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/src/Testcontainers.Weaviate/WeaviateBuilder.cs b/src/Testcontainers.Weaviate/WeaviateBuilder.cs new file mode 100644 index 000000000..92d56dcaa --- /dev/null +++ b/src/Testcontainers.Weaviate/WeaviateBuilder.cs @@ -0,0 +1,61 @@ +namespace Testcontainers.Weaviate; + +/// +[PublicAPI] +public sealed class WeaviateBuilder : ContainerBuilder +{ + public const string WeaviateImage = "semitechnologies/weaviate:1.26.14"; + + public const ushort WeaviateHttpPort = 8080; + + public const ushort WeaviateGrpcPort = 50051; + + /// + /// Initializes a new instance of the class. + /// + public WeaviateBuilder() : this(new WeaviateConfiguration()) + => DockerResourceConfiguration = Init().DockerResourceConfiguration; + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + private WeaviateBuilder(WeaviateConfiguration resourceConfiguration) : base(resourceConfiguration) + => DockerResourceConfiguration = resourceConfiguration; + + /// + protected override WeaviateConfiguration DockerResourceConfiguration { get; } + + /// + public override WeaviateContainer Build() + { + Validate(); + return new WeaviateContainer(DockerResourceConfiguration); + } + + /// + protected override WeaviateBuilder Init() + => base.Init() + .WithImage(WeaviateImage) + .WithPortBinding(WeaviateHttpPort, true) + .WithPortBinding(WeaviateGrpcPort, true) + .WithEnvironment("AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED", "true") + .WithEnvironment("PERSISTENCE_DATA_PATH", "/var/lib/weaviate") + .WithWaitStrategy(Wait.ForUnixContainer() + .UntilPortIsAvailable(WeaviateHttpPort) + .UntilPortIsAvailable(WeaviateGrpcPort) + .UntilHttpRequestIsSucceeded(request => + request.ForPath("/v1/.well-known/ready").ForPort(WeaviateHttpPort))); + + /// + protected override WeaviateBuilder Clone(IResourceConfiguration resourceConfiguration) + => Merge(DockerResourceConfiguration, new WeaviateConfiguration(resourceConfiguration)); + + /// + protected override WeaviateBuilder Clone(IContainerConfiguration resourceConfiguration) + => Merge(DockerResourceConfiguration, new WeaviateConfiguration(resourceConfiguration)); + + /// + protected override WeaviateBuilder Merge(WeaviateConfiguration oldValue, WeaviateConfiguration newValue) + => new(new WeaviateConfiguration(oldValue, newValue)); +} \ No newline at end of file diff --git a/src/Testcontainers.Weaviate/WeaviateConfiguration.cs b/src/Testcontainers.Weaviate/WeaviateConfiguration.cs new file mode 100644 index 000000000..87d42c016 --- /dev/null +++ b/src/Testcontainers.Weaviate/WeaviateConfiguration.cs @@ -0,0 +1,53 @@ +namespace Testcontainers.Weaviate; + +/// +[PublicAPI] +public sealed class WeaviateConfiguration : ContainerConfiguration +{ + /// + /// Initializes a new instance of the class. + /// + public WeaviateConfiguration() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public WeaviateConfiguration(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 WeaviateConfiguration(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 WeaviateConfiguration(WeaviateConfiguration resourceConfiguration) + : this(new WeaviateConfiguration(), 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 WeaviateConfiguration(WeaviateConfiguration oldValue, WeaviateConfiguration newValue) + : base(oldValue, newValue) + { + } +} \ No newline at end of file diff --git a/src/Testcontainers.Weaviate/WeaviateContainer.cs b/src/Testcontainers.Weaviate/WeaviateContainer.cs new file mode 100644 index 000000000..6eba03c0d --- /dev/null +++ b/src/Testcontainers.Weaviate/WeaviateContainer.cs @@ -0,0 +1,15 @@ +namespace Testcontainers.Weaviate; + +/// +[PublicAPI] +public sealed class WeaviateContainer(WeaviateConfiguration configuration) : DockerContainer(configuration) +{ + /// + /// Gets the Weaviate base address. + /// + /// The Weaviate base address. + public string GetBaseAddress() + { + return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(WeaviateBuilder.WeaviateHttpPort)).ToString(); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.Weaviate.Tests/.editorconfig b/tests/Testcontainers.Weaviate.Tests/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/tests/Testcontainers.Weaviate.Tests/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/tests/Testcontainers.Weaviate.Tests/Testcontainers.Weaviate.Tests.csproj b/tests/Testcontainers.Weaviate.Tests/Testcontainers.Weaviate.Tests.csproj new file mode 100644 index 000000000..3d19526bb --- /dev/null +++ b/tests/Testcontainers.Weaviate.Tests/Testcontainers.Weaviate.Tests.csproj @@ -0,0 +1,17 @@ + + + net9.0 + false + false + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Testcontainers.Weaviate.Tests/Usings.cs b/tests/Testcontainers.Weaviate.Tests/Usings.cs new file mode 100644 index 000000000..dc15b5753 --- /dev/null +++ b/tests/Testcontainers.Weaviate.Tests/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Net; +global using System.Net.Http; +global using System.Threading.Tasks; +global using DotNet.Testcontainers.Commons; +global using Xunit; \ No newline at end of file diff --git a/tests/Testcontainers.Weaviate.Tests/WeaviateContainerTest.cs b/tests/Testcontainers.Weaviate.Tests/WeaviateContainerTest.cs new file mode 100644 index 000000000..76b96a50f --- /dev/null +++ b/tests/Testcontainers.Weaviate.Tests/WeaviateContainerTest.cs @@ -0,0 +1,26 @@ +namespace Testcontainers.Weaviate; + +public sealed class WeaviateContainerTest : IAsyncLifetime +{ + private readonly WeaviateContainer _weaviateContainer = new WeaviateBuilder().Build(); + + public Task InitializeAsync() => _weaviateContainer.StartAsync(); + + public Task DisposeAsync() => _weaviateContainer.DisposeAsync().AsTask(); + + [Fact] + [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] + public async Task GetSchemaReturnsHttpStatusCodeOk() + { + // Given + using var httpClient = new HttpClient(); + httpClient.BaseAddress = new Uri(_weaviateContainer.GetBaseAddress()); + + // When + using var httpResponse = await httpClient.GetAsync("v1/schema") + .ConfigureAwait(true); + + // Then + Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode); + } +} \ No newline at end of file