using System.Text.Json; using Microsoft.Extensions.Logging; using StellaOps.Scanner.Sources.Contracts; using StellaOps.Scanner.Sources.Domain; namespace StellaOps.Scanner.Sources.Services; /// /// Dispatches connection tests to type-specific testers. /// public sealed class SourceConnectionTester : ISourceConnectionTester { private readonly IEnumerable _testers; private readonly ILogger _logger; private readonly TimeProvider _timeProvider; public SourceConnectionTester( IEnumerable testers, ILogger logger, TimeProvider? timeProvider = null) { _testers = testers; _logger = logger; _timeProvider = timeProvider ?? TimeProvider.System; } public Task TestAsync(SbomSource source, CancellationToken ct = default) { return TestAsync(source, null, ct); } public async Task TestAsync( SbomSource source, JsonDocument? testCredentials, CancellationToken ct = default) { var tester = _testers.FirstOrDefault(t => t.SourceType == source.SourceType); if (tester == null) { _logger.LogWarning( "No connection tester registered for source type {SourceType}", source.SourceType); return new ConnectionTestResult { Success = false, Message = $"No connection tester available for source type {source.SourceType}", TestedAt = _timeProvider.GetUtcNow() }; } try { _logger.LogDebug( "Testing connection for source {SourceId} ({SourceType})", source.SourceId, source.SourceType); var result = await tester.TestAsync(source, testCredentials, ct); _logger.LogInformation( "Connection test for source {SourceId} {Result}: {Message}", source.SourceId, result.Success ? "succeeded" : "failed", result.Message); return result; } catch (OperationCanceledException) { throw; } catch (Exception ex) { _logger.LogError(ex, "Connection test failed for source {SourceId}", source.SourceId); return new ConnectionTestResult { Success = false, Message = $"Connection test error: {ex.Message}", TestedAt = _timeProvider.GetUtcNow(), Details = new Dictionary { ["exceptionType"] = ex.GetType().Name } }; } } }