diff --git a/Directory.Packages.props b/Directory.Packages.props index d2e90f1f5..084e280f2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -62,7 +62,7 @@ - + diff --git a/src/Testcontainers.Oracle/OracleBuilder.cs b/src/Testcontainers.Oracle/OracleBuilder.cs index c5cd3339e..c259f103e 100644 --- a/src/Testcontainers.Oracle/OracleBuilder.cs +++ b/src/Testcontainers.Oracle/OracleBuilder.cs @@ -8,6 +8,7 @@ public sealed class OracleBuilder : ContainerBuilder 11 and < 23")] public const string DefaultDatabase = "XEPDB1"; public const string DefaultUsername = "oracle"; @@ -59,20 +60,55 @@ public OracleBuilder WithPassword(string password) .WithEnvironment("APP_USER_PASSWORD", password); } + /// + /// Sets the Oracle database. + /// + /// + /// The database can only be set for Oracle 18 and onwards. + /// + /// The Oracle database. + /// A configured instance of . + public OracleBuilder WithDatabase(string database) + { + return Merge(DockerResourceConfiguration, new OracleConfiguration(database: database)); + } + /// public override OracleContainer Build() { Validate(); + + var defaultServiceName = GetDefaultServiceName(); + if (DockerResourceConfiguration.Database == null) + { + return new OracleContainer(WithDatabase(defaultServiceName).DockerResourceConfiguration); + } + + if (DockerResourceConfiguration.Database != defaultServiceName) + { + return new OracleContainer(WithEnvironment("ORACLE_DATABASE", DockerResourceConfiguration.Database).DockerResourceConfiguration); + } + return new OracleContainer(DockerResourceConfiguration); } + private string GetDefaultServiceName() + { + if (DockerResourceConfiguration.Image.MatchVersion(v => v.Major >= 23)) + return "FREEPDB1"; + + if (DockerResourceConfiguration.Image.MatchVersion(v => v.Major > 11)) + return "XEPDB1"; + + return "XE"; + } + /// protected override OracleBuilder Init() { return base.Init() .WithImage(OracleImage) .WithPortBinding(OraclePort, true) - .WithDatabase(DefaultDatabase) .WithUsername(DefaultUsername) .WithPassword(DefaultPassword) .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("DATABASE IS READY TO USE!")); @@ -86,6 +122,9 @@ protected override void Validate() _ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password)) .NotNull() .NotEmpty(); + + if (DockerResourceConfiguration.Database != null && DockerResourceConfiguration.Image.MatchVersion(v => v.Major < 18)) + throw new NotSupportedException($"Setting the database is not supported with {DockerResourceConfiguration.Image.FullName}. It is only supported on Oracle 18 and onwards."); } /// @@ -105,17 +144,4 @@ protected override OracleBuilder Merge(OracleConfiguration oldValue, OracleConfi { return new OracleBuilder(new OracleConfiguration(oldValue, newValue)); } - - /// - /// Sets the Oracle database. - /// - /// - /// The Docker image does not allow to configure the database. - /// - /// The Oracle database. - /// A configured instance of . - private OracleBuilder WithDatabase(string database) - { - return Merge(DockerResourceConfiguration, new OracleConfiguration(database: database)); - } } \ No newline at end of file diff --git a/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs b/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs index 5b6984dd0..d0d92c310 100644 --- a/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs +++ b/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs @@ -1,25 +1,13 @@ namespace Testcontainers.Oracle; -public sealed class OracleContainerTest : IAsyncLifetime +public abstract class OracleContainerTest(OracleContainerTest.OracleFixture oracleFixture) { - private readonly OracleContainer _oracleContainer = new OracleBuilder().Build(); - - public Task InitializeAsync() - { - return _oracleContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _oracleContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new OracleConnection(_oracleContainer.GetConnectionString()); + using DbConnection connection = oracleFixture.CreateConnection(); // When connection.Open(); @@ -36,11 +24,51 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1 FROM DUAL;"; // When - var execResult = await _oracleContainer.ExecScriptAsync(scriptContent) + var execResult = await oracleFixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } + + public abstract class OracleFixture(IMessageSink messageSink, string edition, int? version, string database = null) : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory => OracleClientFactory.Instance; + + protected override OracleBuilder Configure(OracleBuilder builder) + { + if (edition == default && version == default) + { + return builder; + } + + var image = $"gvenzl/oracle-{edition}:{version}-slim-faststart"; + return database == null ? builder.WithImage(image) : builder.WithImage(image).WithDatabase(database); + } + } + + [UsedImplicitly] public sealed class OracleDefault(OracleDefaultFixture fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle11(Oracle11Fixture fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle18(Oracle18Fixture fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle21(Oracle21Fixture fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle23(Oracle23Fixture fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle18Default(Oracle18FixtureDefault fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle21Default(Oracle21FixtureDefault fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle23Default(Oracle23FixtureDefault fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle18Scott(Oracle18FixtureScott fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle21Scott(Oracle21FixtureScott fixture) : OracleContainerTest(fixture), IClassFixture; + [UsedImplicitly] public sealed class Oracle23Scott(Oracle23FixtureScott fixture) : OracleContainerTest(fixture), IClassFixture; + + [UsedImplicitly] public class OracleDefaultFixture(IMessageSink messageSink) : OracleFixture(messageSink, default, default); + [UsedImplicitly] public class Oracle11Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 11); + [UsedImplicitly] public class Oracle18Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18); + [UsedImplicitly] public class Oracle21Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21); + [UsedImplicitly] public class Oracle23Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23); + [UsedImplicitly] public class Oracle18FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18, "XEPDB1"); + [UsedImplicitly] public class Oracle21FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21, "XEPDB1"); + [UsedImplicitly] public class Oracle23FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23, "FREEPDB1"); + [UsedImplicitly] public class Oracle18FixtureScott(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18, "SCOTT"); + [UsedImplicitly] public class Oracle21FixtureScott(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21, "SCOTT"); + [UsedImplicitly] public class Oracle23FixtureScott(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23, "SCOTT"); } \ No newline at end of file diff --git a/tests/Testcontainers.Oracle.Tests/Testcontainers.Oracle.Tests.csproj b/tests/Testcontainers.Oracle.Tests/Testcontainers.Oracle.Tests.csproj index 4d619cd84..264c5af93 100644 --- a/tests/Testcontainers.Oracle.Tests/Testcontainers.Oracle.Tests.csproj +++ b/tests/Testcontainers.Oracle.Tests/Testcontainers.Oracle.Tests.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.Oracle.Tests/Usings.cs b/tests/Testcontainers.Oracle.Tests/Usings.cs index eb37bd7e3..e1e61a204 100644 --- a/tests/Testcontainers.Oracle.Tests/Usings.cs +++ b/tests/Testcontainers.Oracle.Tests/Usings.cs @@ -2,5 +2,8 @@ global using System.Data.Common; global using System.Threading.Tasks; global using DotNet.Testcontainers.Commons; +global using JetBrains.Annotations; global using Oracle.ManagedDataAccess.Client; -global using Xunit; \ No newline at end of file +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file