-
Notifications
You must be signed in to change notification settings - Fork 3
Implemented RemotePathType for credential separation. #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -38,7 +38,7 @@ | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| _options = new Options | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| UseGivenUserCredentialsForRemoteConnections = true, | ||||||||||||||||||||||
| //UseGivenUserCredentialsForRemoteConnections = true, | ||||||||||||||||||||||
| UserName = $"{_domain}\\{_name}", | ||||||||||||||||||||||
| Password = _pwd | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
Comment on lines
+41
to
44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test no longer exercises impersonation; set RemotePath to trigger credential validation. With the old flag commented out, _options = new Options
{
- //UseGivenUserCredentialsForRemoteConnections = true,
+ RemotePath = RemotePathType.Source,
UserName = $"{_domain}\\{_name}",
Password = _pwd
};If the test intends to validate writing under impersonation, use 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
@@ -80,12 +80,12 @@ | |||||||||||||||||||||
| { | ||||||||||||||||||||||
| var options = new Options | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| UseGivenUserCredentialsForRemoteConnections = true, | ||||||||||||||||||||||
| //UseGivenUserCredentialsForRemoteConnections = true, | ||||||||||||||||||||||
| UserName = "test", | ||||||||||||||||||||||
| Password = _pwd | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
Comment on lines
+83
to
86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Domain-less username test should also set RemotePath to ensure the expected ArgumentException. Same issue as above. Without var options = new Options
{
- //UseGivenUserCredentialsForRemoteConnections = true,
+ RemotePath = RemotePathType.Source,
UserName = "test",
Password = _pwd
};Also applies to: 88-90 🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| var ex = Assert.ThrowsAsync<ArgumentException>(() => Files.Move(_input, options, default)); | ||||||||||||||||||||||
| ClassicAssert.AreEqual($@"UserName field must be of format domain\username was: {options.UserName}", ex.Message); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,292 @@ | ||||||||||||||||||||||||||||||||
| using Frends.Files.Move.Definitions; | ||||||||||||||||||||||||||||||||
| using NUnit.Framework; | ||||||||||||||||||||||||||||||||
| using NUnit.Framework.Legacy; | ||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||
| using System.Diagnostics; | ||||||||||||||||||||||||||||||||
| using System.IO; | ||||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||||
| using System.Runtime.InteropServices; | ||||||||||||||||||||||||||||||||
| using System.Threading; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| namespace Frends.Files.Move.Tests.Samba; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [TestFixture] | ||||||||||||||||||||||||||||||||
| public class ImpersonationSambaDockerTests | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| private string _localSourceDir; | ||||||||||||||||||||||||||||||||
| private string _localTargetDir; | ||||||||||||||||||||||||||||||||
| private string _remoteDir; | ||||||||||||||||||||||||||||||||
| private Options _options; | ||||||||||||||||||||||||||||||||
| private bool _isDockerEnvironment; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [OneTimeSetUp] | ||||||||||||||||||||||||||||||||
| public void OneTimeSetup() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var tempRoot = Path.Combine(Path.GetTempPath(), "ImpersonationDockerTests_" + Guid.NewGuid().ToString("N")[..8]); | ||||||||||||||||||||||||||||||||
| _localSourceDir = Path.Combine(tempRoot, "Source"); | ||||||||||||||||||||||||||||||||
| _localTargetDir = Path.Combine(tempRoot, "Target"); | ||||||||||||||||||||||||||||||||
| Directory.CreateDirectory(_localSourceDir); | ||||||||||||||||||||||||||||||||
| Directory.CreateDirectory(_localTargetDir); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| WaitForDockerContainer("impersonation_test_samba", TimeSpan.FromMinutes(2)); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var connectionMethods = new[] | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| @"\\localhost:1445\testshare", // Docker port mapping | ||||||||||||||||||||||||||||||||
| @"\\127.0.0.1:1445\testshare", // Localhost IP with port | ||||||||||||||||||||||||||||||||
| GetContainerDirectPath(), // Container IP fallback | ||||||||||||||||||||||||||||||||
| @"\\host.docker.internal:1445\testshare" // Docker Desktop host | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| _isDockerEnvironment = false; | ||||||||||||||||||||||||||||||||
| string lastError = ""; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| foreach (var path in connectionMethods) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (string.IsNullOrEmpty(path)) continue; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (TestSambaConnection(path, "testuser", "password123!", TimeSpan.FromSeconds(10))) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| _remoteDir = path; | ||||||||||||||||||||||||||||||||
| _isDockerEnvironment = true; | ||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| catch (Exception ex) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| lastError = ex.Message; | ||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!_isDockerEnvironment) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Assert.Ignore("Docker Samba environment not available"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| _options = new Options | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| UserName = @"WORKGROUP\testuser", | ||||||||||||||||||||||||||||||||
| Password = "password123!", | ||||||||||||||||||||||||||||||||
| CreateTargetDirectories = true, | ||||||||||||||||||||||||||||||||
| IfTargetFileExists = FileExistsAction.Overwrite | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private void WaitForDockerContainer(string containerName, TimeSpan timeout) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var stopwatch = Stopwatch.StartNew(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| while (stopwatch.Elapsed < timeout) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var psi = new ProcessStartInfo("docker", $"exec {containerName} smbclient -L //localhost/ -U testuser%password123! --option=\"client min protocol=SMB2\"") | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| RedirectStandardOutput = true, | ||||||||||||||||||||||||||||||||
| RedirectStandardError = true, | ||||||||||||||||||||||||||||||||
| UseShellExecute = false, | ||||||||||||||||||||||||||||||||
| CreateNoWindow = true | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
Comment on lines
+86
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security concern: Password visible in process arguments. The password is passed directly in command-line arguments, which could be visible in process listings. While this is test code, consider using environment variables or stdin for passing credentials to avoid exposing them in process arguments: - var psi = new ProcessStartInfo("docker", $"exec {containerName} smbclient -L //localhost/ -U testuser%password123! --option=\"client min protocol=SMB2\"")
+ // Consider using environment variables or stdin to pass credentials
+ var psi = new ProcessStartInfo("docker", $"exec {containerName} sh -c 'echo password123! | smbclient -L //localhost/ -U testuser --option=\"client min protocol=SMB2\"'")📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| using var process = Process.Start(psi); | ||||||||||||||||||||||||||||||||
| if (process.WaitForExit(5000) && process.ExitCode == 0) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| catch { } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Console.WriteLine($"⏳ Waiting for container {containerName} to be ready... ({stopwatch.Elapsed.TotalSeconds:F0}s)"); | ||||||||||||||||||||||||||||||||
| Thread.Sleep(2000); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private string GetContainerDirectPath() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var psi = new ProcessStartInfo("docker", "inspect impersonation_test_samba -f \"{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\"") | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| RedirectStandardOutput = true, | ||||||||||||||||||||||||||||||||
| RedirectStandardError = true, | ||||||||||||||||||||||||||||||||
| UseShellExecute = false, | ||||||||||||||||||||||||||||||||
| CreateNoWindow = true | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| using var process = Process.Start(psi); | ||||||||||||||||||||||||||||||||
| if (process.WaitForExit(5000) && process.ExitCode == 0) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var ip = process.StandardOutput.ReadToEnd().Trim(); | ||||||||||||||||||||||||||||||||
| if (!string.IsNullOrEmpty(ip) && ip != "localhost") | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return $@"\\{ip}\testshare"; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| catch (Exception ex) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Console.WriteLine($"Failed to get container IP: {ex.Message}"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private bool TestSambaConnection(string uncPath, string username, string password, TimeSpan timeout) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var host = ExtractHostFromUncPath(uncPath); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var psi = new ProcessStartInfo("docker", $"exec impersonation_test_samba smbclient //{host}/testshare -U {username}%{password} -c dir") | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| RedirectStandardOutput = true, | ||||||||||||||||||||||||||||||||
| RedirectStandardError = true, | ||||||||||||||||||||||||||||||||
| UseShellExecute = false, | ||||||||||||||||||||||||||||||||
| CreateNoWindow = true | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| using var process = Process.Start(psi); | ||||||||||||||||||||||||||||||||
| return process.WaitForExit((int)timeout.TotalMilliseconds) && process.ExitCode == 0; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private string ExtractHostFromUncPath(string uncPath) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| // Extract host | ||||||||||||||||||||||||||||||||
| var parts = uncPath.TrimStart('\\').Split('\\'); | ||||||||||||||||||||||||||||||||
| return parts.Length > 0 ? parts[0] : "localhost"; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [OneTimeTearDown] | ||||||||||||||||||||||||||||||||
| public void OneTimeTearDown() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var tempRoot = Path.GetDirectoryName(_localSourceDir); | ||||||||||||||||||||||||||||||||
| if (Directory.Exists(tempRoot)) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Directory.Delete(tempRoot, true); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [SetUp] | ||||||||||||||||||||||||||||||||
| public void Setup() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Helper.CreateTestFiles(_localSourceDir); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [TearDown] | ||||||||||||||||||||||||||||||||
| public void TearDown() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (Directory.Exists(_localTargetDir)) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Helper.DeleteTestFolder(_localTargetDir); | ||||||||||||||||||||||||||||||||
| Directory.CreateDirectory(_localTargetDir); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (Directory.Exists(_localSourceDir)) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Helper.DeleteTestFolder(_localSourceDir); | ||||||||||||||||||||||||||||||||
| Directory.CreateDirectory(_localSourceDir); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| CleanRemoteDirectory(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| catch (Exception ex) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Console.WriteLine($"Warning: Failed to clean remote directory: {ex.Message}"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private void CleanRemoteDirectory() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var psi = new ProcessStartInfo("docker", $"exec impersonation_test_samba find /mount/testshare -type f -delete") | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| RedirectStandardOutput = true, | ||||||||||||||||||||||||||||||||
| RedirectStandardError = true, | ||||||||||||||||||||||||||||||||
| UseShellExecute = false, | ||||||||||||||||||||||||||||||||
| CreateNoWindow = true | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| using var process = Process.Start(psi); | ||||||||||||||||||||||||||||||||
| process.WaitForExit(10000); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||
| public async Task RemoteToLocal_WithImpersonation_Success() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| CreateRemoteTestFiles(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var input = new Input | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Directory = _remoteDir, | ||||||||||||||||||||||||||||||||
| Pattern = "*.txt", | ||||||||||||||||||||||||||||||||
| TargetDirectory = _localTargetDir | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| _options.RemotePath = RemotePathType.Source; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var result = await Files.Move(input, _options, default); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ClassicAssert.AreEqual(5, result.Files.Count); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| foreach (var file in result.Files) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| ClassicAssert.IsTrue(File.Exists(file.TargetPath)); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var content = await File.ReadAllTextAsync(file.TargetPath); | ||||||||||||||||||||||||||||||||
| ClassicAssert.IsNotEmpty(content); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ClassicAssert.IsFalse(File.Exists(file.SourcePath)); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||
| public async Task LocalToRemote_WithImpersonation_Success() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var input = new Input | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Directory = _localSourceDir, | ||||||||||||||||||||||||||||||||
| Pattern = "*.txt", | ||||||||||||||||||||||||||||||||
| TargetDirectory = _remoteDir | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| _options.RemotePath = RemotePathType.Target; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var result = await Files.Move(input, _options, default); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ClassicAssert.AreEqual(5, result.Files.Count); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| foreach (var file in result.Files) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| ClassicAssert.IsTrue(File.Exists(file.TargetPath)); | ||||||||||||||||||||||||||||||||
| ClassicAssert.IsFalse(File.Exists(file.SourcePath)); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private void CreateRemoteTestFiles() | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| // Use docker exec to create test files in the container | ||||||||||||||||||||||||||||||||
| var commands = new[] | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| "echo 'Test1 content' > /mount/testshare/Test1.txt", | ||||||||||||||||||||||||||||||||
| "echo 'Test2 content' > /mount/testshare/Test2.txt", | ||||||||||||||||||||||||||||||||
| "echo 'Test3 content' > /mount/testshare/Test3.txt", | ||||||||||||||||||||||||||||||||
| "echo 'Test4 content' > /mount/testshare/Test4.txt", | ||||||||||||||||||||||||||||||||
| "echo 'Test5 content' > /mount/testshare/Test5.txt" | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| foreach (var command in commands) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var psi = new ProcessStartInfo("docker", $"exec impersonation_test_samba sh -c \"{command}\"") | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| RedirectStandardOutput = true, | ||||||||||||||||||||||||||||||||
| RedirectStandardError = true, | ||||||||||||||||||||||||||||||||
| UseShellExecute = false, | ||||||||||||||||||||||||||||||||
| CreateNoWindow = true | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| using var process = Process.Start(psi); | ||||||||||||||||||||||||||||||||
| process.WaitForExit(5000); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # docker-compose.yml | ||
| version: '3.8' | ||
| services: | ||
| samba: | ||
| image: dperson/samba:latest | ||
| container_name: impersonation_test_samba | ||
| hostname: samba-test | ||
| ports: | ||
| - "1445:445" # Map to port 1445 to avoid conflicts | ||
| volumes: | ||
| - samba_data:/mount/testshare # Use named volume instead of tmpfs | ||
| environment: | ||
| - USER=testuser;password123! | ||
| - SHARE=testshare;/mount/testshare;yes;no;no;testuser;testuser;testuser | ||
| - SMB=true | ||
| - NMBD=true | ||
| - WORKGROUP=WORKGROUP | ||
| command: > | ||
| sh -c " | ||
| /usr/bin/samba.sh -u 'testuser;password123!' -s 'testshare;/mount/testshare;yes;no;no;testuser' -p && | ||
| chmod 777 /mount/testshare && | ||
| chown testuser:testuser /mount/testshare && | ||
| tail -f /dev/null | ||
| " | ||
| restart: unless-stopped | ||
| networks: | ||
| - test-net | ||
| healthcheck: | ||
| test: ["CMD", "smbclient", "-L", "//localhost/", "-U", "testuser%password123!", "--option=client min protocol=SMB2"] | ||
| interval: 15s | ||
| timeout: 10s | ||
| retries: 5 | ||
| start_period: 30s | ||
|
|
||
| volumes: | ||
| samba_data: | ||
| driver: local | ||
|
|
||
| networks: | ||
| test-net: | ||
| driver: bridge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Validate Docker.DotNet version and gate Docker-driven tests when the daemon isn’t available.
Pin looks fine for net6.0, but please double-check the version against your CI images. Add a test category/assumption to skip Docker tests when the engine isn’t reachable to keep pipelines green on Windows/macOS agents without Docker.
Use this quick check to find tests already gated by category and confirm at least one suite depends on Docker:
If missing, wrap Docker-dependent fixtures/tests with
[Category("Docker")]and add an environment/assumption check (e.g., try connecting via DockerClient andAssert.Ignoreon failure).🏁 Script executed:
Length of output: 647
Validate Docker.DotNet version and gate Docker-driven tests when the daemon isn’t available
It looks like you’ve correctly pinned Docker.DotNet to version 3.125.15 in your test project, but we didn’t find any existing test gating or assumptions around Docker availability. To prevent CI failures on agents without Docker (e.g., Windows/macOS hosted runners), please:
Verify the Docker.DotNet version
Ensure v3.125.15 is compatible with the Docker engine version installed in your CI environments. If your images use an older or newer Docker API, you may need to bump or roll back this package reference in
Frends.Files.Move/Frends.Files.Move.Tests/Frends.Files.Move.Tests.csproj(Line 11).Annotate Docker-dependent tests
Wrap any tests or fixtures that rely on Docker with a Docker-specific category. For NUnit, add:
Or for MSTest:
Skip tests when Docker isn’t reachable
At the start of each Docker-dependent test or fixture setup, attempt a lightweight ping to the daemon and call the appropriate skip/ignore API on failure. For example (NUnit):
This will keep your pipelines green on agents that can’t run Docker.
Once these changes are in place, your suite will correctly isolate and skip Docker-driven tests when the engine isn’t available.
🤖 Prompt for AI Agents