Skip to content

Commit 75d564d

Browse files
committed
chore: Update remaining parts
1 parent dd5d7c0 commit 75d564d

File tree

9 files changed

+88
-139
lines changed

9 files changed

+88
-139
lines changed

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
<PackageVersion Include="Confluent.SchemaRegistry" Version="2.8.0"/>
4242
<PackageVersion Include="Consul" Version="1.6.10.9"/>
4343
<PackageVersion Include="CouchbaseNetClient" Version="3.6.4"/>
44-
<PackageVersion Include="Net.IBM.Data.Db2-lnx" Version="8.0.0.200"/>
45-
<PackageVersion Include="Net.IBM.Data.Db2" Version="8.0.0.200"/>
44+
<PackageVersion Include="Net.IBM.Data.Db2-lnx" Version="9.0.0.100"/>
45+
<PackageVersion Include="Net.IBM.Data.Db2" Version="9.0.0.100"/>
4646
<PackageVersion Include="DotPulsar" Version="3.3.2"/>
4747
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.16.3"/>
4848
<PackageVersion Include="EventStore.Client.Grpc.Streams" Version="22.0.0"/>

Testcontainers.sln

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ EndProject
218218
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Xunit.Tests", "tests\Testcontainers.Xunit.Tests\Testcontainers.Xunit.Tests.csproj", "{E901DF14-6F05-4FC2-825A-3055FAD33561}"
219219
EndProject
220220
Global
221+
GlobalSection(SolutionProperties) = preSolution
222+
HideSolutionNode = FALSE
223+
EndGlobalSection
221224
GlobalSection(SolutionConfigurationPlatforms) = preSolution
222225
Debug|Any CPU = Debug|Any CPU
223226
Release|Any CPU = Release|Any CPU

docs/modules/db2.md

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
11
# IBM DB2
22

3-
[IBM DB2](https://www.ibm.com/db2), is a relational database engine developed by IBM. The following example provides .NET developers with a starting point to use a IBM DB2 instance in the [xUnit][xunit] tests.
3+
[IBM DB2](https://www.ibm.com/db2) is a relational database engine developed by IBM.
44

5-
The following example (for windows) uses the following NuGet packages:
5+
Add the following dependency to your project file:
66

7-
```console title="Install the NuGet dependencies"
7+
```shell title="NuGet"
88
dotnet add package Testcontainers.Db2
9-
dotnet add package Net.IBM.Data.Db2
10-
dotnet add package xunit
119
```
1210

13-
Please note: For linux there are currently some hurdles and the package Net.IBM.Data.Db2-lnx has to be used with the following environment variables being set:
11+
!!! warning
1412

15-
- LD_LIBRARY_PATH
16-
- PATH
17-
- DB2_CLI_DRIVER_INSTALL_PATH
13+
The Linux client dependency, [Net.IBM.Data.Db2-lnx](https://www.nuget.org/packages/Net.IBM.Data.Db2-lnx), requires additional configurations. We use the [Testcontainers.Db2.Tests.targets](tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.targets) file to configure the environment variables: `LD_LIBRARY_PATH`, `PATH`, `DB2_CLI_DRIVER_INSTALL_PATH`, at runtime.
1814

19-
One way to achieve this within a test project is to extend the .csproj with a task that writes a .runsettings file. An example is given below:
20-
21-
=== "Example"
22-
```xml
23-
--8<-- "tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj:RunSettingsGeneration"
24-
```
25-
26-
IDEs and editors may also require the following packages to run tests: `xunit.runner.visualstudio` and `Microsoft.NET.Test.Sdk`.
27-
28-
Copy and paste the following code into a new `.cs` test file within an existing test project.
15+
You can start an DB2 container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method.
2916

3017
=== "Usage Example"
3118
```csharp
3219
--8<-- "tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs:UseDb2Container"
3320
```
3421

35-
To execute the tests, use the command `dotnet test` from a terminal.
22+
The test example uses the following NuGet dependencies:
3623

37-
## A Note To Developers
24+
=== "Package References"
25+
```xml
26+
--8<-- "tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj:PackageReferences"
27+
```
3828

39-
Once Testcontainers creates a server instance, developers may use the connection string with any of the popular data-access technologies found in the .NET Ecosystem. Some of these libraries include [Entity Framework Core](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore), [Dapper](https://www.nuget.org/packages/Dapper), and [NHibernate](https://www.nuget.org/packages/NHibernate). At which point, developers can execute database migrations and SQL scripts.
29+
To execute the tests, use the command `dotnet test` from a terminal.
4030

41-
[xunit]: https://xunit.net/
31+
--8<-- "docs/modules/_call_out_test_projects.txt"

src/Testcontainers.Db2/Db2Builder.cs

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ public sealed class Db2Builder : ContainerBuilder<Db2Builder, Db2Container, Db2C
1414

1515
public const string DefaultPassword = "db2inst1";
1616

17-
public const string DefaultLicenseAgreement = "accept";
17+
private const string AcceptLicenseAgreementEnvVar = "LICENSE";
1818

19-
public const string DefaultInMemoryDatabasePath = "/home/db2inst1/data";
19+
private const string AcceptLicenseAgreement = "accept";
20+
21+
private const string DeclineLicenseAgreement = "decline";
2022

2123
/// <summary>
2224
/// Initializes a new instance of the <see cref="Db2Builder" /> class.
@@ -40,6 +42,20 @@ private Db2Builder(Db2Configuration resourceConfiguration)
4042
/// <inheritdoc />
4143
protected override Db2Configuration DockerResourceConfiguration { get; }
4244

45+
/// <summary>
46+
/// Accepts the license agreement.
47+
/// </summary>
48+
/// <remarks>
49+
/// When <paramref name="acceptLicenseAgreement" /> is set to <c>true</c>, the Db2 <see href="www.ibm.com/terms/?id=L-SNMD-UVTL8R">license</see> is accepted.
50+
/// </remarks>
51+
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the Db2 license agreement is accepted.</param>
52+
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
53+
public Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
54+
{
55+
var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement;
56+
return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement);
57+
}
58+
4359
/// <summary>
4460
/// Sets the Db2 database name.
4561
/// </summary>
@@ -59,7 +75,8 @@ public Db2Builder WithDatabase(string database)
5975
public Db2Builder WithUsername(string username)
6076
{
6177
return Merge(DockerResourceConfiguration, new Db2Configuration(username: username))
62-
.WithEnvironment("DB2INSTANCE", username);
78+
.WithEnvironment("DB2INSTANCE", username)
79+
.WithTmpfsMount(string.Join("/", string.Empty, "home", username, "data"));
6380
}
6481

6582
/// <summary>
@@ -73,53 +90,36 @@ public Db2Builder WithPassword(string password)
7390
.WithEnvironment("DB2INST1_PASSWORD", password);
7491
}
7592

76-
/// <summary>
77-
/// Accepts the Db2 license agreement.
78-
/// </summary>
79-
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
80-
public Db2Builder WithLicenseAgreement()
81-
{
82-
return Merge(DockerResourceConfiguration, new Db2Configuration(licenseAgreement: DefaultLicenseAgreement))
83-
.WithEnvironment("LICENSE", DefaultLicenseAgreement);
84-
}
85-
86-
/// <summary>
87-
/// Maps the database to memory.
88-
/// </summary>
89-
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
90-
public Db2Builder WithInMemoryDatabase()
91-
{
92-
return Merge(DockerResourceConfiguration, new Db2Configuration(inMemoryDatabasePath: DefaultInMemoryDatabasePath))
93-
.WithTmpfsMount(DefaultInMemoryDatabasePath);
94-
}
95-
9693
/// <inheritdoc />
9794
public override Db2Container Build()
9895
{
9996
Validate();
10097
return new Db2Container(DockerResourceConfiguration);
10198
}
10299

103-
/// <inheritdoc />
100+
/// <inheritdoc />
104101
protected override Db2Builder Init() => base.Init()
105102
.WithImage(Db2Image)
106103
.WithPortBinding(Db2Port, true)
107104
.WithDatabase(DefaultDatabase)
108105
.WithUsername(DefaultUsername)
109106
.WithPassword(DefaultPassword)
110-
.WithLicenseAgreement()
111-
.WithInMemoryDatabase()
112107
.WithPrivileged(true)
113-
.WithWaitStrategy(Wait.ForUnixContainer()
114-
.UntilMessageIsLogged("All databases are now active")
115-
.UntilMessageIsLogged("Setup has completed.")
116-
.AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration)));
108+
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Setup has completed."));
117109

118110
/// <inheritdoc />
119111
protected override void Validate()
120112
{
113+
const string message = "The image '{0}' requires you to accept a license agreement.";
114+
121115
base.Validate();
122116

117+
Predicate<Db2Configuration> licenseAgreementNotAccepted = value =>
118+
!value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal);
119+
120+
_ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image))
121+
.ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name));
122+
123123
_ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username))
124124
.NotNull()
125125
.NotEmpty();
@@ -146,26 +146,4 @@ protected override Db2Builder Merge(Db2Configuration oldValue, Db2Configuration
146146
{
147147
return new Db2Builder(new Db2Configuration(oldValue, newValue));
148148
}
149-
150-
/// <inheritdoc cref="IWaitUntil" />
151-
private sealed class WaitUntil : IWaitUntil
152-
{
153-
/// <summary>
154-
/// Initializes a new instance of the <see cref="WaitUntil" /> class.
155-
/// </summary>
156-
/// <param name="configuration">The container configuration.</param>
157-
public WaitUntil(Db2Configuration configuration)
158-
{
159-
}
160-
161-
/// <inheritdoc />
162-
public async Task<bool> UntilAsync(IContainer container)
163-
{
164-
var db2Container = (Db2Container)container;
165-
166-
var execResult = await db2Container.ExecScriptAsync("SELECT 1 FROM SYSIBM.SYSDUMMY1").ConfigureAwait(false);
167-
168-
return 0L.Equals(execResult.ExitCode);
169-
}
170-
}
171149
}

src/Testcontainers.Db2/Db2Configuration.cs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,20 @@ namespace Testcontainers.Db2;
44
[PublicAPI]
55
public sealed class Db2Configuration : ContainerConfiguration
66
{
7-
private readonly bool _archiveLogs;
8-
private readonly bool _autoConfig;
9-
private readonly string _licenseAgreement;
10-
private readonly string _inMemoryDatabasePath;
11-
127
/// <summary>
138
/// Initializes a new instance of the <see cref="Db2Configuration" /> class.
149
/// </summary>
1510
/// <param name="database">The Db2 database.</param>
1611
/// <param name="username">The Db2 username.</param>
1712
/// <param name="password">The Db2 password.</param>
18-
/// <param name="archiveLogs">The Db2 archive logs setting.</param>
19-
/// <param name="autoConfig">The Db2 auto config setting.</param>
20-
/// <param name="licenseAgreement">The Db2 license agreement.</param>
21-
/// <param name="inMemoryDatabasePath">The Db2 database path to map into memory (tmpfs).</param>
2213
public Db2Configuration(
2314
string database = null,
2415
string username = null,
25-
string password = null,
26-
bool archiveLogs = false,
27-
bool autoConfig = false,
28-
string licenseAgreement = null,
29-
string inMemoryDatabasePath = null)
16+
string password = null)
3017
{
3118
Database = database;
3219
Username = username;
3320
Password = password;
34-
_archiveLogs = archiveLogs;
35-
_autoConfig = autoConfig;
36-
_licenseAgreement = licenseAgreement;
37-
_inMemoryDatabasePath = inMemoryDatabasePath;
3821
}
3922

4023
/// <summary>
@@ -78,10 +61,6 @@ public Db2Configuration(Db2Configuration oldValue, Db2Configuration newValue)
7861
Database = BuildConfiguration.Combine(oldValue.Database, newValue.Database);
7962
Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username);
8063
Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
81-
_archiveLogs = BuildConfiguration.Combine(oldValue._archiveLogs, newValue._archiveLogs);
82-
_autoConfig = BuildConfiguration.Combine(oldValue._autoConfig, newValue._autoConfig);
83-
_licenseAgreement = BuildConfiguration.Combine(oldValue._licenseAgreement, newValue._licenseAgreement);
84-
_inMemoryDatabasePath = BuildConfiguration.Combine(oldValue._inMemoryDatabasePath, newValue._inMemoryDatabasePath);
8564
}
8665

8766
/// <summary>

src/Testcontainers.Db2/Db2Container.cs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,45 @@ namespace Testcontainers.Db2;
44
[PublicAPI]
55
public sealed class Db2Container : DockerContainer, IDatabaseContainer
66
{
7-
private static string Db2CommandPath = "/opt/ibm/db2/*/bin/db2";
8-
97
private readonly Db2Configuration _configuration;
108

11-
private const char ConnectionStringDelimiter = ';';
12-
139
public Db2Container(Db2Configuration configuration) : base(configuration)
1410
{
1511
_configuration = configuration;
1612
}
1713

18-
public string GetConnectionString() => new StringBuilder()
19-
.Append("Server=").Append(Hostname).Append(':').Append(GetMappedPublicPort(Db2Builder.Db2Port).ToString())
20-
.Append(ConnectionStringDelimiter)
21-
.Append("Database=").Append(_configuration.Database).Append(ConnectionStringDelimiter)
22-
.Append("UID=").Append(_configuration.Username).Append(ConnectionStringDelimiter)
23-
.Append("PWD=").Append(_configuration.Password).Append(ConnectionStringDelimiter)
24-
.ToString();
14+
/// <summary>
15+
/// Gets the Db2 connection string.
16+
/// </summary>
17+
/// <returns>The Db2 connection string.</returns>
18+
public string GetConnectionString()
19+
{
20+
var properties = new Dictionary<string, string>();
21+
properties.Add("Server", Hostname + ":" + GetMappedPublicPort(Db2Builder.Db2Port));
22+
properties.Add("Database", _configuration.Database);
23+
properties.Add("UID", _configuration.Username);
24+
properties.Add("PWD", _configuration.Password);
25+
return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
26+
}
2527

26-
public async Task<ExecResult> ExecScriptAsync(string scriptContent, CancellationToken cancellationToken = default)
28+
/// <summary>
29+
/// Executes the SQL script in the Db2 container.
30+
/// </summary>
31+
/// <param name="scriptContent">The content of the SQL script to execute.</param>
32+
/// <param name="ct">Cancellation token.</param>
33+
/// <returns>Task that completes when the SQL script has been executed.</returns>
34+
public async Task<ExecResult> ExecScriptAsync(string scriptContent, CancellationToken ct = default)
2735
{
28-
string[] command =
29-
[
30-
"su", "db2inst1", "-c", new StringBuilder()
31-
.Append(Db2CommandPath).Append(" connect to ").Append(_configuration.Database)
32-
.Append(" user ").Append(_configuration.Username).Append(" using ").Append(_configuration.Password)
33-
.Append("; ")
34-
.Append(Db2CommandPath).Append(' ').Append(scriptContent)
35-
.ToString()
36-
];
37-
38-
return await ExecAsync(command).ConfigureAwait(false);
36+
const string db2ShellCommandFormat = "su - {1} -c \"db2 connect to {0} && db2 -tvf '{2}'\"";
37+
38+
var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName());
39+
40+
var db2ShellCommand = string.Format(db2ShellCommandFormat, _configuration.Database, _configuration.Username, scriptFilePath);
41+
42+
await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct)
43+
.ConfigureAwait(false);
44+
45+
return await ExecAsync(new [] { "/bin/sh", "-c", db2ShellCommand}, ct)
46+
.ConfigureAwait(false);
3947
}
4048
}

src/Testcontainers.Db2/Usings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
global using System;
2+
global using System.Collections.Generic;
3+
global using System.IO;
24
global using System.Linq;
35
global using System.Text;
46
global using System.Threading;

tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,7 @@ namespace Testcontainers.Db2;
33
public sealed class Db2ContainerTest : IAsyncLifetime
44
{
55
// # --8<-- [start:UseDb2Container]
6-
private readonly Db2Container _db2Container = new Db2Builder()
7-
.WithEnvironment("TO_CREATE_SAMPLEDB", "false")
8-
.WithEnvironment("PERSISTENT_HOME", "true")
9-
.WithEnvironment("REPODB", "false")
10-
.WithEnvironment("BLU", "false")
11-
.WithEnvironment("HADR_ENABLED", "false")
12-
.WithEnvironment("ARCHIVE_LOGS", "false")
13-
.WithEnvironment("AUTOCONFIG", "false")
14-
.Build();
6+
private readonly Db2Container _db2Container = new Db2Builder().WithAcceptLicenseAgreement(true).Build();
157

168
public Task InitializeAsync()
179
{
@@ -25,20 +17,14 @@ public Task DisposeAsync()
2517

2618
[Fact]
2719
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
28-
public async Task ReadFromDb2Database()
20+
public void ConnectionStateReturnsOpen()
2921
{
3022
// Given
3123
using DbConnection connection = new DB2Connection(_db2Container.GetConnectionString());
3224

3325
// When
3426
connection.Open();
3527

36-
using DbCommand command = connection.CreateCommand();
37-
command.CommandText = "SELECT 1 FROM SYSIBM.SYSDUMMY1;";
38-
39-
var actual = await command.ExecuteScalarAsync() as int?;
40-
Assert.Equal(1, actual.GetValueOrDefault());
41-
4228
// Then
4329
Assert.Equal(ConnectionState.Open, connection.State);
4430
}
@@ -51,7 +37,8 @@ public async Task ExecScriptReturnsSuccessful()
5137
const string scriptContent = "SELECT 1 FROM SYSIBM.SYSDUMMY1;";
5238

5339
// When
54-
var execResult = await _db2Container.ExecScriptAsync(scriptContent);
40+
var execResult = await _db2Container.ExecScriptAsync(scriptContent)
41+
.ConfigureAwait(true);
5542

5643
// Then
5744
Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr);

0 commit comments

Comments
 (0)