Files
nebula/src/lib/tasks/issues.test.ts
T
florian.wildeandClaude Opus 5 e1d435f10a feat(tasks): Aufgabe als Git-Issue anlegen, Zustand wandert mit
Die Zuordnung stand im Schema laengst – sourceIssue, sourceUrl und
sourceRepositoryId samt Eindeutigkeit je Repository, gefuellt von der
Uebernahme aus Issues seit v0.9.0. Es fehlte die Gegenrichtung.

Ein Knopf im Aufgabendialog legt das Issue an; Nummer und Adresse landen in
denselben Feldern, der Dublettenschutz gilt also fuer beide Wege. In den
Rumpf kommt ein Rueckverweis auf das Projekt.

Ausdruecklich per Knopf und nie nebenbei: was in ein fremdes System
geschrieben wird, soll jemand gewollt haben. Ist die Aufgabe erst
verknuepft, wandert der Zustand allerdings mit – erledigt schliesst das
Issue, ein Zurueckziehen oeffnet es wieder. Fehlertolerant: ein nicht
erreichbares Git-System darf das Abhaken nicht verhindern.

Die Zustaende passen nicht aufeinander, und das bleibt so. Nebula kennt
vier, ein Issue zwei; nach aussen geht nur die Grenze um ERLEDIGT. Ein
Rundlauf verliert damit IN ARBEIT und BLOCKIERT. Labels waeren die
Alternative, wuerden aber den Issue-Tracker mit Zustaenden fuellen, die
dort niemand pflegt.

Kein Widerspruch zu "keine eigenen Git-Operationen": Issues laufen ueber
die HTTP-API der Provider. Dafuer gibt es jetzt sendJson neben getJson –
durch dieselbe SSRF-Schleuse, aber ohne Umleitungen zu folgen. Ein 307 auf
eine interne Adresse naehme den Rumpf und den Token im Header mit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 00:35:11 +02:00

44 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import { issueShouldBeOpen, statusForIssueState } from "./issues";
describe("issueShouldBeOpen", () => {
it("haelt das Issue offen, solange die Aufgabe nicht erledigt ist", () => {
expect(issueShouldBeOpen("TODO")).toBe(true);
expect(issueShouldBeOpen("DOING")).toBe(true);
expect(issueShouldBeOpen("BLOCKED")).toBe(true);
});
it("schliesst es erst bei ERLEDIGT", () => {
expect(issueShouldBeOpen("DONE")).toBe(false);
});
});
describe("statusForIssueState", () => {
it("macht aus einem geschlossenen Issue eine erledigte Aufgabe", () => {
expect(statusForIssueState(false, "TODO")).toBe("DONE");
expect(statusForIssueState(false, "DOING")).toBe("DONE");
});
it("holt eine erledigte Aufgabe zurueck nach OFFEN", () => {
expect(statusForIssueState(true, "DONE")).toBe("TODO");
});
it("laesst den feineren Zustand stehen, wenn das Issue ohnehin offen ist", () => {
// Ein "opened"-Ereignis zu einer Aufgabe, die schon in Arbeit ist, darf
// sie nicht auf OFFEN zuruecksetzen – IN ARBEIT ist mehr Information.
expect(statusForIssueState(true, "DOING")).toBe("DOING");
expect(statusForIssueState(true, "BLOCKED")).toBe("BLOCKED");
expect(statusForIssueState(true, "TODO")).toBe("TODO");
});
it("bildet den Rundlauf ab, so verlustbehaftet wie er ist", () => {
// IN ARBEIT geht nach draussen als "offen". Kommt es zurueck, bleibt es
// IN ARBEIT – der Zustand ueberlebt also, solange niemand das Issue
// schliesst. Nach dem Schliessen und Wiederoeffnen ist er weg, und das
// ist die bewusst hingenommene Grenze.
const hin = issueShouldBeOpen("DOING");
expect(statusForIssueState(hin, "DOING")).toBe("DOING");
expect(statusForIssueState(true, statusForIssueState(false, "DOING"))).toBe("TODO");
});
});