// These tests inject lost acknowledgements and streams without snapshots. // A daemon E2E cannot schedule those cases deterministically. package daemon import ( "albedo/cli/internal/daemon/protocol" "encoding/json" "net/http" "strings" "sync/atomic" "testing" ) func TestACPSeededCursorNeedsNoSnapshot(t *testing.T) { conn := controlledConnection(t, func(w http.ResponseWriter, r *http.Request) { if r.URL.Query().Get("after_seq") != "41" || r.URL.Query().Get("after_generation") != generationA { t.Errorf("lost attachment cursor: %s", r.URL) } w.Header().Set("Content-Type", "text/event-stream") writeBatch(w, map[string]any{"generation": generationA, "cursor": 42, "events": []any{canonicalText(42, "future")}}) }) client := NewChatClient(conn, "s") client.SeedCursor(protocol.Cursor{Sequence: 41, Generation: generationA}) var text string if err := client.Stream(t.Context(), 0, func(event StreamEvent) error { if event.Type == EventReset { t.Error("unexpected snapshot") } text += event.Text return nil }); err != nil { t.Fatal(err) } if text != "future" { t.Fatal(text) } } func TestACPInputRecoversLostAckWithToken(t *testing.T) { var admissions, lookups atomic.Int32 conn := controlledConnection(t, func(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/sessions/s/inputs/") if r.Method == http.MethodPut { admissions.Add(1) if r.Header.Get("Albedo-Attachment-Token") != "private" { t.Error("attachment token missing") } var body protocol.ACPPromptInputRequest if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Kind != "acp_prompt" { t.Errorf("wrong admission: %+v %v", body, err) } connection, _, err := w.(http.Hijacker).Hijack() if err != nil { t.Error(err) return } _ = connection.Close() return } lookups.Add(1) _ = json.NewEncoder(w).Encode(canonicalInput("s", id, "acp_prompt", "pending")) }) handle, err := NewACPInput("s", "private", []json.RawMessage{json.RawMessage(`{"type":"text","text":"hello"}`)}) if err != nil { t.Fatal(err) } result, err := SubmitOperation(t.Context(), conn, handle) if err != nil { t.Fatal(err) } if result.OperationID != handle.ID() || admissions.Load() != 1 || lookups.Load() != 1 { t.Fatalf("identity lost or admission replayed: %+v %d %d", result, admissions.Load(), lookups.Load()) } }