fix(review round 1): daemon PAT-header auth, job pruning, session-split batches, streaming tars, seed cleanup, ping/pong, byte caps, branch switch, slug --, ctx cancel, Init:true, pagination, latest/before events; web newest-window history + load-older, offline busy gate, staleness guards, memo store, auth probe, scroll key, focus-visible, draft restore, poll dedup; 106+181 tests, coverage 96.2%/95.1%+

This commit is contained in:
Raphael Westphal
2026-08-18 18:49:52 +02:00
parent 64e45e1a82
commit 6aac763563
25 changed files with 2564 additions and 959 deletions
+47 -2
View File
@@ -4,6 +4,7 @@ package main
// serving, body/token validation edges.
import (
"context"
"encoding/json"
"io"
"net/http"
@@ -37,7 +38,7 @@ func newSpawnAPIServer(t *testing.T) (*httptest.Server, *fakeDocker) {
daemonToken = testToken
store := openTestStore(t)
hub := NewHub(store)
sp, err := NewSpawner(store, hub, "https://gitlab.example")
sp, err := NewSpawner(context.Background(), store, hub, "https://gitlab.example")
if err != nil {
t.Fatalf("NewSpawner: %v", err)
}
@@ -139,7 +140,7 @@ func TestAPISpawnFailures(t *testing.T) {
daemonToken = testToken
store := openTestStore(t)
hub := NewHub(store)
sp, err := NewSpawner(store, hub, "https://gitlab.example")
sp, err := NewSpawner(context.Background(), store, hub, "https://gitlab.example")
if err != nil {
t.Fatalf("NewSpawner: %v", err)
}
@@ -279,6 +280,50 @@ func TestAPIPromptBodyValidation(t *testing.T) {
}
}
func TestAPIEventsLatestAndBeforeParams(t *testing.T) {
ts, store := newTestServer(t)
auth := testToken
for seq := int64(1); seq <= 5; seq++ {
if err := store.AppendEvent(Event{SessionID: "s1", Seq: seq, TS: seq, Type: evAgentSettled, Payload: []byte(`{}`)}); err != nil {
t.Fatalf("append %d: %v", seq, err)
}
}
getSeqs := func(query string) []int64 {
t.Helper()
code, body := apiReq(t, http.MethodGet, ts.URL+"/api/sessions/s1/events"+query, auth, "")
if code != http.StatusOK {
t.Fatalf("%s → %d %s, want 200", query, code, body)
}
var frames []map[string]any
if err := json.Unmarshal([]byte(body), &frames); err != nil {
t.Fatalf("decode %s: %v", query, err)
}
seqs := make([]int64, 0, len(frames))
for _, f := range frames {
seqs = append(seqs, int64(f["seq"].(float64)))
}
return seqs
}
if got := getSeqs("?latest=1&limit=2"); len(got) != 2 || got[0] != 4 || got[1] != 5 {
t.Fatalf("latest=1&limit=2 = %v, want [4 5] ascending", got)
}
if got := getSeqs("?before=4&limit=2"); len(got) != 2 || got[0] != 2 || got[1] != 3 {
t.Fatalf("before=4&limit=2 = %v, want [2 3] ascending", got)
}
// default behavior unchanged
if got := getSeqs("?after=0&limit=10"); len(got) != 5 || got[0] != 1 {
t.Fatalf("after=0 = %v, want full ascending replay", got)
}
for _, q := range []string{"?before=abc", "?before=-1", "?latest=2"} {
if code, _ := apiReq(t, http.MethodGet, ts.URL+"/api/sessions/s1/events"+q, auth, ""); code != http.StatusBadRequest {
t.Fatalf("%s → %d, want 400", q, code)
}
}
}
func TestAPIEventsEdgeCases(t *testing.T) {
ts, store := newTestServer(t)
auth := testToken