Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft. Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch. WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b umbenannten Parameter angeglichen. Dabei behoben: - 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue" gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day. - Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine CSS-Klasse gab - jetzt "success" wie laufende Instanzen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
3.1 KiB
C#
88 lines
3.1 KiB
C#
using ClawdDotNet.Core.Deploymentcenter.Watchdog;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Shouldly;
|
|
|
|
namespace ClawdDotNet.Core.Tests.Deploymentcenter;
|
|
|
|
public sealed class WatchdogHeartbeatServiceTests
|
|
{
|
|
/// <summary>Remembers what was sent, without a network.</summary>
|
|
private sealed class RecordingClient : IWatchdogClient
|
|
{
|
|
public List<(string Status, string? Message)> Heartbeats { get; } = [];
|
|
public List<(string Kind, string? Message)> Events { get; } = [];
|
|
|
|
public Task<WatchdogPingResult> SendHeartbeatAsync(
|
|
InstanceHealth health, int intervalSeconds, CancellationToken ct)
|
|
{
|
|
Heartbeats.Add((health.Status, health.Message));
|
|
return Task.FromResult(new WatchdogPingResult("up", []));
|
|
}
|
|
|
|
public Task SendEventAsync(
|
|
string kind, string severity, string? message, object? meta, CancellationToken ct)
|
|
{
|
|
Events.Add((kind, message));
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
|
|
private sealed class FixedHealth(InstanceHealth health) : IInstanceHealthProvider
|
|
{
|
|
public Task<InstanceHealth> GetAsync(CancellationToken ct) => Task.FromResult(health);
|
|
}
|
|
|
|
private static WatchdogHeartbeatService Build(RecordingClient client) =>
|
|
new(client, new FixedHealth(InstanceHealth.Ok("läuft")), 3600, NullLogger.Instance);
|
|
|
|
[Fact]
|
|
public async Task Regulaeres_Ende_meldet_stopped()
|
|
{
|
|
var client = new RecordingClient();
|
|
var service = Build(client);
|
|
|
|
service.Start();
|
|
await service.DisposeAsync();
|
|
|
|
client.Heartbeats[^1].Status.ShouldBe(WatchdogStatus.Stopped);
|
|
client.Events[^1].Kind.ShouldBe(WatchdogEventKind.StoppedGraceful);
|
|
}
|
|
|
|
/// <summary>
|
|
/// On update the instance is about to go away, but comes back. <c>stopped</c> would be
|
|
/// the wrong information: the monitor would be left alone until someone touches it.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Angekuendigte_Wartung_meldet_maintenance()
|
|
{
|
|
var client = new RecordingClient();
|
|
var service = Build(client);
|
|
|
|
service.Start();
|
|
service.AnnounceMaintenance("Update auf 0.1.1 wird eingespielt.");
|
|
await service.DisposeAsync();
|
|
|
|
var last = client.Heartbeats[^1];
|
|
last.Status.ShouldBe(WatchdogStatus.Maintenance);
|
|
last.Message.ShouldNotBeNull().ShouldContain("0.1.1");
|
|
|
|
client.Events[^1].Kind.ShouldBe(WatchdogEventKind.MaintenanceStart);
|
|
}
|
|
|
|
/// <summary>The first beat goes out immediately, not only after a full interval.</summary>
|
|
[Fact]
|
|
public async Task Erster_Heartbeat_kommt_sofort()
|
|
{
|
|
var client = new RecordingClient();
|
|
var service = Build(client);
|
|
|
|
service.Start();
|
|
await service.DisposeAsync();
|
|
|
|
// Start event, at least one beat, then the deregistration.
|
|
client.Events[0].Kind.ShouldBe(WatchdogEventKind.Started);
|
|
client.Heartbeats.Count.ShouldBeGreaterThanOrEqualTo(2);
|
|
client.Heartbeats[0].Status.ShouldBe(WatchdogStatus.Ok);
|
|
}
|
|
}
|