docs(comments): 注释中文化 · internal/runtime/history(15 文件,纯替换)
- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩 4 条行尾注释的翻译,代码前缀全等) - 交叉引用(design/16 §4.6、D4/B3、0010 迁移文件名)逐字保留 - 保留未译技术词:DDL/rowid/LIKE/LEFT JOIN/DTO/rune/WS/actor 及全部标识符与表名 - 顺带副作用:store.go、session_persona_test.go 由注释文字改变而变得 gofmt 干净(未运行 gofmt -w)
This commit is contained in:
1 parent
b0fbb8bc0d
commit
09fba77f87
15 files changed
+430
-516
No files matched your search
@@ -11,12 +11,12 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/db"
|
||||
)
|
||||
|
||||
// testLogger returns a logger that writes nowhere, keeping test output clean.
|
||||
// testLogger 返回一个不写任何地方的日志器,保持测试输出干净。
|
||||
func testLogger() *slog.Logger {
|
||||
return slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
}
|
||||
|
||||
// newStore opens an isolated in-memory DB and returns a Store on it.
|
||||
// newStore 打开一个隔离的内存库并在其上返回 Store。
|
||||
func newStore(t *testing.T) *Store {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
@@ -58,11 +58,11 @@ func TestAppendAndListNewestFirst(t *testing.T) {
|
||||
if len(msgs) != 3 {
|
||||
t.Fatalf("len = %d, want 3", len(msgs))
|
||||
}
|
||||
// Newest first.
|
||||
// 最新优先。
|
||||
if msgs[0].CreatedAt != 3000 || msgs[1].CreatedAt != 2000 || msgs[2].CreatedAt != 1000 {
|
||||
t.Fatalf("order = %+v, want [3000 2000 1000]", []int64{msgs[0].CreatedAt, msgs[1].CreatedAt, msgs[2].CreatedAt})
|
||||
}
|
||||
// Id/timestamp assigned when the caller omits them.
|
||||
// 调用方省略时分配 id/时间戳。
|
||||
empty := Message{SessionID: MainSessionID, Role: RoleAssistant, Content: "ok"}
|
||||
if err := st.Append(ctx, empty); err != nil {
|
||||
t.Fatalf("append no ts: %v", err)
|
||||
@@ -95,7 +95,7 @@ func TestAppendFullReturnsID(t *testing.T) {
|
||||
t.Fatalf("AppendFull returned %+v, want the stored message unchanged plus id/created_at", full)
|
||||
}
|
||||
|
||||
// The message is actually persisted under the returned id.
|
||||
// 消息确实以返回的 id 落盘。
|
||||
msgs, err := st.List(ctx, MainSessionID, 0, 50, "")
|
||||
if err != nil {
|
||||
t.Fatalf("list: %v", err)
|
||||
@@ -104,7 +104,7 @@ func TestAppendFullReturnsID(t *testing.T) {
|
||||
t.Fatalf("list = %+v, want exactly the persisted message id %q", msgs, full.ID)
|
||||
}
|
||||
|
||||
// A caller-supplied id/timestamp is preserved, not regenerated.
|
||||
// 调用方传入的 id/时间戳被保留,而不是重新生成。
|
||||
preset, err := st.AppendFull(ctx, Message{ID: "preset-id", SessionID: MainSessionID, Role: RoleAssistant, Content: "ok", CreatedAt: 42})
|
||||
if err != nil {
|
||||
t.Fatalf("append full preset: %v", err)
|
||||
@@ -119,8 +119,7 @@ func TestAppendFullListPlatformRoundTrip(t *testing.T) {
|
||||
st := newStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// An IM owner-private message carries its channel platform through the round
|
||||
// trip: AppendFull → List.
|
||||
// 一条 IM 主人私聊消息在 AppendFull → List 往返中携带它的渠道平台。
|
||||
full, err := st.AppendFull(ctx, Message{SessionID: MainSessionID, Role: RoleUser, Content: "从微信来", Platform: "weixin-ilink"})
|
||||
if err != nil {
|
||||
t.Fatalf("append full: %v", err)
|
||||
@@ -136,7 +135,7 @@ func TestAppendFullListPlatformRoundTrip(t *testing.T) {
|
||||
t.Fatalf("list round trip = %+v, want platform weixin-ilink", msgs)
|
||||
}
|
||||
|
||||
// A message without an explicit platform defaults to the web lane.
|
||||
// 未显式指定平台的消息默认为 web 通道。
|
||||
web, err := st.AppendFull(ctx, Message{SessionID: MainSessionID, Role: RoleAssistant, Content: "ok"})
|
||||
if err != nil {
|
||||
t.Fatalf("append web default: %v", err)
|
||||
@@ -151,8 +150,8 @@ func TestAppendFullListDeviceIDRoundTrip(t *testing.T) {
|
||||
st := newStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// A Web message tagged with a browser deviceID survives the AppendFull → List
|
||||
// round trip (design/16 §4.5).
|
||||
// 一条带浏览器 deviceID 的 Web 消息活过 AppendFull → List 往返
|
||||
// (design/16 §4.5)。
|
||||
full, err := st.AppendFull(ctx, Message{SessionID: MainSessionID, Role: RoleUser, Content: "来自浏览器", DeviceID: "web:chrome:a1b2c3"})
|
||||
if err != nil {
|
||||
t.Fatalf("append full: %v", err)
|
||||
@@ -168,7 +167,7 @@ func TestAppendFullListDeviceIDRoundTrip(t *testing.T) {
|
||||
t.Fatalf("list round trip = %+v, want DeviceID web:chrome:a1b2c3", msgs)
|
||||
}
|
||||
|
||||
// Recent (WS replay / context tail) reads the same device_id column.
|
||||
// Recent(WS 回放 / 上下文尾部)读同一个 device_id 列。
|
||||
recent, err := st.Recent(ctx, MainSessionID, 5)
|
||||
if err != nil {
|
||||
t.Fatalf("recent: %v", err)
|
||||
@@ -177,7 +176,7 @@ func TestAppendFullListDeviceIDRoundTrip(t *testing.T) {
|
||||
t.Fatalf("recent = %+v, want DeviceID web:chrome:a1b2c3", recent)
|
||||
}
|
||||
|
||||
// A message without an explicit deviceID persists the column default ('').
|
||||
// 未显式指定 deviceID 的消息持久化列默认值(空)。
|
||||
om, err := st.AppendFull(ctx, Message{SessionID: MainSessionID, Role: RoleAssistant, Content: "ok"})
|
||||
if err != nil {
|
||||
t.Fatalf("append empty device: %v", err)
|
||||
@@ -192,7 +191,7 @@ func TestListPlatformFilter(t *testing.T) {
|
||||
st := newStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Seed across the web lane (explicit + default) and one IM lane.
|
||||
// 跨 web 通道(显式 + 默认)与一条 IM 通道播种。
|
||||
for _, m := range []Message{
|
||||
{SessionID: MainSessionID, Role: RoleUser, Content: "web1", Platform: PlatformWeb, CreatedAt: 1000},
|
||||
{SessionID: MainSessionID, Role: RoleUser, Content: "tg", Platform: "telegram", CreatedAt: 2000},
|
||||
@@ -203,7 +202,7 @@ func TestListPlatformFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// No platform filter → all three (the empty-platform message defaulted to web).
|
||||
// 不带平台过滤 → 三条全出(未指定平台的那条在插入时默认为 web)。
|
||||
all, err := st.List(ctx, MainSessionID, 0, 50, "")
|
||||
if err != nil {
|
||||
t.Fatalf("list all: %v", err)
|
||||
@@ -212,7 +211,7 @@ func TestListPlatformFilter(t *testing.T) {
|
||||
t.Fatalf("all = %d, want 3", len(all))
|
||||
}
|
||||
|
||||
// Filter to the IM lane → only its message.
|
||||
// 过滤到 IM 通道 → 只有它的消息。
|
||||
tg, err := st.List(ctx, MainSessionID, 0, 50, "telegram")
|
||||
if err != nil {
|
||||
t.Fatalf("list telegram: %v", err)
|
||||
@@ -221,8 +220,7 @@ func TestListPlatformFilter(t *testing.T) {
|
||||
t.Fatalf("telegram page = %+v, want [tg]", tg)
|
||||
}
|
||||
|
||||
// Filter to the web lane → web1 + web2 (the one left without an explicit
|
||||
// platform defaulted to web on insert).
|
||||
// 过滤到 web 通道 → web1 + web2(那条未显式指定平台的在插入时默认为 web)。
|
||||
web, err := st.List(ctx, MainSessionID, 0, 50, PlatformWeb)
|
||||
if err != nil {
|
||||
t.Fatalf("list web: %v", err)
|
||||
@@ -231,7 +229,7 @@ func TestListPlatformFilter(t *testing.T) {
|
||||
t.Fatalf("web page = %d, want 2 (%+v)", len(web), web)
|
||||
}
|
||||
|
||||
// The filter composes with the before cursor.
|
||||
// 过滤与 before 游标可组合。
|
||||
older, err := st.List(ctx, MainSessionID, 2000, 50, PlatformWeb)
|
||||
if err != nil {
|
||||
t.Fatalf("list web before: %v", err)
|
||||
@@ -252,7 +250,7 @@ func TestListPaginationBefore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// First page: newest 2.
|
||||
// 第一页:最新 2 条。
|
||||
page, err := st.List(ctx, MainSessionID, 0, 2, "")
|
||||
if err != nil {
|
||||
t.Fatalf("first page: %v", err)
|
||||
@@ -260,7 +258,7 @@ func TestListPaginationBefore(t *testing.T) {
|
||||
if len(page) != 2 || page[0].CreatedAt != 6000 || page[1].CreatedAt != 5000 {
|
||||
t.Fatalf("first page = %+v, want [6000 5000]", page)
|
||||
}
|
||||
// Cursor on the page's oldest (5000) → older page [4000 3000].
|
||||
// 游标落在该页最旧那条(5000)→ 更旧的一页 [4000 3000]。
|
||||
next, err := st.List(ctx, MainSessionID, page[1].CreatedAt, 2, "")
|
||||
if err != nil {
|
||||
t.Fatalf("next page: %v", err)
|
||||
@@ -268,7 +266,7 @@ func TestListPaginationBefore(t *testing.T) {
|
||||
if len(next) != 2 || next[0].CreatedAt != 4000 || next[1].CreatedAt != 3000 {
|
||||
t.Fatalf("next page = %+v, want [4000 3000]", next)
|
||||
}
|
||||
// Past the start → empty.
|
||||
// 越过起点 → 空。
|
||||
tail, err := st.List(ctx, MainSessionID, 1000, 50, "")
|
||||
if err != nil {
|
||||
t.Fatalf("tail: %v", err)
|
||||
@@ -302,7 +300,7 @@ func TestNewSubSessionAndListSessions(t *testing.T) {
|
||||
st := newStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Main session exists by default.
|
||||
// 主会话默认存在。
|
||||
sessions, err := st.ListSessions(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("list sessions initial: %v", err)
|
||||
@@ -362,7 +360,7 @@ func TestDeleteSession(t *testing.T) {
|
||||
t.Fatalf("sessions after delete = %+v, want [main]", sessions)
|
||||
}
|
||||
|
||||
// Main session is immutable.
|
||||
// 主会话不可变。
|
||||
if err := st.DeleteSession(ctx, MainSessionID); !errors.Is(err, ErrImmutableMain) {
|
||||
t.Fatalf("delete main err = %v, want ErrImmutableMain", err)
|
||||
}
|
||||
@@ -373,21 +371,21 @@ func TestSenderNameRoundTripOnListRecentAndPlatformLog(t *testing.T) {
|
||||
st := newStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// An isolated group session carries the sender display name (design/16 §4.6).
|
||||
// 隔离的群聊会话携带发送者显示名(design/16 §4.6)。
|
||||
groupID := "sess-g"
|
||||
if err := st.Append(ctx, Message{SessionID: groupID, Role: RoleUser, Content: "hello all", Platform: "telegram", SenderName: "张三", DeviceID: "telegram:-20:9", CreatedAt: 1000}); err != nil {
|
||||
t.Fatalf("append group msg: %v", err)
|
||||
}
|
||||
// An owner-private message on the main session carries no sender name.
|
||||
// 主会话上主人的私聊消息不携带发送者名。
|
||||
if err := st.Append(ctx, Message{SessionID: MainSessionID, Role: RoleUser, Content: "hi", Platform: "telegram", CreatedAt: 900}); err != nil {
|
||||
t.Fatalf("append main msg: %v", err)
|
||||
}
|
||||
// An unrelated platform on the main session should not leak into the log.
|
||||
// 主会话上一个无关平台不得泄进该日志。
|
||||
if err := st.Append(ctx, Message{SessionID: MainSessionID, Role: RoleUser, Content: "web msg", Platform: "web", CreatedAt: 800}); err != nil {
|
||||
t.Fatalf("append web msg: %v", err)
|
||||
}
|
||||
|
||||
// List reads the sender_name back.
|
||||
// List 读回 sender_name。
|
||||
ls, err := st.List(ctx, groupID, 0, 50, "")
|
||||
if err != nil {
|
||||
t.Fatalf("list: %v", err)
|
||||
@@ -396,7 +394,7 @@ func TestSenderNameRoundTripOnListRecentAndPlatformLog(t *testing.T) {
|
||||
t.Fatalf("list = %+v, want SenderName 张三", ls)
|
||||
}
|
||||
|
||||
// Recent reads it too.
|
||||
// Recent 也读它。
|
||||
rs, err := st.Recent(ctx, groupID, 5)
|
||||
if err != nil {
|
||||
t.Fatalf("recent: %v", err)
|
||||
@@ -405,8 +403,8 @@ func TestSenderNameRoundTripOnListRecentAndPlatformLog(t *testing.T) {
|
||||
t.Fatalf("recent = %+v, want SenderName 张三", rs)
|
||||
}
|
||||
|
||||
// Platform log surfaces only the isolated (non-main) messages of the platform,
|
||||
// newest first, excluding the owner-private mirror on the main session.
|
||||
// 平台日志只浮现该平台的隔离(非主)消息,最新优先,排除主会话上的主人
|
||||
// 私聊镜像。
|
||||
log, err := st.ListPlatformLog(ctx, "telegram", 0, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("platform log: %v", err)
|
||||
@@ -418,7 +416,7 @@ func TestSenderNameRoundTripOnListRecentAndPlatformLog(t *testing.T) {
|
||||
t.Fatalf("platform log[0] = %+v, want group session with SenderName 张三", log[0])
|
||||
}
|
||||
|
||||
// A platform with only main-session messages yields an empty log.
|
||||
// 只有主会话消息的平台得到空日志。
|
||||
if log, err := st.ListPlatformLog(ctx, "web", 0, 50); err != nil || len(log) != 0 {
|
||||
t.Fatalf("web platform log = %+v, err=%v; want empty", log, err)
|
||||
}
|
||||
@@ -429,8 +427,8 @@ func TestAdapterLogFiltersByAdapter(t *testing.T) {
|
||||
st := newStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Seed across two adapters, an empty-adapter web message, and a main-session
|
||||
// owner-private mirror that still carries the adapter (design/16 §4.4).
|
||||
// 跨两个适配器、一条空适配器的 web 消息,以及一条仍携带适配器的主会话
|
||||
// 主人私聊镜像播种(design/16 §4.4)。
|
||||
for _, m := range []Message{
|
||||
{SessionID: MainSessionID, Role: RoleUser, Content: "kook-owner", Platform: "kook", AdapterID: "kook:3bef", CreatedAt: 1000},
|
||||
{SessionID: "sess-g1", Role: RoleUser, Content: "kook-group", Platform: "kook", AdapterID: "kook:3bef", SenderName: "张三", CreatedAt: 2000},
|
||||
@@ -442,8 +440,7 @@ func TestAdapterLogFiltersByAdapter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Per-adapter log includes BOTH the owner-private main mirror and the group
|
||||
// message, newest first.
|
||||
// 按适配器日志同时包含主人私聊的主会话镜像与群聊消息,最新优先。
|
||||
kook, err := st.ListAdapterLog(ctx, "kook:3bef", 0, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("adapter log kook:3bef: %v", err)
|
||||
@@ -458,7 +455,7 @@ func TestAdapterLogFiltersByAdapter(t *testing.T) {
|
||||
t.Fatalf("kook[1] = %+v, want owner mirror on main session", kook[1])
|
||||
}
|
||||
|
||||
// A different adapter yields only its own lane.
|
||||
// 另一个适配器只得到它自己的通道。
|
||||
tg, err := st.ListAdapterLog(ctx, "telegram:conf0", 0, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("adapter log telegram:conf0: %v", err)
|
||||
@@ -467,7 +464,7 @@ func TestAdapterLogFiltersByAdapter(t *testing.T) {
|
||||
t.Fatalf("telegram adapter log = %+v, want [tg-group]", tg)
|
||||
}
|
||||
|
||||
// An unknown / empty adapter yields a non-nil empty slice.
|
||||
// 未知 / 空适配器得到非 nil 的空切片。
|
||||
none, err := st.ListAdapterLog(ctx, "kook:unknown", 0, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("adapter log unknown: %v", err)
|
||||
@@ -483,7 +480,7 @@ func TestAdapterLogFiltersByAdapter(t *testing.T) {
|
||||
t.Fatalf("empty adapter log = %#v, want non-nil empty slice", empty)
|
||||
}
|
||||
|
||||
// The before cursor pages backward through the adapter's stream.
|
||||
// before 游标在该适配器的消息流中向后翻页。
|
||||
older, err := st.ListAdapterLog(ctx, "kook:3bef", 2000, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("adapter log before: %v", err)
|
||||
@@ -508,7 +505,7 @@ func TestAllLogSurfacesOnlyAdapterMessages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The overview only surfaces IM adapter sources; the web message is excluded.
|
||||
// 总览只浮现 IM 适配器来源;web 消息被排除。
|
||||
all, err := st.ListAllLog(ctx, 0, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("all log: %v", err)
|
||||
@@ -519,14 +516,14 @@ func TestAllLogSurfacesOnlyAdapterMessages(t *testing.T) {
|
||||
if all[0].Content != "tg-group" || all[1].Content != "kook-owner" {
|
||||
t.Fatalf("all log order = %+v, want [tg-group kook-owner]", all)
|
||||
}
|
||||
// The web message never leaks into the adapter overview.
|
||||
// web 消息绝不泄进适配器总览。
|
||||
for _, m := range all {
|
||||
if m.Content == "web-msg" {
|
||||
t.Fatalf("web message leaked into adapter overview: %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
// An empty store yields a non-nil empty slice.
|
||||
// 空存储得到非 nil 的空切片。
|
||||
st2 := newStore(t)
|
||||
e, err := st2.ListAllLog(ctx, 0, 50)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user