From d53b20ecd72058b1baf76eccdfe5c8b1a7f54e97 Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 14 Oct 2026 14:52:05 +0000 Subject: [PATCH] Verify disabled process replay across independent stores Why: The previous endpoint regression used an SQLite-only server fixture even when isolated PostgreSQL was configured. Evidence: The revised HTTP test passed with dialect=sqlite and dialect=postgres; it creates data through one Store and reads through another before and after disabling the local account. Impact: U3 effective-state checks for the new process replay route now have a direct two-store PostgreSQL regression without changing production behavior. Refs: IAM U3 affected Stage 7 review; Backend PR #198 --- internal/server/run_process_events_test.go | 46 +++++++++++++++++----- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/internal/server/run_process_events_test.go b/internal/server/run_process_events_test.go index 62de1a5..c879d6d 100644 --- a/internal/server/run_process_events_test.go +++ b/internal/server/run_process_events_test.go @@ -80,25 +80,51 @@ func TestRunProcessEventsAreOrderedAndBoundToConversation(t *testing.T) { } func TestRunProcessEventsRejectsDisabledImplicitLocal(t *testing.T) { - srv, st := newTestServer(t) - if err := st.CreateAccount("admin", "", "", true); err != nil { + first, second := twinRecoveryStores(t) + t.Logf("process-event replay dialect=%s independent stores", first.Dialect()) + if err := first.CreateAccount("admin", "", "", true); err != nil { t.Fatal(err) } - h := srv.Handler() - _, convID := setupConversation(t, h) - runID := postMessage(t, h, convID, "整理材料") - waitRunTerminal(t, h, runID, 5*time.Second) - if err := st.EnsureUser(store.OwnerDefault, ""); err != nil { + if err := first.EnsureUser(store.OwnerDefault, ""); err != nil { t.Fatal(err) } - account, err := st.GetAccountByOwner(store.OwnerDefault) + project, err := first.CreateProject(store.OwnerDefault, "Project") if err != nil { t.Fatal(err) } - if _, err := st.SetAccountEnabled("admin", account.ID, account.StateVersion, false); err != nil { + conversation, err := first.CreateConversation(store.OwnerDefault, project.ID, "Process", "") + if err != nil { t.Fatal(err) } - code, _ := doJSON(t, h, http.MethodGet, "/api/conversations/"+convID+"/runs/"+runID+"/events", nil) + run := core.Run{ID: "run_local_process", ConversationID: conversation.ID, ProjectID: project.ID, + Owner: store.OwnerDefault, Kind: core.RunConversation, Status: core.RunCompleted} + if err := first.CreateRun(&run); err != nil { + t.Fatal(err) + } + if _, err := first.AppendEvents(conversation.ID, []core.StreamEvent{{RunID: run.ID, Type: core.EvThinkingDelta, + Payload: json.RawMessage(`{"index":0,"text":"private process"}`)}}); err != nil { + t.Fatal(err) + } + srv := newAuthTestServerOnStore(t, second, t.TempDir()) + h := srv.Handler() + path := "/api/conversations/" + conversation.ID + "/runs/" + run.ID + "/events" + code, _ := doJSON(t, h, http.MethodGet, path, nil) + if code != http.StatusUnauthorized { + t.Fatalf("auth-on replay without credential: %d", code) + } + srv.Auth.Enabled = false + code, _ = doJSON(t, h, http.MethodGet, path, nil) + if code != http.StatusOK { + t.Fatalf("enabled local account could not read process events: %d", code) + } + account, err := first.GetAccountByOwner(store.OwnerDefault) + if err != nil { + t.Fatal(err) + } + if _, err := first.SetAccountEnabled("admin", account.ID, account.StateVersion, false); err != nil { + t.Fatal(err) + } + code, _ = doJSON(t, h, http.MethodGet, path, nil) if code != http.StatusForbidden { t.Fatalf("disabled local account read process events: %d", code) }