Files
NixMsg/test/report/catalog.go
T

179 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package report
import "strings"
func passSub(id, summary, evidence string) SubItem {
return SubItem{ID: id, Summary: summary, Status: StatusPass, Evidence: evidence}
}
func untestedSub(id, summary, note string) SubItem {
return SubItem{ID: id, Summary: summary, Status: StatusUntested, Note: note}
}
var catalogSubs = map[string][]SubItem{
"F01": {
passSub("F01a", "开通一端并可 MQTT 登录", "test/accept/accept_test.go runEndpointCreate"),
passSub("F01b", "批量开通整批校验", "internal/admin/endpoints_test.go TestEndpointImportDuplicateRejectsAll、TestEndpointImportBOMAndCreate"),
passSub("F01c", "停用", "internal/app/identity/lifecycle_test.go TestF01DisableVoidsScheduledAndRejectsNew;internal/admin/endpoints_test.go TestEndpointDisableKickAndUnlock"),
passSub("F01d", "删除群主转让", "internal/app/identity/lifecycle_test.go TestF01DeleteOwnerTransfersEarliest"),
passSub("F01e", "删除后同编号重开不串数据", "internal/app/identity/lifecycle_test.go TestF01DeleteReopenNoOldReceipts"),
},
"F02": {
passSub("F02a", "密码登录后 hello / 会话令牌", "test/accept/accept_test.go;internal/broker/f02_test.go TestF02PasswordLoginReturnsTokenAndHandshake"),
passSub("F02b", "新设备密码登录后旧设备退出", "internal/broker/f02_test.go TestF02TakenOverByPasswordLogin、TestF02OldTokenRejectedAfterPasswordLogin"),
passSub("F02c", "换 IP 用令牌重连", "internal/broker/f02_test.go TestF02TokenReconnectDifferentIPKeepsToken"),
passSub("F02d", "两种密码锁定", "internal/broker/f02_test.go TestF02IPLockDoesNotAffectOtherIP、TestF02EndpointLockAllowsTokenReconnect"),
passSub("F02e", "重置密码后被踢", "internal/broker/f02_test.go TestF02AdminResetPasswordFatal;cmd/nixmsg/fatal_revoked_integration_test.go TestUplinkResetPasswordFatal"),
passSub("F02f", "服务器故障不误报密码错误", "internal/broker/f02_test.go TestF02DBErrorClosesWithout086;internal/broker/broker_test.go TestInternalAuthErrorDoesNotReturnBadPassword"),
},
"F03": {
passSub("F03a", "断开后状态及时变离线", "test/accept/rest_accept_test.go;internal/app/presence/presence_test.go TestF03PresenceAndDirectory"),
passSub("F03b", "目录可列出", "test/accept/rest_accept_test.go;internal/app/presence/presence_test.go TestF03PresenceAndDirectory"),
untestedSub("F03c", "1000 端全表 1s 内返回", "环境/时长限制,本波不跑"),
untestedSub("F03d", "真拔网线后心跳超时离线", "环境限制;验收用关连接模拟断线"),
},
"F04": {
passSub("F04a", "只通知订阅了的端", "test/accept/rest_accept_test.go;internal/app/presence/presence_test.go TestF04PresenceWatch"),
},
"F05": {
passSub("F05a", "双端在线单聊送达与确认", "test/accept/accept_test.go runMessagingAccept"),
passSub("F05b", "崩溃不丢已提交消息", "test/chaos/q3_crash_test.go TestQ3CrashSubmitThenRestart"),
passSub("F05c", "消息号冲突", "internal/app/message/submit_test.go TestSubmitTable/conflict"),
passSub("F05d", "密码门生效", "internal/app/message/submit_test.go TestSubmitTable 对话密码用例"),
passSub("F05e", "配额生效", "internal/app/message/submit_test.go TestSubmitTable/quota_exceeded"),
},
"F06": {
passSub("F06a", "群成员收到同一份、发送者不收到自己的", "test/accept/accept_test.go;internal/app/group/group_test.go TestF06GroupSendMembership"),
passSub("F06b", "入群前不补", "internal/app/group/group_test.go TestF06GroupSendMembership(发送后入群无旧投递)"),
},
"F07": {
passSub("F07a", "256 KiB 通过", "test/accept/rest_accept_test.go"),
passSub("F07b", "超出拒绝", "test/accept/rest_accept_test.go body_too_large"),
passSub("F07c", "接收上限生效", "test/accept/rest_accept_test.go max_receive_bytes=1024"),
},
"F08": {
passSub("F08a", "重启后续传", "test/accept/accept_test.go;test/chaos/q3_crash_test.go"),
passSub("F08b", "应用层不重复(同号同指纹)", "internal/app/message/submit_test.go TestSubmitTable/idempotent_hit"),
passSub("F08c", "toxiproxy 弱网保留送达", "test/chaos/q3_weak_test.go TestQ3ToxiproxyOfflineKeepDelivered"),
untestedSub("F08d", "Linux netem 20% 丢包", "本机 Windows;test/chaos/q3_weak_test.go TestQ3NetemUntested"),
},
"F09": {
passSub("F09a", "离线保留期内上线送达", "test/accept/accept_test.go"),
passSub("F09b", "超时过期", "internal/app/message/delivery_test.go TestDeliveryStateMachine/F09_keep_ttl_expire_via_cleanup"),
},
"F10": {
passSub("F10a", "短断线送到", "test/accept/rest_accept_test.go"),
passSub("F10b", "长断线丢弃", "test/accept/rest_accept_test.go"),
passSub("F10c", "服务器重启后宽限内重连送到", "test/accept/rest_accept_test.go"),
},
"F11": {
passSub("F11a", "发送方离线后到点仍发送", "test/accept/rest_accept_test.go"),
},
"F12": {
passSub("F12a", "延迟窗口内撤回对方收不到", "test/accept/accept_test.go"),
},
"F13": {
passSub("F13a", "未推送必撤成功", "test/accept/accept_test.go"),
passSub("F13b", "群部分确认得到部分撤回", "internal/app/message/delivery_test.go TestDeliveryStateMachine/F13_recall_race_partial"),
},
"F14": {
passSub("F14a", "回执能补送给当时离线的发送方", "test/accept/rest_accept_test.go"),
},
"F15": {
passSub("F15a", "输一次记住", "test/accept/rest_accept_test.go;internal/app/identity/self_talk_test.go TestF15TalkPasswordAuth"),
passSub("F15b", "改密失效", "test/accept/rest_accept_test.go;internal/app/identity/self_talk_test.go TestF15ReplyGrantAndChange"),
passSub("F15c", "回复免密", "test/accept/rest_accept_test.go;internal/app/identity/self_talk_test.go"),
passSub("F15d", "进群仍要密码", "test/accept/rest_accept_test.go;internal/app/identity/self_talk_test.go TestF15JoinNeedsPasswordDespiteGrant"),
passSub("F15e", "防多账号轮流猜", "test/accept/rest_accept_test.go"),
},
"F16": {
passSub("F16a", "建群拉成员后可群发", "test/accept/accept_test.go"),
passSub("F16b", "群主权限", "internal/app/group/group_test.go TestF16CreateAddPasswordAndOwner"),
passSub("F16c", "退出后不再收到", "internal/app/group/group_test.go TestF16LeaveRemoveDissolve"),
passSub("F16d", "解散后同编号新群不收旧消息", "internal/app/group/group_test.go TestF16LeaveRemoveDissolve"),
},
"F17": {
passSub("F17a", "管理登录、CSRF、错误密码锁定", "test/accept/accept_test.go runAdminAuth"),
passSub("F17b", "管端开通", "test/accept/accept_test.go runEndpointCreate"),
passSub("F17c", "管注册", "test/accept/accept_test.go runRegistration;internal/admin/a3_test.go TestRegistrationToggleAndGenerate"),
passSub("F17d", "管群", "internal/admin/a3_test.go TestGroupsCRUD"),
passSub("F17e", "查记录响应无正文", "internal/admin/a3_test.go TestMessageDetailHasNoBody"),
passSub("F17f", "API 令牌可用且不能越权", "internal/admin/admin_test.go TestAPITokenAuthAndRestrictions"),
},
"F18": {
passSub("F18a", "送达后正文消失", "test/accept/rest_accept_test.go;internal/app/message/delivery_test.go TestDeliveryStateMachine/F18_body_gone_record_zero_idempotent_kept"),
passSub("F18b", "记录天数 0 时连记录消失", "test/accept/rest_accept_test.go"),
passSub("F18c", "防重仍在", "test/accept/rest_accept_test.go;internal/app/message/submit_test.go TestSubmitTable/idempotent_hit"),
},
"F19": {
passSub("F19a", "四种 SDK 接入清单(仓库内已有测试,本波不重跑全量)", "sdk/go/itest_checklist_test.go;sdk/js/test/checklist.test.ts;sdk/python/tests/test_checklist.py;sdk/java ChecklistTest"),
},
"F20": {
passSub("F20a", "裸 MQTT WebSocket 登录、收、确认、发", "test/accept/accept_test.go runMessagingAccept"),
},
"F21": {
passSub("F21a", "同一 listen 端口提供后台、WebSocket、注册", "test/accept/accept_test.go"),
untestedSub("F21b", "裸 TCP MQTT 登录收发", "验收用例只走 WebSocket;listener 首字节识别见 internal/listener/listener_test.go TestIdentifyPlainHTTPAndMQTT,不足以上升为通过"),
passSub("F21c", "后台可分到单独端口", "internal/listener/listener_test.go TestAdminSeparateMQTTClosedAndAdmin404OnListen"),
},
"F22": {
passSub("F22a", "空目录 admin init + serve 健康检查", "test/accept/accept_test.go runInitHealthz"),
passSub("F22b", "备份", "cmd/nixmsg/commands_test.go TestBackupVacuumInto"),
passSub("F22c", "升级迁移前备份", "internal/store/db_test.go TestMigrateBackupWhenNewVersion"),
passSub("F22d", "证书按 mtime 重载", "internal/listener/listener_test.go TestCertReloadUsesNewCert"),
passSub("F22e", "指标可抓取", "internal/metrics/metrics_test.go TestMetricsHandlerExposesText;internal/httpx/metrics_test.go TestMetricsGateSharedPort"),
untestedSub("F22f", "Docker 全量冒烟", "镜像未推送;本波未跑 compose 全量"),
},
"F23": {
passSub("F23a", "注册开关、安全码校验、换码不影响已注册", "test/accept/accept_test.go runRegistration;internal/app/identity/register_test.go TestRegisterF23_*"),
passSub("F23b", "输错锁定", "internal/app/identity/register_test.go TestRegisterF23_WrongCodeLock"),
},
}
// CatalogItems 按 PRD §10 拆子项;整项仅在全部子项通过时为通过。
func CatalogItems() []Item {
out := make([]Item, 0, len(Features))
for _, f := range Features {
subs := catalogSubs[f.ID]
copied := make([]SubItem, len(subs))
copy(copied, subs)
out = append(out, Rollup(Item{ID: f.ID, Subs: copied}))
}
return out
}
// CatalogResults 写出带固定时间戳的结果,便于对照表可复现。
func CatalogResults() Results {
return Results{
GeneratedAt: "2026-09-30T08:30:00Z",
Items: CatalogItems(),
}
}
// Reconcile 以目录为底;现场探测失败则整项记失败,忽略「部分覆盖却标通过」。
func Reconcile(live []Item) []Item {
byLive := make(map[string]Item, len(live))
for _, it := range live {
byLive[strings.ToUpper(strings.TrimSpace(it.ID))] = it
}
out := CatalogItems()
for i, it := range out {
lv, ok := byLive[it.ID]
if !ok {
continue
}
st, err := parseStatus(lv.Status)
if err != nil {
continue
}
if st == StatusFail {
it.Status = StatusFail
if lv.Note != "" {
it.Note = lv.Note
}
out[i] = it
}
}
return out
}