chore: directory migration - gateway→server, web→client/electron

This commit is contained in:
2026-09-08 19:16:35 +08:00
parent fd9f99a3f9
commit f9d757b5e5
243 changed files with 5095 additions and 228 deletions

View File

@ -0,0 +1,269 @@
package repo
import (
"context"
"testing"
)
// 这一组测试钉住「省略 session 位复用默认会话」与「真的新建会话」必须可区分。
//
// 事故背景handler 层曾用 `parentMailID == nil` 判断「是不是新建会话」,
// 据此决定要不要写往返预算与权限档位。但省略 session 位复用默认会话时
// parentMailID 也是 nil —— 于是每一封续谈的信都会把这两个字段重置成默认值。
//
// 线上实测(修复前):
//
// 第一封 to=pi@/tmp/budgetprobe max_rounds=7 → budget_max 7
// 第二封 to=pi@/tmp/budgetprobe省略该字段 → budget_max 20 ← 被静默改写
//
// 而那段代码的注释本身正在论证这不该发生(「续谈已有会话若也接受这个字段,
// 每封新信都会悄悄改掉对方正在遵守的预算」)—— 意图是对的,守卫写错了。
//
// 修法FindOrCreateDefaultSessionCreated 额外返回 created
// handler 改用它而不是 parentMailID。
func TestDefaultSessionFirstCallCreates(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
id, created, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "首封")
if err != nil {
t.Fatal(err)
}
if !created {
t.Fatal("从未通信过的 name@path第一次必须报告 created=true")
}
if id.String() == "" {
t.Fatal("应返回有效会话 id")
}
}
func TestDefaultSessionReuseReportsNotCreated(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
first, created, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "首封")
if err != nil || !created {
t.Fatalf("首封应新建created=%v err=%v", created, err)
}
// 复用的前提是这条会话里有该收件人参与过的邮件EXISTS 子查询)
if _, err := CreateMail(ctx, first, nil, "jianf", "", "pi", "/w", "首封", "x", nil); err != nil {
t.Fatal(err)
}
second, created2, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "第二封")
if err != nil {
t.Fatal(err)
}
if second != first {
t.Fatalf("第二封应复用同一条默认会话first=%s second=%s", first, second)
}
if created2 {
t.Fatal("复用已有默认会话时 created 必须为 false —— 这正是预算被冲掉的根因")
}
}
// 这条是上面那个线上事故的最小复现:走 created 判据时预算不被改写。
func TestBudgetSurvivesDefaultSessionReuse(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
id, created, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "首封")
if err != nil || !created {
t.Fatalf("首封应新建created=%v err=%v", created, err)
}
// 模拟 handler只有 created 为真才设预算
if _, err := SetSessionBudget(ctx, id, 7); err != nil {
t.Fatal(err)
}
if _, err := CreateMail(ctx, id, nil, "jianf", "", "pi", "/w", "首封", "x", nil); err != nil {
t.Fatal(err)
}
_, created2, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "第二封")
if err != nil {
t.Fatal(err)
}
if created2 {
// 若这里为真handler 就会重设预算 —— 事故重现
t.Fatal("复用时 created 为真会让 handler 重设预算")
}
b, err := GetSessionBudget(ctx, id)
if err != nil {
t.Fatal(err)
}
if b.Max != 7 {
t.Fatalf("续谈不得改写预算want 7, got %d", b.Max)
}
}
// 档位与预算同一个判据一起钉住plan 档不能因为第二封信而升成 workspace。
func TestPermissionModeSurvivesDefaultSessionReuse(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
id, created, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "首封")
if err != nil || !created {
t.Fatalf("首封应新建created=%v err=%v", created, err)
}
if _, err := SetSessionPermissionMode(ctx, id, "plan"); err != nil {
t.Fatal(err)
}
if _, err := CreateMail(ctx, id, nil, "jianf", "", "pi", "/w", "首封", "x", nil); err != nil {
t.Fatal(err)
}
_, created2, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "第二封")
if err != nil {
t.Fatal(err)
}
if created2 {
t.Fatal("复用时 created 为真会让 handler 把档位重置成默认档")
}
if got := SessionPermissionMode(ctx, id); got != "plan" {
t.Fatalf("续谈不得改写档位want plan, got %s", got)
}
}
// 不同工作目录是不同的默认会话,两边各自新建。
// 这条防的是「把 created 实现成一个全局标志」之类的偷懒写法。
func TestDefaultSessionPerWorkspace(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
a, createdA, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w1", "jianf", "甲")
if err != nil || !createdA {
t.Fatalf("/w1 应新建:%v %v", createdA, err)
}
if _, err := CreateMail(ctx, a, nil, "jianf", "", "pi", "/w1", "甲", "x", nil); err != nil {
t.Fatal(err)
}
b, createdB, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w2", "jianf", "乙")
if err != nil {
t.Fatal(err)
}
if !createdB {
t.Fatal("/w2 是另一个工作目录,应当另建一条默认会话")
}
if a == b {
t.Fatal("不同工作目录不该共用同一条默认会话")
}
}
// 旧签名仍在别处被调用,保持行为不变(只是丢掉 created
func TestLegacyWrapperStillWorks(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
id, err := FindOrCreateDefaultSession(ctx, "pi", "/w", "jianf", "首封")
if err != nil {
t.Fatal(err)
}
if _, err := CreateMail(ctx, id, nil, "jianf", "", "pi", "/w", "首封", "x", nil); err != nil {
t.Fatal(err)
}
again, err := FindOrCreateDefaultSession(ctx, "pi", "/w", "jianf", "第二封")
if err != nil {
t.Fatal(err)
}
if again != id {
t.Fatalf("包装函数应与原行为一致:%s vs %s", id, again)
}
}
// ─── 预算不被冲的回归用例P0 第一项P3 钉死) ───
//
// 守卫原本是 `parentMailID == nil`(表示新建),但省略 session 位复用默认会话时
// parentMailID 也是 nil —— 于是「仅在新建时生效」的字段(预算、档位)在每封
// 省略 session 位的信上都被重写了。实测:第一封 max_rounds=7 → 第二封省略该
// 字段 → 预算被静默改成默认的 20。
//
// 修法handler 改用 FindOrCreateDefaultSessionCreated 返回的 `created` 判据。
// 本测试钉死「复用默认会话时 created=false」这一事实让守卫不会倒退回去。
func TestDefaultSessionReuseBudgetNotReset(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
first, created, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "首封")
if err != nil || !created {
t.Fatalf("首封应新建created=%v err=%v", created, err)
}
// 模拟发信路径:新建会话时定预算为 7低于默认 20
if _, err := SetSessionBudget(ctx, first, 7); err != nil {
t.Fatal(err)
}
// 复用的前提是会话里有该收件人参与过的邮件
if _, err := CreateMail(ctx, first, nil, "jianf", "", "pi", "/w", "首封", "x", nil); err != nil {
t.Fatal(err)
}
// 第二封省略 session 位 → 复用默认会话created 必须为 false
second, created2, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "第二封")
if err != nil {
t.Fatal(err)
}
if second != first {
t.Fatalf("第二封应复用同一会话:%s vs %s", first, second)
}
if created2 {
t.Fatal("复用默认会话时 created 必须为 false否则预算会被默认值冲掉")
}
// 既然 created2=false发信路径不会调 SetSessionBudget → 预算仍为 7
b, err := GetSessionBudget(ctx, first)
if err != nil {
t.Fatal(err)
}
if b.Max != 7 {
t.Errorf("预算被冲掉got max=%dwant 7复用不应重设", b.Max)
}
}
// ─── 档位同样不被冲的回归用例 ───
//
// 与预算同理:复用默认会话时档位也不该被重设成默认档。
// 人指定 plan 档后,第二封信省略 session 位复用同一条会话 →
// created=false → SetSessionPermissionMode 不被调 → 档位仍为 plan。
func TestDefaultSessionReusePermissionModeNotReset(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
seedAgent(t, "pi", 20)
first, created, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "首封")
if err != nil || !created {
t.Fatalf("首封应新建created=%v err=%v", created, err)
}
// 新建会话时定档位为 plan比默认 workspace 更严)
if _, err := SetSessionPermissionMode(ctx, first, "plan"); err != nil {
t.Fatal(err)
}
if _, err := CreateMail(ctx, first, nil, "jianf", "", "pi", "/w", "首封", "x", nil); err != nil {
t.Fatal(err)
}
second, created2, err := FindOrCreateDefaultSessionCreated(ctx, "pi", "/w", "jianf", "第二封")
if err != nil {
t.Fatal(err)
}
if second != first {
t.Fatalf("第二封应复用同一会话:%s vs %s", first, second)
}
if created2 {
t.Fatal("复用默认会话时 created 必须为 false否则档位会被默认值冲掉")
}
mode := SessionPermissionMode(ctx, first)
if mode != "plan" {
t.Errorf("档位被冲掉got %qwant plan复用不应重设", mode)
}
}