diff --git a/Packages.props b/Packages.props index b39430ff1..ef7c70552 100644 --- a/Packages.props +++ b/Packages.props @@ -8,7 +8,7 @@ - + diff --git a/Testcontainers.sln b/Testcontainers.sln index 0969d4f4e..813d4d211 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -87,6 +87,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Couchbase.Te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CouchDb.Tests", "tests\Testcontainers.CouchDb.Tests\Testcontainers.CouchDb.Tests.csproj", "{E4520FB1-4466-4DCA-AD08-4075102C68D3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Databases.Tests", "tests\Testcontainers.Databases.Tests\Testcontainers.Databases.Tests.csproj", "{DA54916E-1128-4200-B6AE-9F5BF02D832D}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.DynamoDb.Tests", "tests\Testcontainers.DynamoDb.Tests\Testcontainers.DynamoDb.Tests.csproj", "{101515E6-74C1-40F9-85C8-871F742A378D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Elasticsearch.Tests", "tests\Testcontainers.Elasticsearch.Tests\Testcontainers.Elasticsearch.Tests.csproj", "{DD5B3678-468F-4D73-AECE-705E3D66CD43}" @@ -302,6 +304,10 @@ Global {E4520FB1-4466-4DCA-AD08-4075102C68D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {E4520FB1-4466-4DCA-AD08-4075102C68D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {E4520FB1-4466-4DCA-AD08-4075102C68D3}.Release|Any CPU.Build.0 = Release|Any CPU + {DA54916E-1128-4200-B6AE-9F5BF02D832D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA54916E-1128-4200-B6AE-9F5BF02D832D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA54916E-1128-4200-B6AE-9F5BF02D832D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA54916E-1128-4200-B6AE-9F5BF02D832D}.Release|Any CPU.Build.0 = Release|Any CPU {101515E6-74C1-40F9-85C8-871F742A378D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {101515E6-74C1-40F9-85C8-871F742A378D}.Debug|Any CPU.Build.0 = Debug|Any CPU {101515E6-74C1-40F9-85C8-871F742A378D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -457,6 +463,7 @@ Global {BD445A54-F411-4758-955E-397A1E98680C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {809322BA-D690-4F2B-B884-23F895663963} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {E4520FB1-4466-4DCA-AD08-4075102C68D3} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} + {DA54916E-1128-4200-B6AE-9F5BF02D832D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {101515E6-74C1-40F9-85C8-871F742A378D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {DD5B3678-468F-4D73-AECE-705E3D66CD43} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {64F8E9B9-78FD-4E13-BDDF-0340E2D4E1D0} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} diff --git a/src/Testcontainers.ClickHouse/ClickHouseContainer.cs b/src/Testcontainers.ClickHouse/ClickHouseContainer.cs index 3b7328860..d1bc808ff 100644 --- a/src/Testcontainers.ClickHouse/ClickHouseContainer.cs +++ b/src/Testcontainers.ClickHouse/ClickHouseContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.ClickHouse; /// [PublicAPI] -public sealed class ClickHouseContainer : DockerContainer +public sealed class ClickHouseContainer : DockerContainer, IDatabaseContainer { private readonly ClickHouseConfiguration _configuration; diff --git a/src/Testcontainers.MariaDb/MariaDbContainer.cs b/src/Testcontainers.MariaDb/MariaDbContainer.cs index 97fba03c9..08b603344 100644 --- a/src/Testcontainers.MariaDb/MariaDbContainer.cs +++ b/src/Testcontainers.MariaDb/MariaDbContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.MariaDb; /// [PublicAPI] -public sealed class MariaDbContainer : DockerContainer +public sealed class MariaDbContainer : DockerContainer, IDatabaseContainer { private readonly MariaDbConfiguration _configuration; diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs index 865f38e20..78c8f2c36 100644 --- a/src/Testcontainers.MsSql/MsSqlContainer.cs +++ b/src/Testcontainers.MsSql/MsSqlContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.MsSql; /// [PublicAPI] -public sealed class MsSqlContainer : DockerContainer +public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer { private readonly MsSqlConfiguration _configuration; diff --git a/src/Testcontainers.MySql/MySqlContainer.cs b/src/Testcontainers.MySql/MySqlContainer.cs index 59dc19c8b..5b55be35e 100644 --- a/src/Testcontainers.MySql/MySqlContainer.cs +++ b/src/Testcontainers.MySql/MySqlContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.MySql; /// [PublicAPI] -public sealed class MySqlContainer : DockerContainer +public sealed class MySqlContainer : DockerContainer, IDatabaseContainer { private readonly MySqlConfiguration _configuration; diff --git a/src/Testcontainers.Oracle/OracleContainer.cs b/src/Testcontainers.Oracle/OracleContainer.cs index fa8cdab92..e1b1ae4c1 100644 --- a/src/Testcontainers.Oracle/OracleContainer.cs +++ b/src/Testcontainers.Oracle/OracleContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.Oracle; /// [PublicAPI] -public sealed class OracleContainer : DockerContainer +public sealed class OracleContainer : DockerContainer, IDatabaseContainer { private readonly OracleConfiguration _configuration; diff --git a/src/Testcontainers.PostgreSql/PostgreSqlContainer.cs b/src/Testcontainers.PostgreSql/PostgreSqlContainer.cs index 3ac48447c..c771e6f11 100644 --- a/src/Testcontainers.PostgreSql/PostgreSqlContainer.cs +++ b/src/Testcontainers.PostgreSql/PostgreSqlContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.PostgreSql; /// [PublicAPI] -public sealed class PostgreSqlContainer : DockerContainer +public sealed class PostgreSqlContainer : DockerContainer, IDatabaseContainer { private readonly PostgreSqlConfiguration _configuration; diff --git a/src/Testcontainers.SqlEdge/SqlEdgeContainer.cs b/src/Testcontainers.SqlEdge/SqlEdgeContainer.cs index ccbb3124b..17ff0f4f1 100644 --- a/src/Testcontainers.SqlEdge/SqlEdgeContainer.cs +++ b/src/Testcontainers.SqlEdge/SqlEdgeContainer.cs @@ -2,7 +2,7 @@ namespace Testcontainers.SqlEdge; /// [PublicAPI] -public sealed class SqlEdgeContainer : DockerContainer +public sealed class SqlEdgeContainer : DockerContainer, IDatabaseContainer { private readonly SqlEdgeConfiguration _configuration; diff --git a/src/Testcontainers/Clients/ContainerConfigurationConverter.cs b/src/Testcontainers/Clients/ContainerConfigurationConverter.cs index 7a2736117..84f7dfb7c 100644 --- a/src/Testcontainers/Clients/ContainerConfigurationConverter.cs +++ b/src/Testcontainers/Clients/ContainerConfigurationConverter.cs @@ -50,7 +50,7 @@ public ContainerConfigurationConverter(IContainerConfiguration configuration) private static string GetQualifiedPort(string containerPort) { - return new[] { UdpPortSuffix, TcpPortSuffix, SctpPortSuffix }.Any(portSuffix => containerPort.EndsWith(portSuffix, StringComparison.OrdinalIgnoreCase)) ? containerPort.ToLowerInvariant() : containerPort + TcpPortSuffix; + return Array.Exists(new[] { UdpPortSuffix, TcpPortSuffix, SctpPortSuffix }, portSuffix => containerPort.EndsWith(portSuffix, StringComparison.OrdinalIgnoreCase)) ? containerPort.ToLowerInvariant() : containerPort + TcpPortSuffix; } private sealed class ToCollection : CollectionConverter diff --git a/src/Testcontainers/Clients/DockerContainerOperations.cs b/src/Testcontainers/Clients/DockerContainerOperations.cs index de7526acc..33d7e7d9e 100644 --- a/src/Testcontainers/Clients/DockerContainerOperations.cs +++ b/src/Testcontainers/Clients/DockerContainerOperations.cs @@ -34,21 +34,11 @@ public async Task> GetAllAsync(FilterByProper .ConfigureAwait(false); } - public Task ByIdAsync(string id, CancellationToken ct = default) - { - return ByPropertyAsync("id", id, ct); - } - - public Task ByNameAsync(string name, CancellationToken ct = default) - { - return ByPropertyAsync("name", name, ct); - } - - public async Task ByPropertyAsync(string property, string value, CancellationToken ct = default) + public async Task ByIdAsync(string id, CancellationToken ct = default) { try { - return await Docker.Containers.InspectContainerAsync(value, ct) + return await Docker.Containers.InspectContainerAsync(id, ct) .ConfigureAwait(false); } catch (DockerApiException) @@ -65,18 +55,12 @@ public async Task ExistsWithIdAsync(string id, CancellationToken ct = defa return response != null; } - public async Task ExistsWithNameAsync(string name, CancellationToken ct = default) + public async Task GetExitCodeAsync(string id, CancellationToken ct = default) { - var response = await ByNameAsync(name, ct) + var response = await Docker.Containers.WaitContainerAsync(id, ct) .ConfigureAwait(false); - return response != null; - } - - public async Task GetExitCodeAsync(string id, CancellationToken ct = default) - { - return (await Docker.Containers.WaitContainerAsync(id, ct) - .ConfigureAwait(false)).StatusCode; + return response.StatusCode; } public async Task<(string Stdout, string Stderr)> GetLogsAsync(string id, TimeSpan since, TimeSpan until, bool timestampsEnabled = true, CancellationToken ct = default) diff --git a/src/Testcontainers/Clients/DockerImageOperations.cs b/src/Testcontainers/Clients/DockerImageOperations.cs index f469bd569..7dc2df009 100644 --- a/src/Testcontainers/Clients/DockerImageOperations.cs +++ b/src/Testcontainers/Clients/DockerImageOperations.cs @@ -37,21 +37,11 @@ public async Task> GetAllAsync(FilterByProperty .ConfigureAwait(false); } - public Task ByIdAsync(string id, CancellationToken ct = default) - { - return ByPropertyAsync("id", id, ct); - } - - public Task ByNameAsync(string name, CancellationToken ct = default) - { - return ByPropertyAsync("reference", name, ct); - } - - public async Task ByPropertyAsync(string property, string value, CancellationToken ct = default) + public async Task ByIdAsync(string id, CancellationToken ct = default) { try { - return await Docker.Images.InspectImageAsync(value, ct) + return await Docker.Images.InspectImageAsync(id, ct) .ConfigureAwait(false); } catch (DockerApiException) @@ -68,14 +58,6 @@ public async Task ExistsWithIdAsync(string id, CancellationToken ct = defa return response != null; } - public async Task ExistsWithNameAsync(string name, CancellationToken ct = default) - { - var response = await ByNameAsync(name, ct) - .ConfigureAwait(false); - - return response != null; - } - public async Task CreateAsync(IImage image, IDockerRegistryAuthenticationConfiguration dockerRegistryAuthConfig, CancellationToken ct = default) { var createParameters = new ImagesCreateParameters @@ -108,7 +90,7 @@ public async Task BuildAsync(IImageFromDockerfileConfiguration configura { var image = configuration.Image; - var imageExists = await ExistsWithNameAsync(image.FullName, ct) + var imageExists = await ExistsWithIdAsync(image.FullName, ct) .ConfigureAwait(false); if (imageExists && configuration.DeleteIfExists.HasValue && configuration.DeleteIfExists.Value) @@ -143,7 +125,7 @@ await DeleteAsync(image, ct) await Docker.Images.BuildImageFromDockerfileAsync(buildParameters, dockerfileArchiveStream, Array.Empty(), new Dictionary(), _traceProgress, ct) .ConfigureAwait(false); - var imageHasBeenCreated = await ExistsWithNameAsync(image.FullName, ct) + var imageHasBeenCreated = await ExistsWithIdAsync(image.FullName, ct) .ConfigureAwait(false); if (!imageHasBeenCreated) diff --git a/src/Testcontainers/Clients/DockerNetworkOperations.cs b/src/Testcontainers/Clients/DockerNetworkOperations.cs index 503db9c0e..b0afbf444 100644 --- a/src/Testcontainers/Clients/DockerNetworkOperations.cs +++ b/src/Testcontainers/Clients/DockerNetworkOperations.cs @@ -32,21 +32,11 @@ public async Task> GetAllAsync(FilterByProperty fil .ConfigureAwait(false); } - public Task ByIdAsync(string id, CancellationToken ct = default) - { - return ByPropertyAsync("id", id, ct); - } - - public Task ByNameAsync(string name, CancellationToken ct = default) - { - return ByPropertyAsync("name", name, ct); - } - - public async Task ByPropertyAsync(string property, string value, CancellationToken ct = default) + public async Task ByIdAsync(string id, CancellationToken ct = default) { try { - return await Docker.Networks.InspectNetworkAsync(value, ct) + return await Docker.Networks.InspectNetworkAsync(id, ct) .ConfigureAwait(false); } catch (DockerApiException) @@ -63,14 +53,6 @@ public async Task ExistsWithIdAsync(string id, CancellationToken ct = defa return response != null; } - public async Task ExistsWithNameAsync(string name, CancellationToken ct = default) - { - var response = await ByNameAsync(name, ct) - .ConfigureAwait(false); - - return response != null; - } - public async Task CreateAsync(INetworkConfiguration configuration, CancellationToken ct = default) { var createParameters = new NetworksCreateParameters diff --git a/src/Testcontainers/Clients/DockerSystemOperations.cs b/src/Testcontainers/Clients/DockerSystemOperations.cs index ad2751c85..72853fcfc 100644 --- a/src/Testcontainers/Clients/DockerSystemOperations.cs +++ b/src/Testcontainers/Clients/DockerSystemOperations.cs @@ -19,6 +19,7 @@ public async Task GetIsWindowsEngineEnabled(CancellationToken ct = default { var version = await GetVersionAsync(ct) .ConfigureAwait(false); + return version.Os.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) > -1; } diff --git a/src/Testcontainers/Clients/DockerVolumeOperations.cs b/src/Testcontainers/Clients/DockerVolumeOperations.cs index 4224bd92b..e1266d809 100644 --- a/src/Testcontainers/Clients/DockerVolumeOperations.cs +++ b/src/Testcontainers/Clients/DockerVolumeOperations.cs @@ -36,21 +36,11 @@ public async Task> GetAllAsync(FilterByProperty filt return response.Volumes; } - public Task ByIdAsync(string id, CancellationToken ct = default) - { - return ByPropertyAsync("id", id, ct); - } - - public Task ByNameAsync(string name, CancellationToken ct = default) - { - return ByPropertyAsync("name", name, ct); - } - - public async Task ByPropertyAsync(string property, string value, CancellationToken ct = default) + public async Task ByIdAsync(string id, CancellationToken ct = default) { try { - return await Docker.Volumes.InspectAsync(value, ct) + return await Docker.Volumes.InspectAsync(id, ct) .ConfigureAwait(false); } catch (DockerApiException) @@ -67,14 +57,6 @@ public async Task ExistsWithIdAsync(string id, CancellationToken ct = defa return response != null; } - public async Task ExistsWithNameAsync(string name, CancellationToken ct = default) - { - var response = await ByNameAsync(name, ct) - .ConfigureAwait(false); - - return response != null; - } - public async Task CreateAsync(IVolumeConfiguration configuration, CancellationToken ct = default) { var createParameters = new VolumesCreateParameters diff --git a/src/Testcontainers/Clients/IHasListOperations.cs b/src/Testcontainers/Clients/IHasListOperations.cs index e22229e6b..e5421df1d 100644 --- a/src/Testcontainers/Clients/IHasListOperations.cs +++ b/src/Testcontainers/Clients/IHasListOperations.cs @@ -12,12 +12,6 @@ internal interface IHasListOperations Task ByIdAsync(string id, CancellationToken ct = default); - Task ByNameAsync(string name, CancellationToken ct = default); - - Task ByPropertyAsync(string property, string value, CancellationToken ct = default); - Task ExistsWithIdAsync(string id, CancellationToken ct = default); - - Task ExistsWithNameAsync(string name, CancellationToken ct = default); } } diff --git a/src/Testcontainers/Clients/TestcontainersClient.cs b/src/Testcontainers/Clients/TestcontainersClient.cs index 2d9e45587..737b82bf3 100644 --- a/src/Testcontainers/Clients/TestcontainersClient.cs +++ b/src/Testcontainers/Clients/TestcontainersClient.cs @@ -293,7 +293,7 @@ public async Task RunAsync(IContainerConfiguration configuration, Cancel .ConfigureAwait(false); } - var cachedImage = await Image.ByNameAsync(configuration.Image.FullName, ct) + var cachedImage = await Image.ByIdAsync(configuration.Image.FullName, ct) .ConfigureAwait(false); if (configuration.ImagePullPolicy(cachedImage)) @@ -323,7 +323,7 @@ await Task.WhenAll(configuration.ResourceMappings.Values.Select(resourceMapping /// public async Task BuildAsync(IImageFromDockerfileConfiguration configuration, CancellationToken ct = default) { - var cachedImage = await Image.ByNameAsync(configuration.Image.FullName, ct) + var cachedImage = await Image.ByIdAsync(configuration.Image.FullName, ct) .ConfigureAwait(false); if (configuration.ImageBuildPolicy(cachedImage)) diff --git a/src/Testcontainers/Containers/DockerContainer.cs b/src/Testcontainers/Containers/DockerContainer.cs index e7db53b33..1e9bbf855 100644 --- a/src/Testcontainers/Containers/DockerContainer.cs +++ b/src/Testcontainers/Containers/DockerContainer.cs @@ -7,7 +7,6 @@ namespace DotNet.Testcontainers.Containers using System.Linq; using System.Threading; using System.Threading.Tasks; - using Docker.DotNet; using Docker.DotNet.Models; using DotNet.Testcontainers.Clients; using DotNet.Testcontainers.Configurations; @@ -472,15 +471,8 @@ protected virtual async Task UnsafeStopAsync(CancellationToken ct = default) await _client.StopAsync(_container.ID, ct) .ConfigureAwait(false); - try - { - _container = await _client.Container.ByIdAsync(_container.ID, ct) - .ConfigureAwait(false); - } - catch (DockerApiException) - { - _container = new ContainerInspectResponse(); - } + _container = await _client.Container.ByIdAsync(_container.ID, ct) + .ConfigureAwait(false); Stopped?.Invoke(this, EventArgs.Empty); } @@ -488,7 +480,7 @@ await _client.StopAsync(_container.ID, ct) /// protected override bool Exists() { - return ContainerHasBeenCreatedStates.HasFlag(State); + return _container != null && ContainerHasBeenCreatedStates.HasFlag(State); } } } diff --git a/src/Testcontainers/Containers/IDatabaseContainer.cs b/src/Testcontainers/Containers/IDatabaseContainer.cs new file mode 100644 index 000000000..8a97e6766 --- /dev/null +++ b/src/Testcontainers/Containers/IDatabaseContainer.cs @@ -0,0 +1,18 @@ +namespace DotNet.Testcontainers.Containers +{ + using JetBrains.Annotations; + + /// + /// Represents a database container instance that can be accessed with an ADO.NET provider. + /// + [PublicAPI] + public interface IDatabaseContainer + { + /// + /// Gets the database connection string. + /// + /// The database connection string. + [NotNull] + string GetConnectionString(); + } +} diff --git a/src/Testcontainers/Images/FutureDockerImage.cs b/src/Testcontainers/Images/FutureDockerImage.cs index bb3b2be13..211c690b2 100644 --- a/src/Testcontainers/Images/FutureDockerImage.cs +++ b/src/Testcontainers/Images/FutureDockerImage.cs @@ -115,7 +115,7 @@ protected override async Task UnsafeCreateAsync(CancellationToken ct = default) _ = await _client.BuildAsync(_configuration, ct) .ConfigureAwait(false); - _image = await _client.Image.ByNameAsync(_configuration.Image.FullName, ct) + _image = await _client.Image.ByIdAsync(_configuration.Image.FullName, ct) .ConfigureAwait(false); } diff --git a/src/Testcontainers/Images/MatchImage.cs b/src/Testcontainers/Images/MatchImage.cs index 8819cdcc7..58c5d227f 100644 --- a/src/Testcontainers/Images/MatchImage.cs +++ b/src/Testcontainers/Images/MatchImage.cs @@ -5,32 +5,33 @@ namespace DotNet.Testcontainers.Images internal static class MatchImage { + private static readonly char[] SlashSeparator = { '/' }; + + private static readonly char[] ColonSeparator = { ':' }; + public static IImage Match(string image) { _ = Guard.Argument(image, nameof(image)) .NotNull() .NotEmpty(); - var imageComponents = image - .Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + var imageComponents = image.Split(SlashSeparator, StringSplitOptions.RemoveEmptyEntries); + + var registry = string.Join("/", imageComponents.Take(imageComponents.Length - 1)); - var repository = string.Join("/", imageComponents - .Take(imageComponents.Length - 1)); + imageComponents = imageComponents[imageComponents.Length - 1].Split(ColonSeparator, StringSplitOptions.RemoveEmptyEntries); - var name = imageComponents - .Last() - .Split(':') - .DefaultIfEmpty(string.Empty) - .First(); + if (2.Equals(imageComponents.Length)) + { + return new DockerImage(registry, imageComponents[0], imageComponents[1]); + } - var tag = imageComponents - .Last() - .Split(':') - .Skip(1) - .DefaultIfEmpty(string.Empty) - .First(); + if (1.Equals(imageComponents.Length)) + { + return new DockerImage(registry, imageComponents[0], string.Empty); + } - return new DockerImage(repository, name, tag); + throw new ArgumentException("Cannot parse image: " + image, nameof(image)); } } } diff --git a/tests/Testcontainers.Databases.Tests/.editorconfig b/tests/Testcontainers.Databases.Tests/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/tests/Testcontainers.Databases.Tests/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/tests/Testcontainers.Databases.Tests/DatabasesContainerTest.cs b/tests/Testcontainers.Databases.Tests/DatabasesContainerTest.cs new file mode 100644 index 000000000..694f7b5ec --- /dev/null +++ b/tests/Testcontainers.Databases.Tests/DatabasesContainerTest.cs @@ -0,0 +1,54 @@ +namespace Testcontainers.Databases; + +public sealed class DatabaseContainersTest +{ + [Theory] + [MemberData(nameof(GetContainerImplementations), parameters: true)] + public void ShouldImplementIDatabaseContainer(Type type) + { + Assert.True(type.IsAssignableTo(typeof(IDatabaseContainer)), $"The type '{type.Name}' does not implement the database interface."); + } + + [Theory] + [MemberData(nameof(GetContainerImplementations), parameters: false)] + public void ShouldNotImplementIDatabaseContainer(Type type) + { + Assert.False(type.IsAssignableTo(typeof(IDatabaseContainer)), $"The type '{type.Name}' does implement the database interface."); + } + + public static IEnumerable GetContainerImplementations(bool expectDataProvider) + { + var testAssemblies = Directory.GetFiles(".", "Testcontainers.*.Tests.dll", SearchOption.TopDirectoryOnly) + .Select(Path.GetFullPath) + .Select(Assembly.LoadFrom) + .ToDictionary(assembly => assembly, assembly => assembly.GetReferencedAssemblies() + .Where(referencedAssembly => referencedAssembly.Name != null) + .Where(referencedAssembly => !referencedAssembly.Name.StartsWith("System")) + .Where(referencedAssembly => !referencedAssembly.Name.StartsWith("xunit")) + .Where(referencedAssembly => !referencedAssembly.Name.Equals("Microsoft.VisualStudio.TestPlatform.ObjectModel")) + .Where(referencedAssembly => !referencedAssembly.Name.Equals("Docker.DotNet")) + .Where(referencedAssembly => !referencedAssembly.Name.Equals("Testcontainers")) + .Select(Assembly.Load) + .SelectMany(referencedAssembly => referencedAssembly.ExportedTypes) + .ToImmutableList()); + + foreach (var testAssembly in testAssemblies) + { + // TODO: If a module contains multiple container implementations, it would require all container implementations to implement the interface. + foreach (var containerType in testAssembly.Value.Where(type => type.IsAssignableTo(typeof(IContainer)))) + { + var hasDataProvider = testAssembly.Value.Exists(type => type.IsSubclassOf(typeof(DbProviderFactory))); + + if (expectDataProvider && hasDataProvider) + { + yield return new object[] { containerType }; + } + + if (!expectDataProvider && !hasDataProvider) + { + yield return new object[] { containerType }; + } + } + } + } +} \ No newline at end of file diff --git a/tests/Testcontainers.Databases.Tests/Testcontainers.Databases.Tests.csproj b/tests/Testcontainers.Databases.Tests/Testcontainers.Databases.Tests.csproj new file mode 100644 index 000000000..cf189875f --- /dev/null +++ b/tests/Testcontainers.Databases.Tests/Testcontainers.Databases.Tests.csproj @@ -0,0 +1,17 @@ + + + net6.0 + false + false + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Testcontainers.Databases.Tests/Usings.cs b/tests/Testcontainers.Databases.Tests/Usings.cs new file mode 100644 index 000000000..2ea483b34 --- /dev/null +++ b/tests/Testcontainers.Databases.Tests/Usings.cs @@ -0,0 +1,9 @@ +global using System; +global using System.Collections.Generic; +global using System.Collections.Immutable; +global using System.Data.Common; +global using System.IO; +global using System.Linq; +global using System.Reflection; +global using DotNet.Testcontainers.Containers; +global using Xunit; \ No newline at end of file diff --git a/tests/Testcontainers.Tests/Unit/Configurations/ResourcePropertiesTest.cs b/tests/Testcontainers.Tests/Unit/Configurations/ResourcePropertiesTest.cs index 2e39f06ef..6b9c3d6b8 100644 --- a/tests/Testcontainers.Tests/Unit/Configurations/ResourcePropertiesTest.cs +++ b/tests/Testcontainers.Tests/Unit/Configurations/ResourcePropertiesTest.cs @@ -44,34 +44,6 @@ public async Task QueryNotExistingDockerVolumeById() .ConfigureAwait(false)); } - [Fact] - public async Task QueryNotExistingDockerContainerByName() - { - Assert.False(await Client.Container.ExistsWithNameAsync(ResourceIdOrName) - .ConfigureAwait(false)); - } - - [Fact] - public async Task QueryNotExistingDockerImageByName() - { - Assert.False(await Client.Image.ExistsWithNameAsync(ResourceIdOrName) - .ConfigureAwait(false)); - } - - [Fact] - public async Task QueryNotExistingDockerNetworkByName() - { - Assert.False(await Client.Network.ExistsWithNameAsync(ResourceIdOrName) - .ConfigureAwait(false)); - } - - [Fact] - public async Task QueryNotExistingDockerVolumeByName() - { - Assert.False(await Client.Volume.ExistsWithNameAsync(ResourceIdOrName) - .ConfigureAwait(false)); - } - [Fact] public async Task QueryContainerInformationOfCreatedContainer() { diff --git a/tests/Testcontainers.Tests/Unit/Networks/TestcontainerNetworkBuilderTest.cs b/tests/Testcontainers.Tests/Unit/Networks/TestcontainerNetworkBuilderTest.cs index 3de38aa54..62c5bafc0 100644 --- a/tests/Testcontainers.Tests/Unit/Networks/TestcontainerNetworkBuilderTest.cs +++ b/tests/Testcontainers.Tests/Unit/Networks/TestcontainerNetworkBuilderTest.cs @@ -52,7 +52,7 @@ public async Task CreateNetworkAssignsOptions() var client = new TestcontainersClient(ResourceReaper.DefaultSessionId, TestcontainersSettings.OS.DockerEndpointAuthConfig, NullLogger.Instance); // When - var networkResponse = await client.Network.ByNameAsync(_network.Name) + var networkResponse = await client.Network.ByIdAsync(_network.Name) .ConfigureAwait(false); // Then @@ -66,7 +66,7 @@ public async Task CreateNetworkAssignsLabels() var client = new TestcontainersClient(ResourceReaper.DefaultSessionId, TestcontainersSettings.OS.DockerEndpointAuthConfig, NullLogger.Instance); // When - var networkResponse = await client.Network.ByNameAsync(_network.Name) + var networkResponse = await client.Network.ByIdAsync(_network.Name) .ConfigureAwait(false); // Then diff --git a/tests/Testcontainers.Tests/Unit/Volumes/TestcontainersVolumeBuilderTest.cs b/tests/Testcontainers.Tests/Unit/Volumes/TestcontainersVolumeBuilderTest.cs index 0b19a8101..ffccc525f 100644 --- a/tests/Testcontainers.Tests/Unit/Volumes/TestcontainersVolumeBuilderTest.cs +++ b/tests/Testcontainers.Tests/Unit/Volumes/TestcontainersVolumeBuilderTest.cs @@ -50,7 +50,7 @@ public async Task CreateVolumeAssignsLabels() var client = new TestcontainersClient(ResourceReaper.DefaultSessionId, TestcontainersSettings.OS.DockerEndpointAuthConfig, NullLogger.Instance); // When - var volumeResponse = await client.Volume.ByNameAsync(_volume.Name) + var volumeResponse = await client.Volume.ByIdAsync(_volume.Name) .ConfigureAwait(false); // Then