Files
ClawdDotNet/tests/ClawdDotNet.Core.Tests/Deploymentcenter/InstanceHealthProviderTests.cs
T
RichardandClaude Opus 5.5 77062facc9 Stufe 4c (#764): Bezeichner auf Englisch - Typen, Methoden, Felder, Parameter, Locals, Testnamen
Mit Roslyn umbenannt (Verweise folgen, Zeichenketten bleiben unberuehrt).
Nicht umbenannt, weil gespeichert oder ueber die API gebunden (Stufe 4e):
Properties, Record-Parameter, Enum-Member sowie die Parameter der
Endpunkt-Handler (Routen-/Query-Bindung ueber den Namen). Die JSON-Felder
anonymer Objekte bleiben ausdruecklich benannt (JsonFilter: pfad).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:18:43 +02:00

108 lines
4.0 KiB
C#

using ClawdDotNet.Core.Accounting;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Deploymentcenter.Watchdog;
using Shouldly;
namespace ClawdDotNet.Core.Tests.Deploymentcenter;
public sealed class InstanceHealthProviderTests
{
/// <summary>Delivers a fixed daily consumption, without a DB.</summary>
private sealed class FakeUsage(DailyUsage daily) : IUsageRepository
{
public Task RecordAsync(RunUsage usage, CancellationToken ct) => Task.CompletedTask;
public Task<DailyUsage> GetDailyAsync(DateOnly date, string agentId, CancellationToken ct)
=> Task.FromResult(daily);
public Task<IReadOnlyList<RunUsage>> GetRecentAsync(int limit, CancellationToken ct)
=> Task.FromResult((IReadOnlyList<RunUsage>)Array.Empty<RunUsage>());
public Task<int> PurgeBeforeAsync(DateOnly date, CancellationToken ct) => Task.FromResult(0);
}
private static DailyUsage Usage(decimal cost, int tokens = 0) =>
new(DateOnly.FromDateTime(DateTime.Now), tokens, 0, cost, true, 1);
[Fact]
public async Task Without_API_key_reports_error()
{
var provider = new InstanceHealthProvider(
"Test", agentsEnabled: false, InstanceBudget.Unlimited,
usage: null, () => 2, () => 0);
var health = await provider.GetAsync(default);
health.Status.ShouldBe(WatchdogStatus.Error);
health.Metrics["agentCount"].ShouldBe(2);
health.Checks["agents"].Ok.ShouldBeFalse();
}
[Fact]
public async Task Exhausted_cost_budget_reports_warning()
{
var budget = new InstanceBudget { DailyCostUsd = 5m };
var provider = new InstanceHealthProvider(
"Test", agentsEnabled: true, budget,
new FakeUsage(Usage(cost: 5m)), () => 1, () => 0);
var health = await provider.GetAsync(default);
health.Status.ShouldBe(WatchdogStatus.Warning);
health.Checks["budget"].Ok.ShouldBeFalse();
health.Message.ShouldNotBeNull();
}
/// <summary>
/// A heartbeat only proves that a thread is running. If the scanner stands still, the
/// instance no longer works through anything — and exactly that is what the check should make visible.
/// </summary>
[Fact]
public async Task Stalled_scanner_reports_warning()
{
var provider = new InstanceHealthProvider(
"Test", agentsEnabled: true, InstanceBudget.Unlimited,
new FakeUsage(Usage(cost: 0m)), () => 1, () => 0,
schedulerRunning: () => false);
var health = await provider.GetAsync(default);
health.Status.ShouldBe(WatchdogStatus.Warning);
health.Checks["scheduler"].Ok.ShouldBeFalse();
}
[Fact]
public async Task Normal_operation_reports_ok_with_metrics()
{
var provider = new InstanceHealthProvider(
"Test", agentsEnabled: true, InstanceBudget.Unlimited,
new FakeUsage(Usage(cost: 0.5m, tokens: 1200)), () => 3, () => 1,
schedulerRunning: () => true);
var health = await provider.GetAsync(default);
health.Status.ShouldBe(WatchdogStatus.Ok);
health.Metrics["runningChats"].ShouldBe(1);
health.Metrics.Keys.ShouldContain("todayCostUsd");
health.Metrics.Keys.ShouldContain("todayTokens");
health.Checks.Values.ShouldAllBe(c => c.Ok);
}
/// <summary>
/// Metrics are only evaluated server-side if they are numbers — everything else
/// the history silently discards. Descriptive things belong in the message.
/// </summary>
[Fact]
public async Task Instance_name_is_in_the_message_not_in_the_metrics()
{
var provider = new InstanceHealthProvider(
"Produktion", agentsEnabled: true, InstanceBudget.Unlimited,
usage: null, () => 1, () => 0);
var health = await provider.GetAsync(default);
health.Message.ShouldNotBeNull().ShouldContain("Produktion");
health.Metrics.Keys.ShouldNotContain("instanceName");
}
}