package memory import ( "context" "encoding/json" "strings" "testing" "time" ) // ---- N3 WP5:记忆端口 ---- type fakeStore struct { entries []Entry appended []Entry recallQ RecallQuery err error } func (f *fakeStore) Recall(_ context.Context, q RecallQuery) ([]Entry, error) { f.recallQ = q return f.entries, f.err } func (f *fakeStore) Append(_ context.Context, e Entry) error { f.appended = append(f.appended, e) return nil } func TestBlockRenderOrderingAndCaps(t *testing.T) { future := time.Now().Add(24 * time.Hour) past := time.Now().Add(-24 * time.Hour) entries := []Entry{ {Type: "preference", Content: "喜欢简洁回复"}, {Type: "fact", Content: "项目用 Go 1.27"}, {Type: "fact", Content: " "}, {Type: "fact", Content: "过期了", ExpiresAt: &past}, {Type: "fact", Content: "没过期", ExpiresAt: &future}, } out := Block(entries, time.Now()) if !strings.Contains(out, "[记忆]\n") { t.Fatalf("缺块头:%q", out) } // 确定性排序:Type 字典序(fact 先于 preference);过期与空内容剔除。 firstFact := strings.Index(out, "fact") firstPref := strings.Index(out, "preference") if firstFact < 0 || firstPref < 0 || firstFact > firstPref { t.Fatalf("排序不确定:%q", out) } if strings.Contains(out, "过期了") { t.Fatalf("过期条目未剔除:%q", out) } if !strings.Contains(out, "没过期") { t.Fatalf("未过期条目被误剔:%q", out) } if Block(nil, time.Now()) != "" { t.Fatal("空结果应为空串") } } func TestWriteToolValidation(t *testing.T) { store := &fakeStore{} tool := &writeTool{store: store} cases := []struct { name string args string wantErr string }{ {"坏 scope", `{"scope":"global","type":"fact","content":"x"}`, "user 或 project"}, {"坏 type", `{"type":"todo","content":"x"}`, "fact|preference"}, {"空 content", `{"type":"fact","content":" "}`, "非空"}, {"超长 content", `{"type":"fact","content":"` + strings.Repeat("长", 2001) + `"}`, "上限"}, } for _, tc := range cases { res, err := tool.Execute(context.Background(), json.RawMessage(tc.args)) if err != nil || !res.IsError || !strings.Contains(res.Content, tc.wantErr) { t.Fatalf("%s:%+v %v", tc.name, res, err) } } if len(store.appended) != 0 { t.Fatalf("校验失败不得落库:%+v", store.appended) } // 合法路径:默认 scope=user + 过期换算。 res, err := tool.Execute(context.Background(), json.RawMessage(`{"type":"preference","content":"喜欢简洁","expires_in_days":30}`)) if err != nil || res.IsError { t.Fatalf("合法写入被拒:%+v %v", res, err) } if len(store.appended) != 1 || store.appended[0].Scope != "user" || store.appended[0].Type != "preference" || store.appended[0].ExpiresAt == nil { t.Fatalf("写入形态:%+v", store.appended) } }