package tools import ( "context" "encoding/json" "strings" "testing" "github.com/krow/krow-backend/go-api/internal/authctx" "github.com/krow/krow-backend/go-api/internal/memory" ) type recordingStore struct { writes []memory.Write err error } func (r *recordingStore) Remember(_ context.Context, _ authctx.Identity, w memory.Write) (string, error) { if r.err != nil { return "", r.err } r.writes = append(r.writes, w) return "mem-1", nil } func rememberCtx() Context { return Context{ Principal: authctx.Identity{UserID: "u1", OrgID: "o1", Role: "admin"}, RunID: "run_abc", } } // I4. A memory stores personal data that shapes later hiring answers, so it // goes through the same gate as any other write — and the registry is what // enforces that, not this tool's good intentions. func TestRememberIsAConfirmedWrite(t *testing.T) { tool := Remember(&recordingStore{}) if tool.Effect != EffectWrite { t.Errorf("Effect = %q, want write", tool.Effect) } r := NewRegistry() r.MustRegister(tool) registered := r.Catalogue() var found bool for _, info := range registered { if info.Name == "remember" { found = true if !info.RequiresConfirmation { t.Error("remember was registered without a confirmation gate") } } } if !found { t.Fatal("remember did not register") } } // A model may not claim a person wrote something. The distinction is what // keeps "the agent inferred X" from being read back later as "X". func TestARememberedMemoryIsAlwaysAttributedToTheModel(t *testing.T) { store := &recordingStore{} tool := Remember(store) res := tool.Handler(context.Background(), rememberCtx(), json.RawMessage(`{"text":"This venue staffs on Thursdays.","subject":"workspace"}`)) if res.Error != nil { t.Fatalf("the write failed: %+v", res.Error) } if len(store.writes) != 1 { t.Fatalf("got %d writes, want 1", len(store.writes)) } if store.writes[0].Author != memory.AuthorModel { t.Errorf("Author = %q, want model", store.writes[0].Author) } } // Without the run id, a memory that shaped an answer cannot be traced to where // it came from, and "why did it say that" stops being answerable. func TestARememberedMemoryCarriesItsRun(t *testing.T) { store := &recordingStore{} Remember(store).Handler(context.Background(), rememberCtx(), json.RawMessage(`{"text":"Thursdays are short-staffed.","subject":"workspace"}`)) if len(store.writes) == 0 || store.writes[0].SourceRunID != "run_abc" { t.Error("the memory does not name the run that wrote it") } } func TestAnInventedSubjectIsRefused(t *testing.T) { store := &recordingStore{} res := Remember(store).Handler(context.Background(), rememberCtx(), json.RawMessage(`{"text":"x","subject":"everything"}`)) if res.Error == nil { t.Error("an invented subject was accepted") } if len(store.writes) != 0 { t.Error("a refused memory still reached the store") } } func TestAMemoryWithNoWordsIsRefused(t *testing.T) { store := &recordingStore{} res := Remember(store).Handler(context.Background(), rememberCtx(), json.RawMessage(`{"text":" ","subject":"workspace"}`)) if res.Error == nil || len(store.writes) != 0 { t.Error("an empty memory was accepted") } } /* ── What a person is shown before agreeing ──────────────────────────────── */ // The two questions somebody needs answered before keeping a memory are // "about whom" and "who decided this". func TestTheConfirmationSaysWhatIsKeptAndWhoDecided(t *testing.T) { c, bad := Remember(&recordingStore{}).Confirm(context.Background(), rememberCtx(), json.RawMessage(`{"text":"Prefers Bay Area venues.","subject":"user","subject_id":"u9"}`)) if bad != nil { t.Fatalf("the confirmation was refused: %+v", bad) } flat := c.Summary for _, d := range c.Details { flat += " " + d.Label + "=" + d.Value } for _, want := range []string{"Prefers Bay Area venues.", "user u9", "an agent, not a person", "90 days"} { if !strings.Contains(flat, want) { t.Errorf("the card does not state %q:\n%s", want, flat) } } } // A personal memory is flagged as such, because the thing being agreed to is // different in kind from remembering an opening time. func TestAPersonalMemoryWarnsAndAWorkspaceFactDoesNot(t *testing.T) { tool := Remember(&recordingStore{}) personal, _ := tool.Confirm(context.Background(), rememberCtx(), json.RawMessage(`{"text":"Was late twice.","subject":"candidate","subject_id":"c1"}`)) if len(personal.Warnings) == 0 || !strings.Contains(strings.Join(personal.Warnings, " "), "personal data") { t.Errorf("a memory about a person carries no warning: %+v", personal.Warnings) } if !strings.Contains(strings.Join(personal.Warnings, " "), "erased") { t.Error("the warning does not say the memory can be erased") } operational, _ := tool.Confirm(context.Background(), rememberCtx(), json.RawMessage(`{"text":"Thursdays are short-staffed.","subject":"workspace"}`)) if len(operational.Warnings) != 0 { t.Errorf("an operational fact was warned about: %+v", operational.Warnings) } }