CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (push) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (push) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
Neue additive status-Spalte (open/in_progress/done) auf tasks,
status.go (SetStatus/ByStatus/GetStatus/ListAll) haelt completed_at
synchron fuer TSK-03s escalation.Store. Neuer HTTP-Layer taskapi
(GET /tasks, PATCH /tasks/{id}/status), Tenant-Kontext-Durchsetzung
wie backchannelapi (VOR-06). Neue Next.js-Oberflaeche
web/workflow-tasks (Liste + Kanban, Drag&Drop + Tastaturbedienung).
tasks-a11y-fixture fuer den echten Browser-Test, kein Produktions-
binary vorhanden das geaendert werden musste.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
149 lines
4.1 KiB
Go
149 lines
4.1 KiB
Go
package task
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
// newTestStoreForStatus reuses newTestStore (task_test.go) so status.go's
|
|
// tests run against the same freshly migrated, isolated schema.
|
|
func newTestStoreForStatus(t *testing.T) *Store {
|
|
return newTestStore(t)
|
|
}
|
|
|
|
func TestSetStatus_ValidTransition(t *testing.T) {
|
|
s := newTestStoreForStatus(t)
|
|
ctx := context.Background()
|
|
|
|
id, err := s.Create(ctx, Task{Title: "Statustest", AssigneeType: AssigneePerson, AssigneeID: "u1"})
|
|
if err != nil {
|
|
t.Fatalf("create: %v", err)
|
|
}
|
|
|
|
got, err := s.Get(ctx, id)
|
|
if err != nil {
|
|
t.Fatalf("get: %v", err)
|
|
}
|
|
if got.Status != StatusOpen {
|
|
t.Fatalf("neue aufgabe sollte status open haben, war %q", got.Status)
|
|
}
|
|
|
|
if err := s.SetStatus(ctx, id, StatusInProgress); err != nil {
|
|
t.Fatalf("setstatus in_progress: %v", err)
|
|
}
|
|
got, _ = s.Get(ctx, id)
|
|
if got.Status != StatusInProgress {
|
|
t.Fatalf("erwartet in_progress, war %q", got.Status)
|
|
}
|
|
}
|
|
|
|
func TestSetStatus_DoneSetsCompletedAt(t *testing.T) {
|
|
s := newTestStoreForStatus(t)
|
|
ctx := context.Background()
|
|
|
|
id, err := s.Create(ctx, Task{Title: "Erledigt-Test", AssigneeType: AssigneePerson, AssigneeID: "u1"})
|
|
if err != nil {
|
|
t.Fatalf("create: %v", err)
|
|
}
|
|
|
|
if err := s.SetStatus(ctx, id, StatusDone); err != nil {
|
|
t.Fatalf("setstatus done: %v", err)
|
|
}
|
|
|
|
var completedAt *string
|
|
if err := s.pool.QueryRow(ctx, `SELECT completed_at::text FROM tasks WHERE id = $1`, id).Scan(&completedAt); err != nil {
|
|
t.Fatalf("completed_at lesen: %v", err)
|
|
}
|
|
if completedAt == nil {
|
|
t.Fatalf("completed_at sollte nach status done gesetzt sein")
|
|
}
|
|
|
|
// Zurueck auf open: completed_at muss wieder NULL werden, damit
|
|
// escalation.Store (TSK-03) die Aufgabe erneut als offen behandelt.
|
|
if err := s.SetStatus(ctx, id, StatusOpen); err != nil {
|
|
t.Fatalf("setstatus zurueck auf open: %v", err)
|
|
}
|
|
if err := s.pool.QueryRow(ctx, `SELECT completed_at::text FROM tasks WHERE id = $1`, id).Scan(&completedAt); err != nil {
|
|
t.Fatalf("completed_at erneut lesen: %v", err)
|
|
}
|
|
if completedAt != nil {
|
|
t.Fatalf("completed_at sollte nach rueckwechsel auf open wieder NULL sein, war %v", *completedAt)
|
|
}
|
|
}
|
|
|
|
func TestSetStatus_InvalidStatusRejected(t *testing.T) {
|
|
s := newTestStoreForStatus(t)
|
|
ctx := context.Background()
|
|
|
|
id, err := s.Create(ctx, Task{Title: "Ungueltig-Test", AssigneeType: AssigneePerson, AssigneeID: "u1"})
|
|
if err != nil {
|
|
t.Fatalf("create: %v", err)
|
|
}
|
|
if err := s.SetStatus(ctx, id, Status("erfunden")); err == nil {
|
|
t.Fatal("erwartet fehler bei unbekanntem status")
|
|
}
|
|
}
|
|
|
|
func TestSetStatus_UnknownTaskNotFound(t *testing.T) {
|
|
s := newTestStoreForStatus(t)
|
|
ctx := context.Background()
|
|
if err := s.SetStatus(ctx, "00000000-0000-0000-0000-000000000000", StatusDone); err != ErrNotFound {
|
|
t.Fatalf("erwartet ErrNotFound, bekam %v", err)
|
|
}
|
|
}
|
|
|
|
func TestByStatus_FiltersCorrectly(t *testing.T) {
|
|
s := newTestStoreForStatus(t)
|
|
ctx := context.Background()
|
|
|
|
idOpen, _ := s.Create(ctx, Task{Title: "Offen", AssigneeType: AssigneePerson, AssigneeID: "u2"})
|
|
idDone, _ := s.Create(ctx, Task{Title: "Fertig", AssigneeType: AssigneePerson, AssigneeID: "u2"})
|
|
if err := s.SetStatus(ctx, idDone, StatusDone); err != nil {
|
|
t.Fatalf("setstatus: %v", err)
|
|
}
|
|
|
|
doneTasks, err := s.ByStatus(ctx, StatusDone)
|
|
if err != nil {
|
|
t.Fatalf("bystatus: %v", err)
|
|
}
|
|
found := false
|
|
for _, tk := range doneTasks {
|
|
if tk.ID == idDone {
|
|
found = true
|
|
}
|
|
if tk.ID == idOpen {
|
|
t.Fatal("offene aufgabe sollte nicht im done-filter erscheinen")
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("erledigte aufgabe fehlt im done-filter")
|
|
}
|
|
}
|
|
|
|
func TestListAll_ReturnsTasksWithStatus(t *testing.T) {
|
|
s := newTestStoreForStatus(t)
|
|
ctx := context.Background()
|
|
|
|
id, err := s.Create(ctx, Task{Title: "Liste-Test", AssigneeType: AssigneePerson, AssigneeID: "u3"})
|
|
if err != nil {
|
|
t.Fatalf("create: %v", err)
|
|
}
|
|
|
|
all, err := s.ListAll(ctx)
|
|
if err != nil {
|
|
t.Fatalf("listall: %v", err)
|
|
}
|
|
found := false
|
|
for _, tk := range all {
|
|
if tk.ID == id {
|
|
found = true
|
|
if tk.Status != StatusOpen {
|
|
t.Fatalf("erwartet status open, war %q", tk.Status)
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("neu angelegte aufgabe fehlt in ListAll")
|
|
}
|
|
}
|