Commit 6ad85ff
committed
run dump again
kushal@syn-2600-6c56-9840-001d-0000-0000-0000-1157:~/src/dotnet/network-monitor/src$ cd ~/src/dotnet/network-monitor/src/; cat generate-network-monitor.sh; time bash generate-network-monitor.sh
#!/bin/bash
# =============================================================================
# Fix Build Errors Script for NetworkMonitor
# Fixes:
# 1. CS0104: Ambiguous NullLogger<> reference (2 instances)
# 2. CA1001: NetworkMonitorServiceTests owns disposable field but is not disposable
# =============================================================================
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() { echo -e "${YELLOW}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Determine the source directory
if [[ -d "src/NetworkMonitor.Core" ]]; then
SRC_DIR="src"
elif [[ -d "NetworkMonitor.Core" ]]; then
SRC_DIR="."
else
log_error "Cannot find NetworkMonitor source directory"
exit 1
fi
log_info "Working directory: $(pwd)"
log_info "Source directory: $SRC_DIR"
# =============================================================================
# Fix 1: Remove custom NullLogger (use Microsoft.Extensions.Logging.Abstractions)
# =============================================================================
log_info "Removing custom NullLogger (will use Microsoft's version)..."
NULLLOGGER_FILE="$SRC_DIR/NetworkMonitor.Tests/Fakes/NullLogger.cs"
if [[ -f "$NULLLOGGER_FILE" ]]; then
rm "$NULLLOGGER_FILE"
log_success "Removed custom NullLogger.cs"
else
log_info "NullLogger.cs already removed or not found"
fi
# =============================================================================
# Fix 2: Update NetworkConfigurationServiceTests to use fully qualified NullLogger
# =============================================================================
log_info "Updating NetworkConfigurationServiceTests..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Services/NetworkConfigurationServiceTests.cs" << 'EOF'
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NetworkMonitor.Core.Models;
using NetworkMonitor.Core.Services;
using NetworkMonitor.Tests.Fakes;
using Xunit;
namespace NetworkMonitor.Tests.Services;
/// <summary>
/// Tests for NetworkConfigurationService.
/// </summary>
public sealed class NetworkConfigurationServiceTests : IDisposable
{
private readonly FakeGatewayDetector _gatewayDetector;
private readonly FakeInternetTargetProvider _internetTargetProvider;
private readonly FakePingService _pingService;
private NetworkConfigurationService? _service;
public NetworkConfigurationServiceTests()
{
_gatewayDetector = new FakeGatewayDetector();
_internetTargetProvider = new FakeInternetTargetProvider();
_pingService = new FakePingService();
}
private NetworkConfigurationService CreateService(MonitorOptions? options = null)
{
_service = new NetworkConfigurationService(
_gatewayDetector,
_internetTargetProvider,
_pingService,
Options.Create(options ?? new MonitorOptions()),
NullLogger<NetworkConfigurationService>.Instance);
return _service;
}
public void Dispose()
{
_service?.Dispose();
}
[Fact]
public async Task GetRouterAddressAsync_WhenExplicitlyConfigured_ReturnsConfiguredAddress()
{
// Arrange
var options = new MonitorOptions { RouterAddress = "10.0.0.1" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("10.0.0.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_WhenAutoDetect_UsesDetectedGateway()
{
// Arrange
_gatewayDetector.WithGateway("192.168.1.1");
_pingService.AlwaysSucceed(5);
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("192.168.1.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_WhenDetectionFails_FallsBackToCommonGateways()
{
// Arrange
_gatewayDetector.WithNoGateway();
_gatewayDetector.WithCommonGateways("192.168.0.1", "10.0.0.1");
_pingService.AlwaysSucceed(5);
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("192.168.0.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_WhenNoGatewayReachable_ReturnsNull()
{
// Arrange
_gatewayDetector.WithNoGateway();
_gatewayDetector.WithCommonGateways(); // Empty
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Null(result);
}
[Fact]
public async Task GetInternetTargetAsync_ReturnsPrimaryTarget()
{
// Arrange
_internetTargetProvider.WithPrimaryTarget("1.1.1.1");
var service = CreateService();
// Act
var result = await service.GetInternetTargetAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("1.1.1.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_CachesResult()
{
// Arrange
_gatewayDetector.WithGateway("192.168.1.1");
_pingService.AlwaysSucceed(5);
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act - call twice
var result1 = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Change the gateway - should not affect second call due to caching
_gatewayDetector.WithGateway("10.0.0.1");
var result2 = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert - both should return cached value
Assert.Equal("192.168.1.1", result1);
Assert.Equal("192.168.1.1", result2);
}
[Fact]
public void Dispose_CanBeCalledMultipleTimes()
{
// Arrange
var service = CreateService();
// Act & Assert - should not throw
service.Dispose();
service.Dispose();
}
[Fact]
public async Task GetRouterAddressAsync_AfterDispose_ThrowsObjectDisposedException()
{
// Arrange
var service = CreateService();
service.Dispose();
_service = null; // Prevent double dispose in cleanup
// Act & Assert
await Assert.ThrowsAsync<ObjectDisposedException>(
() => service.GetRouterAddressAsync(TestContext.Current.CancellationToken));
}
[Fact]
public async Task GetInternetTargetAsync_AfterDispose_ThrowsObjectDisposedException()
{
// Arrange
var service = CreateService();
service.Dispose();
_service = null; // Prevent double dispose in cleanup
// Act & Assert
await Assert.ThrowsAsync<ObjectDisposedException>(
() => service.GetInternetTargetAsync(TestContext.Current.CancellationToken));
}
}
EOF
log_success "NetworkConfigurationServiceTests updated"
# =============================================================================
# Fix 3: Update NetworkMonitorServiceTests to implement IDisposable
# =============================================================================
log_info "Updating NetworkMonitorServiceTests to implement IDisposable..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Services/NetworkMonitorServiceTests.cs" << 'EOF'
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NetworkMonitor.Core.Models;
using NetworkMonitor.Core.Services;
using NetworkMonitor.Tests.Fakes;
using Xunit;
namespace NetworkMonitor.Tests.Services;
/// <summary>
/// Tests for NetworkMonitorService.
/// Uses fake implementations for isolation.
/// </summary>
public sealed class NetworkMonitorServiceTests : IDisposable
{
private readonly FakePingService _pingService;
private readonly FakeNetworkConfigurationService _configService;
private readonly NetworkMonitorService _service;
public NetworkMonitorServiceTests()
{
_pingService = new FakePingService();
_configService = new FakeNetworkConfigurationService();
var options = Options.Create(new MonitorOptions());
_service = new NetworkMonitorService(
_pingService,
_configService,
options,
NullLogger<NetworkMonitorService>.Instance);
}
public void Dispose()
{
_configService.Dispose();
}
[Fact]
public async Task CheckNetworkAsync_WhenBothSucceed_ReturnsExcellentOrGood()
{
// Arrange
_pingService.AlwaysSucceed(latencyMs: 5);
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Excellent or NetworkHealth.Good);
Assert.True(status.RouterResult?.Success);
Assert.True(status.InternetResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenRouterFails_ReturnsOfflineOrDegraded()
{
// Arrange - router fails, internet succeeds
_pingService
.QueueResult(PingResult.Failed("192.168.1.1", "Timeout"))
.QueueResult(PingResult.Failed("192.168.1.1", "Timeout"))
.QueueResult(PingResult.Failed("192.168.1.1", "Timeout"))
.AlwaysSucceed(latencyMs: 10); // Internet succeeds
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Offline or NetworkHealth.Degraded or NetworkHealth.Poor);
Assert.False(status.RouterResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenInternetFails_ReturnsDegradedOrPoor()
{
// Arrange - router succeeds, internet fails
_pingService
.QueueResult(PingResult.Succeeded("192.168.1.1", 5))
.QueueResult(PingResult.Failed("8.8.8.8", "Timeout"))
.QueueResult(PingResult.Failed("8.8.8.8", "Timeout"))
.QueueResult(PingResult.Failed("8.8.8.8", "Timeout"));
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Degraded or NetworkHealth.Poor or NetworkHealth.Offline);
Assert.True(status.RouterResult?.Success);
Assert.False(status.InternetResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenBothFail_ReturnsOffline()
{
// Arrange - both fail
_pingService.AlwaysFail("Network unreachable");
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal(NetworkHealth.Offline, status.Health);
Assert.False(status.RouterResult?.Success);
Assert.False(status.InternetResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenNoRouter_StillChecksInternet()
{
// Arrange - no router configured
_configService.WithRouterAddress(null);
_pingService.AlwaysSucceed(latencyMs: 10);
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Null(status.RouterResult);
Assert.NotNull(status.InternetResult);
Assert.True(status.InternetResult.Success);
}
[Fact]
public async Task CheckNetworkAsync_HighLatency_ReturnsGoodOrDegraded()
{
// Arrange
_pingService.AlwaysSucceed(latencyMs: 150);
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Good or NetworkHealth.Degraded);
}
[Fact]
public async Task StatusChanged_FiresWhenHealthChanges()
{
// Arrange
NetworkStatusEventArgs? receivedArgs = null;
_service.StatusChanged += (_, args) => receivedArgs = args;
_pingService.AlwaysSucceed(latencyMs: 5);
// Act - first check establishes baseline
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Change to failing
_pingService.AlwaysFail("Network error");
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.NotNull(receivedArgs);
Assert.NotNull(receivedArgs.CurrentStatus);
}
[Fact]
public async Task StatusChanged_IncludesPreviousStatus()
{
// Arrange
var receivedArgs = new List<NetworkStatusEventArgs>();
_service.StatusChanged += (_, args) => receivedArgs.Add(args);
_pingService.AlwaysSucceed(latencyMs: 5);
// Act - first check
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Second check - should have previous
_pingService.AlwaysFail("Timeout");
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(receivedArgs.Count >= 1);
// The second event should have a previous status
if (receivedArgs.Count > 1)
{
Assert.NotNull(receivedArgs[1].PreviousStatus);
}
}
[Fact]
public async Task CheckNetworkAsync_CancellationToken_Respected()
{
// Arrange
using var cts = new CancellationTokenSource();
cts.Cancel();
// Act & Assert
await Assert.ThrowsAnyAsync<OperationCanceledException>(
() => _service.CheckNetworkAsync(cts.Token));
}
}
EOF
log_success "NetworkMonitorServiceTests updated with IDisposable"
# =============================================================================
# Update InternetTargetProviderTests to use Microsoft's NullLogger
# =============================================================================
log_info "Updating InternetTargetProviderTests..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Services/InternetTargetProviderTests.cs" << 'EOF'
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NetworkMonitor.Core.Models;
using NetworkMonitor.Core.Services;
using Xunit;
namespace NetworkMonitor.Tests.Services;
/// <summary>
/// Tests for InternetTargetProvider.
/// </summary>
public sealed class InternetTargetProviderTests
{
[Fact]
public void PrimaryTarget_ReturnsConfiguredTarget()
{
// Arrange
var options = Options.Create(new MonitorOptions { InternetTarget = "1.1.1.1" });
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act & Assert
Assert.Equal("1.1.1.1", provider.PrimaryTarget);
}
[Fact]
public void GetTargets_ReturnsConfiguredTargetFirst()
{
// Arrange
var options = Options.Create(new MonitorOptions { InternetTarget = "1.1.1.1" });
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act
var targets = provider.GetTargets();
// Assert
Assert.Equal("1.1.1.1", targets[0]);
}
[Fact]
public void GetTargets_IncludesMultipleFallbacks()
{
// Arrange
var options = Options.Create(new MonitorOptions());
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act
var targets = provider.GetTargets();
// Assert
Assert.True(targets.Count >= 3, "Should have multiple fallback targets");
Assert.Contains("8.8.8.8", targets);
Assert.Contains("1.1.1.1", targets);
}
[Fact]
public void GetTargets_CustomTargetAddedToFront()
{
// Arrange - use a target not in the default list
var options = Options.Create(new MonitorOptions { InternetTarget = "4.4.4.4" });
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act
var targets = provider.GetTargets();
// Assert
Assert.Equal("4.4.4.4", targets[0]);
Assert.Contains("8.8.8.8", targets); // Default fallbacks still present
}
}
EOF
log_success "InternetTargetProviderTests updated"
# =============================================================================
# Ensure FakeNetworkConfigurationService implements IDisposable
# =============================================================================
log_info "Ensuring FakeNetworkConfigurationService implements IDisposable..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Fakes/FakeNetworkConfigurationService.cs" << 'EOF'
using NetworkMonitor.Core.Services;
namespace NetworkMonitor.Tests.Fakes;
/// <summary>
/// Fake network configuration service for testing.
/// Returns configurable addresses without actual network operations.
/// </summary>
public sealed class FakeNetworkConfigurationService : INetworkConfigurationService, IDisposable
{
private string? _routerAddress = "192.168.1.1";
private string _internetTarget = "8.8.8.8";
public FakeNetworkConfigurationService WithRouterAddress(string? address)
{
_routerAddress = address;
return this;
}
public FakeNetworkConfigurationService WithInternetTarget(string target)
{
_internetTarget = target;
return this;
}
public Task<string?> GetRouterAddressAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(_routerAddress);
public Task<string> GetInternetTargetAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(_internetTarget);
public void Dispose()
{
// Nothing to dispose in fake
}
}
EOF
log_success "FakeNetworkConfigurationService updated"
# =============================================================================
# Ensure FakeInternetTargetProvider exists with proper methods
# =============================================================================
log_info "Ensuring FakeInternetTargetProvider is properly configured..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Fakes/FakeInternetTargetProvider.cs" << 'EOF'
using NetworkMonitor.Core.Services;
namespace NetworkMonitor.Tests.Fakes;
/// <summary>
/// Fake internet target provider for testing.
/// </summary>
public sealed class FakeInternetTargetProvider : IInternetTargetProvider
{
private string _primaryTarget = "8.8.8.8";
private List<string> _targets = new() { "8.8.8.8", "1.1.1.1", "208.67.222.222" };
public string PrimaryTarget => _primaryTarget;
public FakeInternetTargetProvider WithPrimaryTarget(string target)
{
_primaryTarget = target;
if (!_targets.Contains(target))
{
_targets.Insert(0, target);
}
return this;
}
public FakeInternetTargetProvider WithTargets(params string[] targets)
{
_targets = targets.ToList();
if (_targets.Count > 0)
{
_primaryTarget = _targets[0];
}
return this;
}
public IReadOnlyList<string> GetTargets() => _targets;
}
EOF
log_success "FakeInternetTargetProvider updated"
# =============================================================================
# Add NetworkStatusEventArgsTests for better coverage
# =============================================================================
log_info "Adding NetworkStatusEventArgsTests..."
mkdir -p "$SRC_DIR/NetworkMonitor.Tests/Models"
cat > "$SRC_DIR/NetworkMonitor.Tests/Models/NetworkStatusEventArgsTests.cs" << 'EOF'
using NetworkMonitor.Core.Models;
using Xunit;
namespace NetworkMonitor.Tests.Models;
/// <summary>
/// Tests for NetworkStatusEventArgs.
/// </summary>
public sealed class NetworkStatusEventArgsTests
{
private static NetworkStatus CreateTestStatus(NetworkHealth health) =>
new(health, null, null, DateTimeOffset.UtcNow, "Test");
[Fact]
public void Constructor_SingleArg_SetsCurrentStatus()
{
// Arrange
var status = CreateTestStatus(NetworkHealth.Excellent);
// Act
var args = new NetworkStatusEventArgs(status);
// Assert
Assert.Equal(status, args.CurrentStatus);
Assert.Null(args.PreviousStatus);
}
[Fact]
public void Constructor_TwoArgs_SetsBothStatuses()
{
// Arrange
var current = CreateTestStatus(NetworkHealth.Excellent);
var previous = CreateTestStatus(NetworkHealth.Poor);
// Act
var args = new NetworkStatusEventArgs(current, previous);
// Assert
Assert.Equal(current, args.CurrentStatus);
Assert.Equal(previous, args.PreviousStatus);
}
[Fact]
public void Status_ReturnsCurrentStatus()
{
// Arrange
var current = CreateTestStatus(NetworkHealth.Good);
var previous = CreateTestStatus(NetworkHealth.Degraded);
var args = new NetworkStatusEventArgs(current, previous);
// Act & Assert
Assert.Same(args.CurrentStatus, args.Status);
}
[Fact]
public void Constructor_WithNullPrevious_Succeeds()
{
// Arrange
var current = CreateTestStatus(NetworkHealth.Excellent);
// Act
var args = new NetworkStatusEventArgs(current, null);
// Assert
Assert.Equal(current, args.CurrentStatus);
Assert.Null(args.PreviousStatus);
}
}
EOF
log_success "NetworkStatusEventArgsTests added"
# =============================================================================
# Add PingResultTests for better coverage
# =============================================================================
log_info "Adding PingResultTests..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Models/PingResultTests.cs" << 'EOF'
using NetworkMonitor.Core.Models;
using Xunit;
namespace NetworkMonitor.Tests.Models;
/// <summary>
/// Tests for PingResult.
/// </summary>
public sealed class PingResultTests
{
[Fact]
public void Succeeded_CreatesSuccessfulResult()
{
// Act
var result = PingResult.Succeeded("8.8.8.8", 15);
// Assert
Assert.True(result.Success);
Assert.Equal("8.8.8.8", result.Target);
Assert.Equal(15, result.LatencyMs);
Assert.Null(result.ErrorMessage);
}
[Fact]
public void Failed_CreatesFailedResult()
{
// Act
var result = PingResult.Failed("8.8.8.8", "Request timed out");
// Assert
Assert.False(result.Success);
Assert.Equal("8.8.8.8", result.Target);
Assert.Null(result.LatencyMs);
Assert.Equal("Request timed out", result.ErrorMessage);
}
[Fact]
public void Timestamp_IsSetToCurrentTime()
{
// Arrange
var before = DateTimeOffset.UtcNow;
// Act
var result = PingResult.Succeeded("8.8.8.8", 10);
// Assert
var after = DateTimeOffset.UtcNow;
Assert.True(result.Timestamp >= before);
Assert.True(result.Timestamp <= after);
}
[Fact]
public void Succeeded_WithZeroLatency_IsValid()
{
// Act
var result = PingResult.Succeeded("localhost", 0);
// Assert
Assert.True(result.Success);
Assert.Equal(0, result.LatencyMs);
}
}
EOF
log_success "PingResultTests added"
# =============================================================================
# Add NetworkHealthTests for better coverage
# =============================================================================
log_info "Adding NetworkHealthTests..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Models/NetworkHealthTests.cs" << 'EOF'
using NetworkMonitor.Core.Models;
using Xunit;
namespace NetworkMonitor.Tests.Models;
/// <summary>
/// Tests for NetworkHealth enum values.
/// </summary>
public sealed class NetworkHealthTests
{
[Fact]
public void NetworkHealth_HasExpectedValues()
{
// Assert all expected values exist
Assert.Equal(0, (int)NetworkHealth.Offline);
Assert.Equal(1, (int)NetworkHealth.Poor);
Assert.Equal(2, (int)NetworkHealth.Degraded);
Assert.Equal(3, (int)NetworkHealth.Good);
Assert.Equal(4, (int)NetworkHealth.Excellent);
}
[Fact]
public void NetworkHealth_CanCompare()
{
// Assert ordering works as expected
Assert.True(NetworkHealth.Excellent > NetworkHealth.Good);
Assert.True(NetworkHealth.Good > NetworkHealth.Degraded);
Assert.True(NetworkHealth.Degraded > NetworkHealth.Poor);
Assert.True(NetworkHealth.Poor > NetworkHealth.Offline);
}
}
EOF
log_success "NetworkHealthTests added"
# =============================================================================
# Build and Test
# =============================================================================
log_info "Building solution..."
cd "$SRC_DIR"
if dotnet build --nologo -v q; then
log_success "Build succeeded!"
else
log_error "Build failed!"
exit 1
fi
log_info "Running tests..."
if dotnet test --nologo -v q; then
log_success "All tests passed!"
else
log_error "Some tests failed. Please review the output above."
exit 1
fi
# =============================================================================
# Summary
# =============================================================================
echo ""
echo "============================================================================="
echo -e "${GREEN}Fix Summary${NC}"
echo "============================================================================="
echo ""
echo "Fixed 3 build errors:"
echo ""
echo "1. CS0104: Ambiguous NullLogger<> reference (NetworkConfigurationServiceTests)"
echo " - Removed custom NullLogger.cs from Fakes folder"
echo " - Now using Microsoft.Extensions.Logging.Abstractions.NullLogger<T>"
echo ""
echo "2. CS0104: Ambiguous NullLogger<> reference (NetworkMonitorServiceTests)"
echo " - Same fix as above - using Microsoft's NullLogger<T>"
echo ""
echo "3. CA1001: NetworkMonitorServiceTests owns disposable field '_configService'"
echo " - Made NetworkMonitorServiceTests implement IDisposable"
echo " - Added Dispose() method that disposes _configService"
echo ""
echo "Added/Updated test files for better coverage:"
echo " - NetworkStatusEventArgsTests.cs"
echo " - PingResultTests.cs"
echo " - NetworkHealthTests.cs"
echo " - NetworkConfigurationServiceTests.cs (with dispose tests)"
echo " - NetworkMonitorServiceTests.cs (implements IDisposable)"
echo " - InternetTargetProviderTests.cs"
echo ""
echo "Updated Fakes:"
echo " - Removed NullLogger.cs (using Microsoft's version)"
echo " - FakeNetworkConfigurationService.cs (implements IDisposable)"
echo " - FakeInternetTargetProvider.cs (fluent configuration)"
echo ""
echo "============================================================================="
[INFO] Working directory: /home/kushal/src/dotnet/network-monitor/src
[INFO] Source directory: .
[INFO] Removing custom NullLogger (will use Microsoft's version)...
[SUCCESS] Removed custom NullLogger.cs
[INFO] Updating NetworkConfigurationServiceTests...
[SUCCESS] NetworkConfigurationServiceTests updated
[INFO] Updating NetworkMonitorServiceTests to implement IDisposable...
[SUCCESS] NetworkMonitorServiceTests updated with IDisposable
[INFO] Updating InternetTargetProviderTests...
[SUCCESS] InternetTargetProviderTests updated
[INFO] Ensuring FakeNetworkConfigurationService implements IDisposable...
[SUCCESS] FakeNetworkConfigurationService updated
[INFO] Ensuring FakeInternetTargetProvider is properly configured...
[SUCCESS] FakeInternetTargetProvider updated
[INFO] Adding NetworkStatusEventArgsTests...
[SUCCESS] NetworkStatusEventArgsTests added
[INFO] Adding PingResultTests...
[SUCCESS] PingResultTests added
[INFO] Adding NetworkHealthTests...
[SUCCESS] NetworkHealthTests added
[INFO] Building solution...
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Fakes/FakeNetworkConfigurationService.cs(9,55): error CS0535: 'FakeNetworkConfigurationService' does not implement interface member 'INetworkConfigurationService.InitializeAsync(CancellationToken)'
[ERROR] Build failed!
real 0m1.645s
user 0m1.314s
sys 0m0.283s
kushal@syn-2600-6c56-9840-001d-0000-0000-0000-1157:~/src/dotnet/network-monitor/src$ cd ~/src/dotnet/network-monitor/; time bash export.sh; cd ~/src/dotnet/network-monitor/src; time dotnet --info; time dotnet format; time dotnet restore; time dotnet clean; time dotnet build; time dotnet test; time dotnet list package; time dotnet list package --outdated;
Starting project export...
Project Path: /home/kushal/src/dotnet/network-monitor
Output File: docs/llm/dump.txt
Generating directory structure...
Collecting files...
Found 51 files to export
Processing (1/51): .github/workflows/build-and-test.yml
Processing (2/51): .github/workflows/release.yml
Processing (3/51): src/Directory.Build.props
Processing (4/51): src/Directory.Packages.props
Processing (5/51): src/NetworkMonitor.Console/appsettings.json
Processing (6/51): src/NetworkMonitor.Console/NetworkMonitor.Console.csproj
Processing (7/51): src/NetworkMonitor.Console/Program.cs
Processing (8/51): src/NetworkMonitor.Core/Exporters/FileExporterExtensions.cs
Processing (9/51): src/NetworkMonitor.Core/Exporters/FileExporterOptions.cs
Processing (10/51): src/NetworkMonitor.Core/Exporters/FileMetricExporter.cs
Processing (11/51): src/NetworkMonitor.Core/Models/HistoricalData.cs
Processing (12/51): src/NetworkMonitor.Core/Models/MonitorOptions.cs
Processing (13/51): src/NetworkMonitor.Core/Models/NetworkStatus.cs
Processing (14/51): src/NetworkMonitor.Core/Models/NetworkStatusEventArgs.cs
Processing (15/51): src/NetworkMonitor.Core/Models/PingResult.cs
Processing (16/51): src/NetworkMonitor.Core/Models/StorageOptions.cs
Processing (17/51): src/NetworkMonitor.Core/NetworkMonitor.Core.csproj
Processing (18/51): src/NetworkMonitor.Core/ServiceCollectionExtensions.cs
Processing (19/51): src/NetworkMonitor.Core/Services/ConsoleStatusDisplay.cs
Processing (20/51): src/NetworkMonitor.Core/Services/GatewayDetector.cs
Processing (21/51): src/NetworkMonitor.Core/Services/IGatewayDetector.cs
Processing (22/51): src/NetworkMonitor.Core/Services/IInternetTargetProvider.cs
Processing (23/51): src/NetworkMonitor.Core/Services/INetworkConfigurationService.cs
Processing (24/51): src/NetworkMonitor.Core/Services/INetworkMonitorService.cs
Processing (25/51): src/NetworkMonitor.Core/Services/InternetTargetProvider.cs
Processing (26/51): src/NetworkMonitor.Core/Services/IPingService.cs
Processing (27/51): src/NetworkMonitor.Core/Services/IStatusDisplay.cs
Processing (28/51): src/NetworkMonitor.Core/Services/MonitorBackgroundService.cs
Processing (29/51): src/NetworkMonitor.Core/Services/NetworkConfigurationService.cs
Processing (30/51): src/NetworkMonitor.Core/Services/NetworkMonitorService.cs
Processing (31/51): src/NetworkMonitor.Core/Services/PingService.cs
Processing (32/51): src/NetworkMonitor.Core/Storage/IStorageService.cs
Processing (33/51): src/NetworkMonitor.Core/Storage/SqliteStorageService.cs
Processing (34/51): src/NetworkMonitor.slnx
Processing (35/51): src/NetworkMonitor.Tests/Fakes/FakeGatewayDetector.cs
Processing (36/51): src/NetworkMonitor.Tests/Fakes/FakeInternetTargetProvider.cs
Processing (37/51): src/NetworkMonitor.Tests/Fakes/FakeNetworkConfigurationService.cs
Processing (38/51): src/NetworkMonitor.Tests/Fakes/FakePingService.cs
Processing (39/51): src/NetworkMonitor.Tests/Fakes/FakePingServiceTests.cs
Processing (40/51): src/NetworkMonitor.Tests/Fakes/FakeStorageService.cs
Processing (41/51): src/NetworkMonitor.Tests/Models/MonitorOptionsTests.cs
Processing (42/51): src/NetworkMonitor.Tests/Models/NetworkHealthTests.cs
Processing (43/51): src/NetworkMonitor.Tests/Models/NetworkStatusEventArgsTests.cs
Processing (44/51): src/NetworkMonitor.Tests/Models/PingResultTests.cs
Processing (45/51): src/NetworkMonitor.Tests/NetworkMonitor.Tests.csproj
Processing (46/51): src/NetworkMonitor.Tests/NetworkStatusTests.cs
Processing (47/51): src/NetworkMonitor.Tests/PingResultTests.cs
Processing (48/51): src/NetworkMonitor.Tests/Services/GatewayDetectorTests.cs
Processing (49/51): src/NetworkMonitor.Tests/Services/InternetTargetProviderTests.cs
Processing (50/51): src/NetworkMonitor.Tests/Services/NetworkConfigurationServiceTests.cs
Processing (51/51): src/NetworkMonitor.Tests/Services/NetworkMonitorServiceTests.cs
Export completed successfully!
Output file: /home/kushal/src/dotnet/network-monitor/docs/llm/dump.txt
Total files exported: 51
Output file size: 0.14 MB
real 0m0.459s
user 0m0.244s
sys 0m0.314s
.NET SDK:
Version: 10.0.101
Commit: fad253f51b
Workload version: 10.0.101.1
MSBuild version: 18.0.6+fad253f51
Runtime Environment:
OS Name: fedora
OS Version: 43
OS Platform: Linux
RID: fedora.43-x64
Base Path: /usr/lib64/dotnet/sdk/10.0.101/
.NET workloads installed:
[android]
Installation Source: SDK 10.0.100
Manifest Version: 36.1.2/10.0.100
Manifest Path: /home/kushal/.dotnet/sdk-manifests/10.0.100/microsoft.net.sdk.android/36.1.2/WorkloadManifest.json
Install Type: FileBased
Configured to use workload sets when installing new manifests.
Host:
Version: 10.0.1
Architecture: x64
Commit: fad253f51b
.NET SDKs installed:
10.0.101 [/usr/lib64/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 10.0.1 [/usr/lib64/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 10.0.1 [/usr/lib64/dotnet/shared/Microsoft.NETCore.App]
Other architectures found:
None
Environment variables:
DOTNET_BUNDLE_EXTRACT_BASE_DIR [/home/kushal/.cache/dotnet_bundle_extract]
DOTNET_ROOT [/usr/lib64/dotnet]
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download
real 0m0.154s
user 0m0.124s
sys 0m0.029s
real 0m5.621s
user 0m7.976s
sys 0m0.725s
Restore complete (0.5s)
Build succeeded in 0.6s
real 0m0.691s
user 0m0.713s
sys 0m0.124s
Build succeeded in 0.4s
real 0m0.585s
user 0m0.599s
sys 0m0.106s
Restore complete (0.4s)
NetworkMonitor.Core net10.0 succeeded (0.2s) → NetworkMonitor.Core/bin/Debug/net10.0/NetworkMonitor.Core.dll
NetworkMonitor.Tests net10.0 failed with 4 error(s) (0.1s)
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Fakes/FakeNetworkConfigurationService.cs(33,17): error CS7036: There is no argument given that corresponds to the required parameter 'result' of 'Task.FromResult<TResult>(TResult)'
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Models/PingResultTests.cs(20,33): error CS1061: 'PingResult' does not contain a definition for 'LatencyMs' and no accessible extension method 'LatencyMs' accepting a first argument of type 'PingResult' could be found (are you missing a using directive or an assembly reference?)
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Models/PingResultTests.cs(33,28): error CS1061: 'PingResult' does not contain a definition for 'LatencyMs' and no accessible extension method 'LatencyMs' accepting a first argument of type 'PingResult' could be found (are you missing a using directive or an assembly reference?)
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Models/PingResultTests.cs(60,32): error CS1061: 'PingResult' does not contain a definition for 'LatencyMs' and no accessible extension method 'LatencyMs' accepting a first argument of type 'PingResult' could be found (are you missing a using directive or an assembly reference?)
NetworkMonitor.Console net10.0 succeeded (0.3s) → NetworkMonitor.Console/bin/Debug/net10.0/NetworkMonitor.Console.dll
Build failed with 4 error(s) in 1.1s
real 0m1.208s
user 0m1.220s
sys 0m0.228s
Restore complete (0.4s)
NetworkMonitor.Core net10.0 succeeded (0.0s) → NetworkMonitor.Core/bin/Debug/net10.0/NetworkMonitor.Core.dll
NetworkMonitor.Tests net10.0 failed with 4 error(s) (0.1s)
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Fakes/FakeNetworkConfigurationService.cs(33,17): error CS7036: There is no argument given that corresponds to the required parameter 'result' of 'Task.FromResult<TResult>(TResult)'
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Models/PingResultTests.cs(20,33): error CS1061: 'PingResult' does not contain a definition for 'LatencyMs' and no accessible extension method 'LatencyMs' accepting a first argument of type 'PingResult' could be found (are you missing a using directive or an assembly reference?)
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Models/PingResultTests.cs(33,28): error CS1061: 'PingResult' does not contain a definition for 'LatencyMs' and no accessible extension method 'LatencyMs' accepting a first argument of type 'PingResult' could be found (are you missing a using directive or an assembly reference?)
/home/kushal/src/dotnet/network-monitor/src/NetworkMonitor.Tests/Models/PingResultTests.cs(60,32): error CS1061: 'PingResult' does not contain a definition for 'LatencyMs' and no accessible extension method 'LatencyMs' accepting a first argument of type 'PingResult' could be found (are you missing a using directive or an assembly reference?)
Build failed with 4 error(s) in 0.8s
real 0m0.923s
user 0m0.891s
sys 0m0.210s
Restore complete (0.4s)
Build succeeded in 0.5s
Project 'NetworkMonitor.Console' has the following package references
[net10.0]:
Top-level Package Requested Resolved
> Microsoft.Extensions.Hosting 10.0.1 10.0.1
> OpenTelemetry.Exporter.Console 1.14.0 1.14.0
> OpenTelemetry.Extensions.Hosting 1.14.0 1.14.0
> OpenTelemetry.Instrumentation.Runtime 1.14.0 1.14.0
Project 'NetworkMonitor.Core' has the following package references
[net10.0]:
Top-level Package Requested Resolved
> Microsoft.Data.Sqlite 10.0.1 10.0.1
> Microsoft.Extensions.Hosting 10.0.1 10.0.1
> Microsoft.Extensions.Logging.Abstractions 10.0.1 10.0.1
> Microsoft.Extensions.Options 10.0.1 10.0.1
> OpenTelemetry 1.14.0 1.14.0
> OpenTelemetry.Exporter.Console 1.14.0 1.14.0
> OpenTelemetry.Extensions.Hosting 1.14.0 1.14.0
> OpenTelemetry.Instrumentation.Runtime 1.14.0 1.14.0
Project 'NetworkMonitor.Tests' has the following package references
[net10.0]:
Top-level Package Requested Resolved
> Microsoft.Extensions.Logging.Abstractions 10.0.1 10.0.1
> Microsoft.NET.Test.Sdk 18.0.1 18.0.1
> xunit.runner.visualstudio 3.1.5 3.1.5
> xunit.v3 3.2.1 3.2.1
real 0m1.369s
user 0m1.370s
sys 0m0.247s
Restore complete (0.4s)
Build succeeded in 0.5s
The following sources were used:
https://api.nuget.org/v3/index.json
The given project `NetworkMonitor.Console` has no updates given the current sources.
The given project `NetworkMonitor.Core` has no updates given the current sources.
The given project `NetworkMonitor.Tests` has no updates given the current sources.
real 0m1.555s
user 0m1.545s
sys 0m0.250s
kushal@syn-2600-6c56-9840-001d-0000-0000-0000-1157:~/src/dotnet/network-monitor/src$ cd ~/src/dotnet/network-monitor/src/; cat generate-network-monitor.sh; time bash generate-network-monitor.sh
#!/bin/bash
# =============================================================================
# Fix Build Errors Script for NetworkMonitor
# Fixes:
# 1. CS0104: Ambiguous NullLogger<> reference (2 instances)
# 2. CA1001: NetworkMonitorServiceTests owns disposable field but is not disposable
# 3. CS7036: Task.FromResult requires argument - use Task.CompletedTask
# 4. CS1061: PingResult uses RoundtripTimeMs, not LatencyMs
# =============================================================================
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() { echo -e "${YELLOW}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Determine the source directory
if [[ -d "src/NetworkMonitor.Core" ]]; then
SRC_DIR="src"
elif [[ -d "NetworkMonitor.Core" ]]; then
SRC_DIR="."
else
log_error "Cannot find NetworkMonitor source directory"
exit 1
fi
log_info "Working directory: $(pwd)"
log_info "Source directory: $SRC_DIR"
# =============================================================================
# Fix 1: Remove custom NullLogger (use Microsoft.Extensions.Logging.Abstractions)
# =============================================================================
log_info "Removing custom NullLogger (will use Microsoft's version)..."
NULLLOGGER_FILE="$SRC_DIR/NetworkMonitor.Tests/Fakes/NullLogger.cs"
if [[ -f "$NULLLOGGER_FILE" ]]; then
rm "$NULLLOGGER_FILE"
log_success "Removed custom NullLogger.cs"
else
log_info "NullLogger.cs already removed or not found"
fi
# =============================================================================
# Fix 2: Update FakeNetworkConfigurationService with InitializeAsync
# =============================================================================
log_info "Updating FakeNetworkConfigurationService..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Fakes/FakeNetworkConfigurationService.cs" << 'EOF'
using NetworkMonitor.Core.Services;
namespace NetworkMonitor.Tests.Fakes;
/// <summary>
/// Fake network configuration service for testing.
/// Returns configurable addresses without actual network operations.
/// </summary>
public sealed class FakeNetworkConfigurationService : INetworkConfigurationService, IDisposable
{
private string? _routerAddress = "192.168.1.1";
private string _internetTarget = "8.8.8.8";
public FakeNetworkConfigurationService WithRouterAddress(string? address)
{
_routerAddress = address;
return this;
}
public FakeNetworkConfigurationService WithInternetTarget(string target)
{
_internetTarget = target;
return this;
}
public Task<string?> GetRouterAddressAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(_routerAddress);
public Task<string> GetInternetTargetAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(_internetTarget);
public Task InitializeAsync(CancellationToken cancellationToken = default)
=> Task.CompletedTask;
public void Dispose()
{
// Nothing to dispose in fake
}
}
EOF
log_success "FakeNetworkConfigurationService updated with InitializeAsync"
# =============================================================================
# Fix 3: Update FakeInternetTargetProvider with WithPrimaryTarget method
# =============================================================================
log_info "Updating FakeInternetTargetProvider..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Fakes/FakeInternetTargetProvider.cs" << 'EOF'
using NetworkMonitor.Core.Services;
namespace NetworkMonitor.Tests.Fakes;
/// <summary>
/// Fake internet target provider for testing.
/// </summary>
public sealed class FakeInternetTargetProvider : IInternetTargetProvider
{
private string _primaryTarget = "8.8.8.8";
private List<string> _targets = new() { "8.8.8.8", "1.1.1.1", "208.67.222.222" };
public string PrimaryTarget => _primaryTarget;
public FakeInternetTargetProvider WithPrimaryTarget(string target)
{
_primaryTarget = target;
if (!_targets.Contains(target))
{
_targets.Insert(0, target);
}
return this;
}
public FakeInternetTargetProvider WithTargets(params string[] targets)
{
_targets = targets.ToList();
if (_targets.Count > 0)
{
_primaryTarget = _targets[0];
}
return this;
}
public IReadOnlyList<string> GetTargets() => _targets;
}
EOF
log_success "FakeInternetTargetProvider updated"
# =============================================================================
# Fix 4: Update NetworkConfigurationServiceTests
# =============================================================================
log_info "Updating NetworkConfigurationServiceTests..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Services/NetworkConfigurationServiceTests.cs" << 'EOF'
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NetworkMonitor.Core.Models;
using NetworkMonitor.Core.Services;
using NetworkMonitor.Tests.Fakes;
using Xunit;
namespace NetworkMonitor.Tests.Services;
/// <summary>
/// Tests for NetworkConfigurationService.
/// </summary>
public sealed class NetworkConfigurationServiceTests : IDisposable
{
private readonly FakeGatewayDetector _gatewayDetector;
private readonly FakeInternetTargetProvider _internetTargetProvider;
private readonly FakePingService _pingService;
private NetworkConfigurationService? _service;
public NetworkConfigurationServiceTests()
{
_gatewayDetector = new FakeGatewayDetector();
_internetTargetProvider = new FakeInternetTargetProvider();
_pingService = new FakePingService();
}
private NetworkConfigurationService CreateService(MonitorOptions? options = null)
{
_service = new NetworkConfigurationService(
_gatewayDetector,
_internetTargetProvider,
_pingService,
Options.Create(options ?? new MonitorOptions()),
NullLogger<NetworkConfigurationService>.Instance);
return _service;
}
public void Dispose()
{
_service?.Dispose();
}
[Fact]
public async Task GetRouterAddressAsync_WhenExplicitlyConfigured_ReturnsConfiguredAddress()
{
// Arrange
var options = new MonitorOptions { RouterAddress = "10.0.0.1" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("10.0.0.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_WhenAutoDetect_UsesDetectedGateway()
{
// Arrange
_gatewayDetector.WithGateway("192.168.1.1");
_pingService.AlwaysSucceed(5);
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("192.168.1.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_WhenDetectionFails_FallsBackToCommonGateways()
{
// Arrange
_gatewayDetector.WithNoGateway();
_gatewayDetector.WithCommonGateways("192.168.0.1", "10.0.0.1");
_pingService.AlwaysSucceed(5);
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("192.168.0.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_WhenNoGatewayReachable_ReturnsNull()
{
// Arrange
_gatewayDetector.WithNoGateway();
_gatewayDetector.WithCommonGateways(); // Empty
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act
var result = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Null(result);
}
[Fact]
public async Task GetInternetTargetAsync_ReturnsPrimaryTarget()
{
// Arrange
_internetTargetProvider.WithPrimaryTarget("1.1.1.1");
var service = CreateService();
// Act
var result = await service.GetInternetTargetAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal("1.1.1.1", result);
}
[Fact]
public async Task GetRouterAddressAsync_CachesResult()
{
// Arrange
_gatewayDetector.WithGateway("192.168.1.1");
_pingService.AlwaysSucceed(5);
var options = new MonitorOptions { RouterAddress = "auto" };
var service = CreateService(options);
// Act - call twice
var result1 = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Change the gateway - should not affect second call due to caching
_gatewayDetector.WithGateway("10.0.0.1");
var result2 = await service.GetRouterAddressAsync(TestContext.Current.CancellationToken);
// Assert - both should return cached value
Assert.Equal("192.168.1.1", result1);
Assert.Equal("192.168.1.1", result2);
}
[Fact]
public void Dispose_CanBeCalledMultipleTimes()
{
// Arrange
var service = CreateService();
// Act & Assert - should not throw
service.Dispose();
service.Dispose();
}
[Fact]
public async Task GetRouterAddressAsync_AfterDispose_ThrowsObjectDisposedException()
{
// Arrange
var service = CreateService();
service.Dispose();
_service = null; // Prevent double dispose in cleanup
// Act & Assert
await Assert.ThrowsAsync<ObjectDisposedException>(
() => service.GetRouterAddressAsync(TestContext.Current.CancellationToken));
}
[Fact]
public async Task GetInternetTargetAsync_AfterDispose_ThrowsObjectDisposedException()
{
// Arrange
var service = CreateService();
service.Dispose();
_service = null; // Prevent double dispose in cleanup
// Act & Assert
await Assert.ThrowsAsync<ObjectDisposedException>(
() => service.GetInternetTargetAsync(TestContext.Current.CancellationToken));
}
}
EOF
log_success "NetworkConfigurationServiceTests updated"
# =============================================================================
# Fix 5: Update NetworkMonitorServiceTests to implement IDisposable
# =============================================================================
log_info "Updating NetworkMonitorServiceTests to implement IDisposable..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Services/NetworkMonitorServiceTests.cs" << 'EOF'
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NetworkMonitor.Core.Models;
using NetworkMonitor.Core.Services;
using NetworkMonitor.Tests.Fakes;
using Xunit;
namespace NetworkMonitor.Tests.Services;
/// <summary>
/// Tests for NetworkMonitorService.
/// Uses fake implementations for isolation.
/// </summary>
public sealed class NetworkMonitorServiceTests : IDisposable
{
private readonly FakePingService _pingService;
private readonly FakeNetworkConfigurationService _configService;
private readonly NetworkMonitorService _service;
public NetworkMonitorServiceTests()
{
_pingService = new FakePingService();
_configService = new FakeNetworkConfigurationService();
var options = Options.Create(new MonitorOptions());
_service = new NetworkMonitorService(
_pingService,
_configService,
options,
NullLogger<NetworkMonitorService>.Instance);
}
public void Dispose()
{
_configService.Dispose();
}
[Fact]
public async Task CheckNetworkAsync_WhenBothSucceed_ReturnsExcellentOrGood()
{
// Arrange
_pingService.AlwaysSucceed(latencyMs: 5);
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Excellent or NetworkHealth.Good);
Assert.True(status.RouterResult?.Success);
Assert.True(status.InternetResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenRouterFails_ReturnsOfflineOrDegraded()
{
// Arrange - router fails, internet succeeds
_pingService
.QueueResult(PingResult.Failed("192.168.1.1", "Timeout"))
.QueueResult(PingResult.Failed("192.168.1.1", "Timeout"))
.QueueResult(PingResult.Failed("192.168.1.1", "Timeout"))
.AlwaysSucceed(latencyMs: 10); // Internet succeeds
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Offline or NetworkHealth.Degraded or NetworkHealth.Poor);
Assert.False(status.RouterResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenInternetFails_ReturnsDegradedOrPoor()
{
// Arrange - router succeeds, internet fails
_pingService
.QueueResult(PingResult.Succeeded("192.168.1.1", 5))
.QueueResult(PingResult.Failed("8.8.8.8", "Timeout"))
.QueueResult(PingResult.Failed("8.8.8.8", "Timeout"))
.QueueResult(PingResult.Failed("8.8.8.8", "Timeout"));
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Degraded or NetworkHealth.Poor or NetworkHealth.Offline);
Assert.True(status.RouterResult?.Success);
Assert.False(status.InternetResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenBothFail_ReturnsOffline()
{
// Arrange - both fail
_pingService.AlwaysFail("Network unreachable");
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Equal(NetworkHealth.Offline, status.Health);
Assert.False(status.RouterResult?.Success);
Assert.False(status.InternetResult?.Success);
}
[Fact]
public async Task CheckNetworkAsync_WhenNoRouter_StillChecksInternet()
{
// Arrange - no router configured
_configService.WithRouterAddress(null);
_pingService.AlwaysSucceed(latencyMs: 10);
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.Null(status.RouterResult);
Assert.NotNull(status.InternetResult);
Assert.True(status.InternetResult.Success);
}
[Fact]
public async Task CheckNetworkAsync_HighLatency_ReturnsGoodOrDegraded()
{
// Arrange
_pingService.AlwaysSucceed(latencyMs: 150);
// Act
var status = await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(status.Health is NetworkHealth.Good or NetworkHealth.Degraded);
}
[Fact]
public async Task StatusChanged_FiresWhenHealthChanges()
{
// Arrange
NetworkStatusEventArgs? receivedArgs = null;
_service.StatusChanged += (_, args) => receivedArgs = args;
_pingService.AlwaysSucceed(latencyMs: 5);
// Act - first check establishes baseline
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Change to failing
_pingService.AlwaysFail("Network error");
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.NotNull(receivedArgs);
Assert.NotNull(receivedArgs.CurrentStatus);
}
[Fact]
public async Task StatusChanged_IncludesPreviousStatus()
{
// Arrange
var receivedArgs = new List<NetworkStatusEventArgs>();
_service.StatusChanged += (_, args) => receivedArgs.Add(args);
_pingService.AlwaysSucceed(latencyMs: 5);
// Act - first check
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Second check - should have previous
_pingService.AlwaysFail("Timeout");
await _service.CheckNetworkAsync(TestContext.Current.CancellationToken);
// Assert
Assert.True(receivedArgs.Count >= 1);
// The second event should have a previous status
if (receivedArgs.Count > 1)
{
Assert.NotNull(receivedArgs[1].PreviousStatus);
}
}
[Fact]
public async Task CheckNetworkAsync_CancellationToken_Respected()
{
// Arrange
using var cts = new CancellationTokenSource();
cts.Cancel();
// Act & Assert
await Assert.ThrowsAnyAsync<OperationCanceledException>(
() => _service.CheckNetworkAsync(cts.Token));
}
}
EOF
log_success "NetworkMonitorServiceTests updated with IDisposable"
# =============================================================================
# Fix 6: Update InternetTargetProviderTests to use Microsoft's NullLogger
# =============================================================================
log_info "Updating InternetTargetProviderTests..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Services/InternetTargetProviderTests.cs" << 'EOF'
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NetworkMonitor.Core.Models;
using NetworkMonitor.Core.Services;
using Xunit;
namespace NetworkMonitor.Tests.Services;
/// <summary>
/// Tests for InternetTargetProvider.
/// </summary>
public sealed class InternetTargetProviderTests
{
[Fact]
public void PrimaryTarget_ReturnsConfiguredTarget()
{
// Arrange
var options = Options.Create(new MonitorOptions { InternetTarget = "1.1.1.1" });
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act & Assert
Assert.Equal("1.1.1.1", provider.PrimaryTarget);
}
[Fact]
public void GetTargets_ReturnsConfiguredTargetFirst()
{
// Arrange
var options = Options.Create(new MonitorOptions { InternetTarget = "1.1.1.1" });
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act
var targets = provider.GetTargets();
// Assert
Assert.Equal("1.1.1.1", targets[0]);
}
[Fact]
public void GetTargets_IncludesMultipleFallbacks()
{
// Arrange
var options = Options.Create(new MonitorOptions());
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act
var targets = provider.GetTargets();
// Assert
Assert.True(targets.Count >= 3, "Should have multiple fallback targets");
Assert.Contains("8.8.8.8", targets);
Assert.Contains("1.1.1.1", targets);
}
[Fact]
public void GetTargets_CustomTargetAddedToFront()
{
// Arrange - use a target not in the default list
var options = Options.Create(new MonitorOptions { InternetTarget = "4.4.4.4" });
var provider = new InternetTargetProvider(options, NullLogger<InternetTargetProvider>.Instance);
// Act
var targets = provider.GetTargets();
// Assert
Assert.Equal("4.4.4.4", targets[0]);
Assert.Contains("8.8.8.8", targets); // Default fallbacks still present
}
}
EOF
log_success "InternetTargetProviderTests updated"
# =============================================================================
# Fix 7: Update PingResultTests to use RoundtripTimeMs (correct property name)
# =============================================================================
log_info "Updating PingResultTests with correct property name..."
cat > "$SRC_DIR/NetworkMonitor.Tests/Models/PingResultTests.cs" << 'EOF'
using NetworkMonitor.Core.Models;
using Xunit;
namespace NetworkMonitor.Tests.Models;
/// <summary>
/// Tests for PingResult.
/// </summary>
public sealed class PingResultTests
{
[Fact]
public void Succeeded_CreatesSuccessfulResult()
{
// Act
var result = PingResult.Succeeded("8.8.8.8", 15);
// Assert
Assert.True(result.Success);
Assert.Equal("8.8.8.8", result.Target);
Assert.Equal(15, result.RoundtripTimeMs);
Assert.Null(result.ErrorMessage);
}
[Fact]
public void Failed_CreatesFailedResult()
{
// Act
var result = PingResult.Failed("8.8.8.8", "Request timed out");
// Assert
Assert.False(result.Success);
Assert.Equal("8.8.8.8", result.Target);
Assert.Null(result.RoundtripTimeMs);
Assert.Equal("Request timed out", result.ErrorMessage);
}
[Fact]
public void Timestamp_IsSetToCurrentTime()
{
// Arrange
var before = DateTimeOffset.UtcNow;
// Act
var result = PingResult.Succeeded("8.8.8.8", 10);
// Assert
var after = DateTimeOffset.UtcNow;
Assert.True(result.Timestamp >= before);
Assert.True(result.Timestamp <= after);
}
[Fact]
public void Succeeded_WithZeroLatency_IsValid()
{
// Act
var result = PingResult.Succeeded("localhost", 0);
// Assert
Assert.True(result.Success);
Assert.Equal(0, result.RoundtripTimeMs);
}
[Fact]
public void Record_Equality_WorksCorrectly()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow;
var result1 = new PingResult("8.8.8.8", true, 10, timestamp);
var result2 = new PingResult("8.8.8.8", true, 10, timestamp);
var result3 = new PingResult("8.8.8.8", true, 20, timestamp);
// Assert
Assert.Equal(result1, result2);
Assert.NotEqual(result1, result3);
}
}
EOF
log_success "PingResultTests updated with RoundtripTimeMs"
# =============================================================================
# Fix 8: Add NetworkStatusEventArgsTests
# =============================================================================
log_info "Adding NetworkStatusEventArgsTests..."
mkdir -p "$SRC_DIR/NetworkMonitor.Tests/Models"
cat > "$SRC_DIR/NetworkMonitor.Tests/Models/NetworkStatusEventArgsTests.cs" << 'EOF'
using NetworkMonitor.Core.Models;
using Xunit;
namespace NetworkMonitor.Tests.Models;
/// <summary>
/// Tests for NetworkStatusEventArgs.
/// </summary>
public sealed class NetworkStatusEventArgsTests
{
private static NetworkStatus CreateTestStatus(NetworkHealth health) =>
new(health, null, null, DateTimeOffset.UtcNow, "Test");
[Fact]
public void Constructor_SingleArg_SetsCurrentStatus()
{
// Arrange
var status = CreateTestStatus(NetworkHealth.Excellent);
// Act
var args = new NetworkStatusEventArgs(status);
// Assert
Assert.Equal(status, args.CurrentStatus);
Assert.Null(args.PreviousStatus);
}
[Fact]
public void Constructor_TwoArgs_SetsBothStatuses()
{
// Arrange
var current = CreateTestStatus(NetworkHealth.Excellent);
var previous = CreateTestStatus(NetworkHealth.Poor);
// Act
var args = new NetworkStatusEventArgs(current, previous);
// Assert
Assert.Equal(current, args.CurrentStatus);
Assert.Equal(previous, args.PreviousStatus);
}
[Fact]
public void Status_ReturnsCurrentStatus()
{
// Arrange
var current = CreateTestStatus(NetworkHealth.Good);
var previous = CreateTestStatus(NetworkHealth.Degraded);
var args = new NetworkStatusEventArgs(current, previous);
// Act & Assert
Assert.Same(args.CurrentStatus, args.Status);
}
[Fact]
public void Constructor_WithNullPrevious_Succeeds()
{
// Arrange
var current = CreateTestStatus(NetworkHealth.Excellent);
// Act
var args = new NetworkStatusEventArgs(current, null);
// Assert
Assert.Equal(current, args.CurrentStatus);
Assert.Null(args.PreviousStatus);
}
}
EOF
log_success "NetworkStatusEventArgsTests added"
# =============================================================================
# Fix 9: Add NetworkHealthTests
# =============================================================================
log_info "Adding NetworkHealthTests…1 parent a9029f4 commit 6ad85ff
2 files changed
+217
-3
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
80 | 82 | | |
81 | 83 | | |
82 | 84 | | |
| |||
4555 | 4557 | | |
4556 | 4558 | | |
4557 | 4559 | | |
4558 | | - | |
| 4560 | + | |
4559 | 4561 | | |
4560 | 4562 | | |
4561 | 4563 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7044 | 7044 | | |
7045 | 7045 | | |
7046 | 7046 | | |
| 7047 | + | |
| 7048 | + | |
| 7049 | + | |
| 7050 | + | |
| 7051 | + | |
| 7052 | + | |
| 7053 | + | |
| 7054 | + | |
| 7055 | + | |
| 7056 | + | |
| 7057 | + | |
| 7058 | + | |
| 7059 | + | |
| 7060 | + | |
| 7061 | + | |
| 7062 | + | |
| 7063 | + | |
| 7064 | + | |
| 7065 | + | |
| 7066 | + | |
| 7067 | + | |
| 7068 | + | |
| 7069 | + | |
| 7070 | + | |
| 7071 | + | |
| 7072 | + | |
| 7073 | + | |
| 7074 | + | |
| 7075 | + | |
| 7076 | + | |
| 7077 | + | |
| 7078 | + | |
| 7079 | + | |
| 7080 | + | |
| 7081 | + | |
| 7082 | + | |
| 7083 | + | |
| 7084 | + | |
| 7085 | + | |
| 7086 | + | |
| 7087 | + | |
| 7088 | + | |
| 7089 | + | |
| 7090 | + | |
| 7091 | + | |
| 7092 | + | |
| 7093 | + | |
| 7094 | + | |
| 7095 | + | |
| 7096 | + | |
| 7097 | + | |
| 7098 | + | |
| 7099 | + | |
| 7100 | + | |
| 7101 | + | |
| 7102 | + | |
| 7103 | + | |
| 7104 | + | |
| 7105 | + | |
| 7106 | + | |
| 7107 | + | |
| 7108 | + | |
| 7109 | + | |
| 7110 | + | |
| 7111 | + | |
| 7112 | + | |
| 7113 | + | |
| 7114 | + | |
| 7115 | + | |
| 7116 | + | |
| 7117 | + | |
| 7118 | + | |
| 7119 | + | |
| 7120 | + | |
| 7121 | + | |
| 7122 | + | |
| 7123 | + | |
| 7124 | + | |
| 7125 | + | |
| 7126 | + | |
| 7127 | + | |
| 7128 | + | |
| 7129 | + | |
| 7130 | + | |
| 7131 | + | |
| 7132 | + | |
| 7133 | + | |
| 7134 | + | |
| 7135 | + | |
| 7136 | + | |
| 7137 | + | |
| 7138 | + | |
| 7139 | + | |
| 7140 | + | |
| 7141 | + | |
| 7142 | + | |
| 7143 | + | |
| 7144 | + | |
| 7145 | + | |
| 7146 | + | |
| 7147 | + | |
| 7148 | + | |
| 7149 | + | |
| 7150 | + | |
| 7151 | + | |
| 7152 | + | |
| 7153 | + | |
| 7154 | + | |
| 7155 | + | |
| 7156 | + | |
| 7157 | + | |
| 7158 | + | |
| 7159 | + | |
| 7160 | + | |
| 7161 | + | |
| 7162 | + | |
| 7163 | + | |
| 7164 | + | |
| 7165 | + | |
| 7166 | + | |
| 7167 | + | |
| 7168 | + | |
| 7169 | + | |
| 7170 | + | |
| 7171 | + | |
| 7172 | + | |
| 7173 | + | |
| 7174 | + | |
| 7175 | + | |
| 7176 | + | |
| 7177 | + | |
| 7178 | + | |
| 7179 | + | |
| 7180 | + | |
| 7181 | + | |
| 7182 | + | |
| 7183 | + | |
| 7184 | + | |
| 7185 | + | |
| 7186 | + | |
| 7187 | + | |
| 7188 | + | |
| 7189 | + | |
| 7190 | + | |
| 7191 | + | |
| 7192 | + | |
| 7193 | + | |
| 7194 | + | |
| 7195 | + | |
| 7196 | + | |
| 7197 | + | |
| 7198 | + | |
| 7199 | + | |
| 7200 | + | |
| 7201 | + | |
| 7202 | + | |
| 7203 | + | |
| 7204 | + | |
| 7205 | + | |
| 7206 | + | |
| 7207 | + | |
| 7208 | + | |
| 7209 | + | |
| 7210 | + | |
| 7211 | + | |
| 7212 | + | |
| 7213 | + | |
| 7214 | + | |
| 7215 | + | |
| 7216 | + | |
| 7217 | + | |
| 7218 | + | |
| 7219 | + | |
| 7220 | + | |
| 7221 | + | |
| 7222 | + | |
| 7223 | + | |
| 7224 | + | |
| 7225 | + | |
| 7226 | + | |
| 7227 | + | |
| 7228 | + | |
| 7229 | + | |
| 7230 | + | |
| 7231 | + | |
| 7232 | + | |
| 7233 | + | |
| 7234 | + | |
| 7235 | + | |
| 7236 | + | |
| 7237 | + | |
| 7238 | + | |
| 7239 | + | |
| 7240 | + | |
| 7241 | + | |
| 7242 | + | |
| 7243 | + | |
| 7244 | + | |
| 7245 | + | |
| 7246 | + | |
| 7247 | + | |
| 7248 | + | |
| 7249 | + | |
| 7250 | + | |
| 7251 | + | |
| 7252 | + | |
| 7253 | + | |
| 7254 | + | |
| 7255 | + | |
| 7256 | + | |
| 7257 | + | |
| 7258 | + | |
0 commit comments