Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions .github/workflows/dotnetcore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- run: git fetch --prune --unshallow

- name: Setup .NET Core 8
- name: Setup .NET 10
uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
with:
dotnet-version: '8.0.x'
dotnet-version: '10.0.x'

- name: Install dependencies
run: dotnet restore
Expand All @@ -34,19 +34,8 @@ jobs:
id: gitversion # step id used as reference for output values
uses: gittools/actions/gitversion/execute@51d325634925d7d9ce0a7efc2c586c0bc2b9eee6 # v3.2.1

# - name: Setup SonarScanner
# run: dotnet tool install --tool-path artifacts dotnet-sonarscanner

# - name: SonarScanner begin
# run: artifacts/dotnet-sonarscanner begin /k:"Haproxy.AgentCheck" /o:"lucca" /d:sonar.login=${{ secrets.SONAR_TOKEN }} /d:sonar.host.url="https://sonarcloud.io/" /d:sonar.cs.opencover.reportsPaths="./coverage.opencover.xml"

- name: Test
run: dotnet test --no-restore --verbosity minimal /p:CollectCoverage=true /p:CoverletOutput=../ /p:CoverletOutputFormat=opencover --logger:"console;verbosity=detailed"

# - name: SonarScanner end
# run: artifacts/dotnet-sonarscanner end /d:sonar.login=${{ secrets.SONAR_TOKEN }}
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: dotnet test --solution Haproxy.AgentCheck.sln --coverage --report-github

- name: Publish win-x64
run: |
Expand Down
28 changes: 10 additions & 18 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,22 @@
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.22" />
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.1" />
<PackageVersion Include="Microsoft.CodeCoverage" Version="18.0.1" />
<PackageVersion Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.652701" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.28" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="9.10.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.11" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.1.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.11" />
<PackageVersion Include="System.IO.Pipelines" Version="9.0.11" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Systemd" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.0" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="10.0.0" />
<PackageVersion Include="xunit.v3.mtp-v2" Version="3.2.1" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="10.16.0.128591" />
</ItemGroup>
</Project>
</Project>
198 changes: 198 additions & 0 deletions Haproxy.AgentCheck.Tests/CountersStateTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
using Lucca.Infra.Haproxy.AgentCheck.Config;
using Lucca.Infra.Haproxy.AgentCheck.Metrics;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Lucca.Infra.Haproxy.AgentCheck.Tests;

public class CountersStateTests(ITestOutputHelper testOutputHelper)
{
[Fact]
public void UpdateState_WithCounterValue_UpdatesWeight()
{
var serviceProvider = CreateServiceProvider(
new RuleConfig
{
Name = "custom-counter",
Source = RuleSource.Counters,
Weight = new WeightRule
{
SystemResponse = SystemResponse.Linear,
MinValue = 0,
MaxValue = 100,
MinWeight = 0,
MaxWeight = 100
}
});

var sut = serviceProvider.GetRequiredService<State>();
sut.UpdateState(new CountersState
{
Values = new Dictionary<string, double> { ["custom-counter"] = 50 }
});

Assert.True(sut.IsUp);
Assert.Equal(50, sut.Weight, 0.01);
}

[Fact]
public void UpdateState_WithCounterAboveThreshold_BreaksCircuit()
{
var serviceProvider = CreateServiceProvider(
new RuleConfig
{
Name = "error-rate",
Source = RuleSource.Counters,
Failure = new FailureRule
{
EnterThreshold = 10,
LeaveThreshold = 5
}
});

var sut = serviceProvider.GetRequiredService<State>();
sut.UpdateState(new CountersState
{
Values = new Dictionary<string, double> { ["error-rate"] = 15 }
});

Assert.False(sut.IsUp);
Assert.Contains("Counters/error-rate", sut.BrokenCircuitsBreakers);
}

[Fact]
public void UpdateState_WhenCounterRemoved_RemovesBrokenCircuit()
{
var serviceProvider = CreateServiceProvider(
new RuleConfig
{
Name = "temp-counter",
Source = RuleSource.Counters,
Failure = new FailureRule
{
EnterThreshold = 10,
LeaveThreshold = 5
}
});

var sut = serviceProvider.GetRequiredService<State>();

// First update with counter above threshold
sut.UpdateState(new CountersState
{
Values = new Dictionary<string, double> { ["temp-counter"] = 15 }
});
Assert.Contains("Counters/temp-counter", sut.BrokenCircuitsBreakers);

// Second update without the counter
sut.UpdateState(new CountersState
{
Values = new Dictionary<string, double>()
});

Assert.DoesNotContain("Counters/temp-counter", sut.BrokenCircuitsBreakers);
}

[Fact]
public void UpdateState_WithMultipleCounters_UsesLowestWeight()
{
var serviceProvider = CreateServiceProvider(
new RuleConfig
{
Name = "counter1",
Source = RuleSource.Counters,
Weight = new WeightRule
{
SystemResponse = SystemResponse.Linear,
MinValue = 0,
MaxValue = 100,
MinWeight = 0,
MaxWeight = 100
}
},
new RuleConfig
{
Name = "counter2",
Source = RuleSource.Counters,
Weight = new WeightRule
{
SystemResponse = SystemResponse.Linear,
MinValue = 0,
MaxValue = 100,
MinWeight = 0,
MaxWeight = 100
}
});

var sut = serviceProvider.GetRequiredService<State>();
sut.UpdateState(new CountersState
{
Values = new Dictionary<string, double>
{
["counter1"] = 20, // Weight would be 80
["counter2"] = 90 // Weight would be 10
}
});

Assert.True(sut.IsUp);
Assert.Equal(10, sut.Weight, 0.01);
}

[Fact]
public void UpdateState_CombineSystemAndCounters_UsesLowestWeight()
{
var serviceProvider = CreateServiceProvider(
new RuleConfig
{
Name = "CPU",
Source = RuleSource.System,
Weight = new WeightRule
{
SystemResponse = SystemResponse.Linear,
MinValue = 0,
MaxValue = 100,
MinWeight = 0,
MaxWeight = 100
}
},
new RuleConfig
{
Name = "custom-metric",
Source = RuleSource.Counters,
Weight = new WeightRule
{
SystemResponse = SystemResponse.Linear,
MinValue = 0,
MaxValue = 100,
MinWeight = 0,
MaxWeight = 100
}
});

var sut = serviceProvider.GetRequiredService<State>();

// CPU at 30% -> weight 70
sut.UpdateState(new SystemState { CpuPercent = 30 });

// Custom metric at 80 -> weight 20
sut.UpdateState(new CountersState
{
Values = new Dictionary<string, double> { ["custom-metric"] = 80 }
});

Assert.True(sut.IsUp);
Assert.Equal(20, sut.Weight, 0.01);
}

private IServiceProvider CreateServiceProvider(params RuleConfig[] rules)
{
return new ServiceCollection()
.AddOptions()
.Configure<RulesConfig>(o => o.AddRange(rules))
.AddFakeLogging(o => o.OutputSink = testOutputHelper.WriteLine)
.AddSingleton<TestTimeProvider>()
.AddSingleton<TimeProvider>(p => p.GetRequiredService<TestTimeProvider>())
.AddSingleton<State>()
.BuildServiceProvider();
}
}
16 changes: 5 additions & 11 deletions Haproxy.AgentCheck.Tests/Haproxy.AgentCheck.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="GitHubActionsTestLogger" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="coverlet.msbuild">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="xunit.v3.mtp-v2" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading