test(domain): cover clamd timeout, caller cancellation, null content and defaults (refs #192)
CI / k8s (pull_request) Successful in 8s
CI / lint (pull_request) Successful in 1m47s
CI / build (pull_request) Successful in 1m38s
CI / docs (pull_request) Successful in 55s
CI / unit (pull_request) Successful in 1m13s
CI / frontend (pull_request) Successful in 2m24s
CI / mutation (pull_request) Successful in 4m42s
CI / verify-stack (pull_request) Skipped
CI / k8s (pull_request) Successful in 8s
CI / lint (pull_request) Successful in 1m47s
CI / build (pull_request) Successful in 1m38s
CI / docs (pull_request) Successful in 55s
CI / unit (pull_request) Successful in 1m13s
CI / frontend (pull_request) Successful in 2m24s
CI / mutation (pull_request) Successful in 4m42s
CI / verify-stack (pull_request) Skipped
Domain mutation score 89.87% → 91.98%, back above the 90% break. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,13 +17,14 @@ public class ClamdDocumentScannerTests
|
||||
public Task<byte[]> Received { get; }
|
||||
public int Port => ((IPEndPoint)_listener.LocalEndpoint).Port;
|
||||
|
||||
public FakeClamd(string reply)
|
||||
/// <param name="reply">The answer, or null to accept the request and never answer (a hung clamd).</param>
|
||||
public FakeClamd(string? reply)
|
||||
{
|
||||
_listener.Start();
|
||||
Received = Serve(reply);
|
||||
}
|
||||
|
||||
private async Task<byte[]> Serve(string reply)
|
||||
private async Task<byte[]> Serve(string? reply)
|
||||
{
|
||||
using var client = await _listener.AcceptTcpClientAsync();
|
||||
var stream = client.GetStream();
|
||||
@@ -35,7 +36,10 @@ public class ClamdDocumentScannerTests
|
||||
if (n == 0) break;
|
||||
received.Write(buffer, 0, n);
|
||||
}
|
||||
await stream.WriteAsync(System.Text.Encoding.ASCII.GetBytes(reply + "\0"));
|
||||
if (reply is null)
|
||||
await Task.Delay(TimeSpan.FromSeconds(10)); // long past any test timeout
|
||||
else
|
||||
await stream.WriteAsync(System.Text.Encoding.ASCII.GetBytes(reply + "\0"));
|
||||
return received.ToArray();
|
||||
}
|
||||
|
||||
@@ -82,4 +86,32 @@ public class ClamdDocumentScannerTests
|
||||
|
||||
Assert.Equal(ScanVerdict.Unavailable, await ScannerFor(port).ScanAsync([1, 2, 3]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_clamd_that_never_answers_is_unavailable_after_the_timeout()
|
||||
{
|
||||
using var clamd = new FakeClamd(reply: null);
|
||||
var scanner = new ClamdDocumentScanner(
|
||||
new ClamAvOptions { Host = "127.0.0.1", Port = clamd.Port, Timeout = TimeSpan.FromMilliseconds(300) });
|
||||
|
||||
Assert.Equal(ScanVerdict.Unavailable, await scanner.ScanAsync([1, 2, 3]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_cancelled_request_is_cancelled_not_reported_unavailable()
|
||||
{
|
||||
// The caller giving up is not a scanner outage: it propagates instead of becoming a 503.
|
||||
using var clamd = new FakeClamd(reply: null);
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(300));
|
||||
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => ScannerFor(clamd.Port).ScanAsync([1, 2, 3], cts.Token));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Rejects_null_content()
|
||||
=> await Assert.ThrowsAsync<ArgumentNullException>(() => ScannerFor(1).ScanAsync(null!));
|
||||
|
||||
[Fact]
|
||||
public void Defaults_to_the_clamav_service_on_the_clamd_port()
|
||||
=> Assert.Equal(("clamav", 3310), (new ClamAvOptions().Host, new ClamAvOptions().Port));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user