Files
ClawdDotNet/tests/ClawdDotNet.Core.Tests/Tasks/TaskboardAusbauTests.cs
T
RichardandClaude Opus 5 0b6ea73ec1 Entscheidungen aus dem Testbetrieb umgesetzt (#572): #577 #579 #581 #589 #193 #205 #594 #595 #597 #598 #599 #588
- Engine: leere Antworten nachfassen, Aufgaben enden mit Ergebniszeile (EmptyResponse/Incomplete),
  gleiche Fehlschlaege erkennen (RepeatedFailure), Zeitzeile vor Chatnachrichten,
  Werkzeugergebnisse gerahmt, Vorpruefung vor dem Staging, Belegkosten wie RunUsage
- Staging: gescheiterte Freigaben als Failed; AgentEditor-Aenderungen freigabepflichtig,
  Selbstbearbeitung gesperrt (allowSelfEdit)
- Taskboard: Termine beim Anlegen pruefen, naechster/letzter Lauf, pausieren, Sofortlauf,
  Kostenprognose, Hinweis auf wachsenden Kontext; Jobs-Reiter und /api/jobs entfernt
- Release: Instanzmanager samt Weboberflaeche im Paket, setup.json fragt manager.json ab,
  Token nicht mehr ins systemd-Journal
Version 0.1.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 11:23:10 +02:00

110 lines
4.3 KiB
C#

using ClawdDotNet.Core.Tasks;
using Shouldly;
namespace ClawdDotNet.Core.Tests.Tasks;
/// <summary>
/// Termine pruefen, naechster Lauf und Laeufe je Tag (#594, #595, #597) sowie der Hinweis
/// auf wachsenden Kontext (#598).
/// </summary>
public sealed class TaskboardAusbauTests
{
private static DateTime Utc(int y, int mo, int d, int h, int mi) => new(y, mo, d, h, mi, 0, DateTimeKind.Utc);
private static TaskWhen Cron(string value, string tz = "Europe/Berlin")
=> new() { Kind = TaskWhenKind.Cron, Value = value, TimeZone = tz };
// ─── #597 Termin pruefen ───
[Fact]
public void Gueltiger_Termin_mit_Bereich_und_Schritt_besteht_die_Pruefung()
=> TaskSchedule.ValidateWhen(Cron("0 7-22/3 * * *")).ShouldBeNull();
[Fact]
public void Kaputter_Cron_Ausdruck_wird_benannt()
=> TaskSchedule.ValidateWhen(Cron("0 7-22/x * * *")).ShouldNotBeNull().ShouldContain("'x' ist keine Zahl");
[Fact]
public void Unbekannte_Zeitzone_wird_benannt()
=> TaskSchedule.ValidateWhen(Cron("0 9 * * *", "Phantasie/Ort")).ShouldNotBeNull().ShouldContain("Phantasie/Ort");
[Theory]
[InlineData(TaskWhenKind.Every, "30m", true)]
[InlineData(TaskWhenKind.Every, "halbstündlich", false)]
[InlineData(TaskWhenKind.At, "2026-09-15T08:00", true)]
[InlineData(TaskWhenKind.At, "morgen früh", false)]
public void Intervall_und_Zeitpunkt_werden_geprueft(TaskWhenKind kind, string value, bool gueltig)
=> (TaskSchedule.ValidateWhen(new TaskWhen { Kind = kind, Value = value, TimeZone = "Europe/Berlin" }) is null)
.ShouldBe(gueltig);
// ─── #594 naechster Lauf ───
[Fact]
public void Naechster_Lauf_eines_Cron_Termins_in_Berliner_Zeit()
{
var task = new TaskItem { Id = "t-7e0bbe", When = Cron("0 7-22/3 * * *") };
// 12:30 Berlin (MESZ) -> naechster Termin 13:00 Berlin = 11:00 UTC
TaskSchedule.NextOccurrenceUtc(task, Utc(2026, 9, 14, 10, 30)).ShouldBe(Utc(2026, 9, 14, 11, 0));
}
[Fact]
public void Naechster_Lauf_eines_Intervalls_zaehlt_ab_dem_letzten_Lauf()
{
var task = new TaskItem
{
Id = "t-1",
When = new TaskWhen { Kind = TaskWhenKind.Every, Value = "2h", TimeZone = "" },
LastOccurrence = "2026-09-14T09:00:00.0000000Z"
};
TaskSchedule.NextOccurrenceUtc(task, Utc(2026, 9, 14, 10, 0)).ShouldBe(Utc(2026, 9, 14, 11, 0));
}
[Fact]
public void Einmalige_Aufgabe_steht_an_bis_sie_lief()
{
var jetzt = Utc(2026, 9, 14, 10, 0);
TaskSchedule.NextOccurrenceUtc(new TaskItem { Id = "t-1" }, jetzt).ShouldBe(jetzt);
TaskSchedule.NextOccurrenceUtc(new TaskItem { Id = "t-1", LastOccurrence = "x" }, jetzt).ShouldBeNull();
}
// ─── #595 Laeufe je Tag ───
[Fact]
public void Laeufe_je_Tag_aus_Cron_und_Intervall()
{
var jetzt = Utc(2026, 9, 14, 10, 0);
TaskSchedule.RunsPerDay(new TaskItem { When = Cron("0 7-22/3 * * *") }, jetzt).ShouldBe(6);
TaskSchedule.RunsPerDay(new TaskItem { When = Cron("5 * * * *") }, jetzt).ShouldBe(24);
TaskSchedule.RunsPerDay(new TaskItem { When = new TaskWhen { Kind = TaskWhenKind.Every, Value = "30m" } }, jetzt).ShouldBe(48);
TaskSchedule.RunsPerDay(new TaskItem(), jetzt).ShouldBe(0);
}
// ─── #598 Hinweis ───
[Fact]
public void Wiederkehrend_im_Chatverlauf_bekommt_den_Hinweis()
=> TaskHinweise.WachsenderKontext(new TaskItem { Assignee = "@nachrichten", When = Cron("0 7-22/3 * * *") })
.ShouldNotBeNull().ShouldContain("@new:nachrichten");
[Theory]
[InlineData("@new:nachrichten", true)]
[InlineData("@nachrichten", false)]
[InlineData("@human", true)]
public void Kein_Hinweis_fuer_frische_Laeufe_einmalige_und_menschliche_Aufgaben(string assignee, bool wiederkehrend)
=> TaskHinweise.WachsenderKontext(new TaskItem
{
Assignee = assignee,
When = wiederkehrend ? Cron("5 * * * *") : null
}).ShouldBeNull();
[Fact]
public void Kein_Hinweis_fuer_Werkzeugjobs()
=> TaskHinweise.WachsenderKontext(new TaskItem
{
Assignee = "@telegram", Type = TaskItemType.ToolJob, When = Cron("* * * * *")
}).ShouldBeNull();
}