From 653b0d18668a5db899fc8c645e0a2c2f1e15b0f8 Mon Sep 17 00:00:00 2001 From: Nixevol Date: Wed, 30 Sep 2026 07:44:57 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E5=AF=B9=E9=BD=90=20F06=20=E7=BE=A4?= =?UTF-8?q?=E5=8F=91=E4=B8=8E=20M2=20=E7=A6=BB=E7=BA=BF=E4=B8=8D=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=AE=8C=E6=88=90=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/DEVIATIONS.md | 7 +++++ internal/app/group/group_test.go | 47 ++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/docs/DEVIATIONS.md b/docs/DEVIATIONS.md index f29f66c..cc70333 100644 --- a/docs/DEVIATIONS.md +++ b/docs/DEVIATIONS.md @@ -492,6 +492,13 @@ - 备选方案:塞进 context。 - 影响:协议分发接线时从 `ConnInfo.RemoteIP` 传入。 +10. **F06 群发断言与 M2 分发语义对齐(2026-09-30)** + - 原条款:F06 验收「发到当时成员」;早期单测在仅写 `pending` 的桩分发下断言 Submit 返回 `dispatched`。 + - 实际做法:成员离线且默认不保留时,投递立即 `dropped`,消息为 `completed`(DEVELOPMENT 7.4/7.6);`TestF06GroupSendMembership` 改断言 `completed`;另加 `TestF06GroupSendKeepOfflinePending`:选离线保留时期望 `dispatched` 且接收者有 `pending`。不改消息分发实现。 + - 原因:合入含 M2 完整分发的 main 后,旧断言与产品规则冲突;永远 `dispatched` 才是错的。 + - 备选方案:测试里注入在线连接表使默认不保留也走 pending(与「离线不保留」场景重复覆盖)。 + - 影响:仅测试期望;产品行为不变。 + ## 后台接口 A ### A1 2026-09-30 diff --git a/internal/app/group/group_test.go b/internal/app/group/group_test.go index 44c2231..85eda56 100644 --- a/internal/app/group/group_test.go +++ b/internal/app/group/group_test.go @@ -58,7 +58,7 @@ func setup(t *testing.T) (*group.App, *identity.App, *message.App, *store.DB, *m DB: db, Talk: idApp, Downlink: down, MaxGroupMembers: 1000, Now: func() time.Time { return fixed }, DefaultRemoteIP: "1.1.1.1", }) - lim := message.LimitsFromConfig(config.Default().Limits) + lim := message.LimitsFromFullConfig(config.Default()) lim.RequestsPerSecond = 0 msgApp := message.New(db, lim, auth.NewStubHashPool(), message.WithNow(func() time.Time { return fixed }), @@ -325,6 +325,7 @@ func TestF06GroupSendMembership(t *testing.T) { if err != nil { t.Fatal(err) } + // 默认不保留 + 成员离线(无连接、无 offline_since):投递立即 dropped,消息 completed(DEVELOPMENT 7.4/7.6) res, err := msgApp.Submit(ctx, "alice", port.ConnInfo{}, &protocol.Send{ V: protocol.Version, Type: protocol.TypeSend, RID: "s", ID: "m1", To: protocol.Target{Kind: protocol.TargetGroup, ID: created.ID}, @@ -333,14 +334,20 @@ func TestF06GroupSendMembership(t *testing.T) { if err != nil { t.Fatal(err) } - if res.State != message.StateDispatched { - t.Fatalf("state=%s", res.State) + if res.State != message.StateCompleted { + t.Fatalf("state=%s want completed (offline, keep=false)", res.State) } var cnt int _ = db.Read.QueryRow(`SELECT COUNT(*) FROM deliveries d JOIN messages m ON m.seq=d.seq WHERE m.id='m1'`).Scan(&cnt) if cnt != 2 { t.Fatalf("deliveries=%d want 2 (not sender)", cnt) } + var pending, dropped int + _ = db.Read.QueryRow(`SELECT COUNT(*) FROM deliveries d JOIN messages m ON m.seq=d.seq WHERE m.id='m1' AND d.state='pending'`).Scan(&pending) + _ = db.Read.QueryRow(`SELECT COUNT(*) FROM deliveries d JOIN messages m ON m.seq=d.seq WHERE m.id='m1' AND d.state='dropped'`).Scan(&dropped) + if pending != 0 || dropped != 2 { + t.Fatalf("pending=%d dropped=%d want pending=0 dropped=2", pending, dropped) + } var self int _ = db.Read.QueryRow(`SELECT COUNT(*) FROM deliveries d JOIN messages m ON m.seq=d.seq WHERE m.id='m1' AND d.endpoint_id='alice'`).Scan(&self) if self != 0 { @@ -363,6 +370,40 @@ func TestF06GroupSendMembership(t *testing.T) { } } +func TestF06GroupSendKeepOfflinePending(t *testing.T) { + t.Parallel() + gApp, _, msgApp, db, _ := setup(t) + ctx := context.Background() + insertEP(t, db, "alice", 1) + insertEP(t, db, "bob", 1) + insertEP(t, db, "carol", 1) + created, err := gApp.Create(ctx, "alice", &protocol.GroupCreate{ + V: protocol.Version, Type: protocol.TypeGroupCreate, RID: "1", + Name: "GKeep", Members: []protocol.GroupMemberIn{{ID: "bob"}, {ID: "carol"}}, + }) + if err != nil { + t.Fatal(err) + } + ttl := int64(3600) + res, err := msgApp.Submit(ctx, "alice", port.ConnInfo{}, &protocol.Send{ + V: protocol.Version, Type: protocol.TypeSend, RID: "s", ID: "mkeep", + To: protocol.Target{Kind: protocol.TargetGroup, ID: created.ID}, + Body: protocol.Body{Enc: protocol.EncUTF8, Data: "kept"}, + Offline: &protocol.OfflineOpts{Keep: true, TTLSeconds: &ttl}, + }) + if err != nil { + t.Fatal(err) + } + if res.State != message.StateDispatched { + t.Fatalf("state=%s want dispatched (offline keep)", res.State) + } + var pending int + _ = db.Read.QueryRow(`SELECT COUNT(*) FROM deliveries d JOIN messages m ON m.seq=d.seq WHERE m.id='mkeep' AND d.state='pending'`).Scan(&pending) + if pending != 2 { + t.Fatalf("pending=%d want 2", pending) + } +} + func TestGroupTransferRenameListGet(t *testing.T) { t.Parallel() gApp, _, _, db, _ := setup(t)