diff --git a/src/Testcontainers.MsSql/MsSqlBuilder.cs b/src/Testcontainers.MsSql/MsSqlBuilder.cs
index 402986a81..819c8ffdd 100644
--- a/src/Testcontainers.MsSql/MsSqlBuilder.cs
+++ b/src/Testcontainers.MsSql/MsSqlBuilder.cs
@@ -126,17 +126,24 @@ private MsSqlBuilder WithUsername(string username)
///
///
- /// Uses the sqlcmd utility scripting variables to detect readiness of the MsSql container:
+ /// Uses the sqlcmd utility scripting variables to detect readiness of the MsSql container:
/// https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility?view=sql-server-linux-ver15#sqlcmd-scripting-variables.
///
private sealed class WaitUntil : IWaitUntil
{
- private readonly string[] _command = { "/opt/mssql-tools/bin/sqlcmd", "-Q", "SELECT 1;" };
-
///
- public async Task UntilAsync(IContainer container)
+ public Task UntilAsync(IContainer container)
+ {
+ return UntilAsync(container as MsSqlContainer);
+ }
+
+ ///
+ private async Task UntilAsync(MsSqlContainer container)
{
- var execResult = await container.ExecAsync(_command)
+ var sqlCmdFilePath = await container.GetSqlCmdFilePathAsync()
+ .ConfigureAwait(false);
+
+ var execResult = await container.ExecAsync(new[] { sqlCmdFilePath, "-C", "-Q", "SELECT 1;" })
.ConfigureAwait(false);
return 0L.Equals(execResult.ExitCode);
diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs
index 626fec38d..6f3b13f1e 100644
--- a/src/Testcontainers.MsSql/MsSqlContainer.cs
+++ b/src/Testcontainers.MsSql/MsSqlContainer.cs
@@ -4,6 +4,10 @@ namespace Testcontainers.MsSql;
[PublicAPI]
public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer
{
+ private static readonly string[] FindSqlCmdFilePath = { "/bin/sh", "-c", "find /opt/mssql-tools*/bin/sqlcmd -type f -print -quit" };
+
+ private readonly Lazy> _lazySqlCmdFilePath;
+
private readonly MsSqlConfiguration _configuration;
///
@@ -13,6 +17,7 @@ public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer
public MsSqlContainer(MsSqlConfiguration configuration)
: base(configuration)
{
+ _lazySqlCmdFilePath = new Lazy>(FindSqlCmdFilePathAsync);
_configuration = configuration;
}
@@ -31,6 +36,19 @@ public string GetConnectionString()
return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
}
+ ///
+ /// Gets the sqlcmd utility file path.
+ ///
+ ///
+ /// The file path represents the path from the container, not from the Docker or test host.
+ ///
+ /// Cancellation token.
+ /// Task that completes when the sqlcmd utility file path has been found.
+ public Task GetSqlCmdFilePathAsync(CancellationToken ct = default)
+ {
+ return _lazySqlCmdFilePath.Value;
+ }
+
///
/// Executes the SQL script in the MsSql container.
///
@@ -41,10 +59,26 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation
{
var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName());
+ var sqlCmdFilePath = await GetSqlCmdFilePathAsync(ct)
+ .ConfigureAwait(false);
+
await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct)
.ConfigureAwait(false);
- return await ExecAsync(new[] { "/opt/mssql-tools/bin/sqlcmd", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct)
+ return await ExecAsync(new[] { sqlCmdFilePath, "-C", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct)
+ .ConfigureAwait(false);
+ }
+
+ private async Task FindSqlCmdFilePathAsync()
+ {
+ var findSqlCmdFilePathExecResult = await ExecAsync(FindSqlCmdFilePath)
.ConfigureAwait(false);
+
+ if (findSqlCmdFilePathExecResult.ExitCode == 0)
+ {
+ return findSqlCmdFilePathExecResult.Stdout.Trim();
+ }
+
+ throw new NotSupportedException("The sqlcmd binary could not be found.");
}
}
\ No newline at end of file
diff --git a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs
index f7d18102f..1f94c3b82 100644
--- a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs
+++ b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs
@@ -1,8 +1,13 @@
namespace Testcontainers.MsSql;
-public sealed class MsSqlContainerTest : IAsyncLifetime
+public abstract class MsSqlContainerTest : IAsyncLifetime
{
- private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build();
+ private readonly MsSqlContainer _msSqlContainer;
+
+ public MsSqlContainerTest(MsSqlContainer msSqlContainer)
+ {
+ _msSqlContainer = msSqlContainer;
+ }
public Task InitializeAsync()
{
@@ -43,4 +48,22 @@ public async Task ExecScriptReturnsSuccessful()
Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr);
Assert.Empty(execResult.Stderr);
}
+
+ [UsedImplicitly]
+ public sealed class MsSqlDefaultConfiguration : MsSqlContainerTest
+ {
+ public MsSqlDefaultConfiguration()
+ : base(new MsSqlBuilder().Build())
+ {
+ }
+ }
+
+ [UsedImplicitly]
+ public sealed class MsSqlTools18Configuration : MsSqlContainerTest
+ {
+ public MsSqlTools18Configuration()
+ : base(new MsSqlBuilder().WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04").Build())
+ {
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/Testcontainers.MsSql.Tests/Usings.cs b/tests/Testcontainers.MsSql.Tests/Usings.cs
index 02acb4398..493aff2cb 100644
--- a/tests/Testcontainers.MsSql.Tests/Usings.cs
+++ b/tests/Testcontainers.MsSql.Tests/Usings.cs
@@ -2,5 +2,6 @@
global using System.Data.Common;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
+global using JetBrains.Annotations;
global using Microsoft.Data.SqlClient;
global using Xunit;
\ No newline at end of file