mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
用户要求的四类测试全部落地,且每类都抓到了真问题: ## 0) 测试暴露的真实缺陷(先修后测) **保险库明文落盘**(加密解密测试的目标缺陷):.secrets/<plugin>.json 此前 是明文 JSON。接入 SecretBox(与 config.yaml 同一把 master key、同 enc:v1 形态):SetSecretBox 注入(secretSealer 接口,测试可桩);写盘逐值加密、 读盘逐值解密;无前缀旧值原样通过 —— 旧明文库可读、下次写整体重封。 core 注入点在 store 创建之后(NewPlugins 时 box 还不存在 —— 顺序 load-bearing, 修掉了一个 nil-deref)。 ## 1) 加密解密(internal/lua/secrets_crypto_test.go) - EncryptedAtRest:磁盘字节不含明文 token、带 enc:v1、文件 0600/目录 0700 - PlaintextFallbackAndUpgrade:旧明文库可读 + 任意一次写整体重封为密文 - WrongMasterKey:换 key 后读库必须报错(静默空会让插件重登覆盖凭据) - ConcurrentPlugins:8 插件×2 goroutine 并发读写,无串台/丢写/文件数正确 ## 2) 变异(internal/core/device_mutate_test.go + device_harness_test.go) - 决策表抽成 deviceHarness.runDecisionTable(行为测试与变异测试共用, 单一事实源) - 5 个语义变异逐个杀:slow_down 不加 interval / denied 不识别 / expired_token 不识别 / authorization_pending 当失败 / 成功分支禁用 - 行3/行4 断言 message 区分(用户拒绝/过期)—— 初版只断 state=failed 时 两个变异体幸存(落进通用 failed 分支),加 message 后全杀。 这正是变异测试的价值:暴露断言太弱。 - 基准(未变异)必须全过 —— 防止测试自身失效的假阳性 ## 3) 压力(internal/core/channels_stress_test.go) - ConcurrentLogins:12 轮并发 device-poll,会话互不串台、全部终态 - ClaimStress:30 并发领签到,上游收到恰 30 次(无登录态丢失) ## 4) 旧通路(channels_stress_test.go 后半 + 复用 lua 包测试) - LegacyVaultPlaintextLoad:老版本明文库 → 新版本可读 → 下次写重封 - LegacySourceSealedConfig:明文 api_key 的旧 config.yaml → 运行期解密、 落盘 seal、master.key 0600 生成 全量 lua/core/gateway/types/scheduler/provider/config 测试通过;build+vet 绿。
70 lines
2.3 KiB
Go
70 lines
2.3 KiB
Go
package core
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// TestDevicePollMutants 是 device-poll 执行器的变异测试。
|
||
//
|
||
// 用户要求:移植要照参考实现的行为形成测试项,且要变异验证测试真的能杀掉
|
||
// 「行为走样」的代码。每个用例把 channels 源里的一个决策分支做语义变异,
|
||
// 断言决策表**会失败**(变异体被杀)。变异体幸存 = 该分支没有覆盖。
|
||
func TestDevicePollMutants(t *testing.T) {
|
||
src, err := readBundledChannels(t)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
muts := []struct {
|
||
name string
|
||
from string
|
||
to string
|
||
}{
|
||
{
|
||
name: "slow_down 不再加 interval",
|
||
from: `sess.device_interval = (sess.device_interval or d.interval or 5) + 5`,
|
||
to: `sess.device_interval = (sess.device_interval or d.interval or 5)`,
|
||
},
|
||
{
|
||
name: "denied 被当成继续等待",
|
||
from: `if resp.status == 200 and (status == "denied" or status == "access_denied") then`,
|
||
to: `if resp.status == 200 and (status == "denied_x" or status == "access_denied") then`,
|
||
},
|
||
{
|
||
name: "expired_token 不被识别",
|
||
from: `if resp.status == 200 and (status == "expired" or status == "expired_token") then`,
|
||
to: `if resp.status == 200 and (status == "expired" or status == "expired_token_x") then`,
|
||
},
|
||
{
|
||
name: "authorization_pending 被当成失败",
|
||
from: `if ecode == "authorization_pending" then return false end`,
|
||
to: `if ecode == "authorization_pending_x" then return false end`,
|
||
},
|
||
{
|
||
name: "成功分支被禁用",
|
||
from: `if resp.status == 200 and rec.access_token then`,
|
||
to: `if resp.status == 200 and rec.access_token and false then`,
|
||
},
|
||
}
|
||
|
||
for _, mu := range muts {
|
||
t.Run(mu.name, func(t *testing.T) {
|
||
if !strings.Contains(src, mu.from) {
|
||
t.Fatalf("锚点丢失,先更新实现与锚点:%q", mu.from)
|
||
}
|
||
mutated := strings.Replace(src, mu.from, mu.to, 1)
|
||
if mutated == src {
|
||
t.Fatal("变异未生效")
|
||
}
|
||
// 基准必须全过(测试自身有效),变异体必须被杀(有覆盖)
|
||
if !newDeviceHarness(t, src).runDecisionTable() {
|
||
t.Fatal("基准决策表未全过 —— 测试自身失效")
|
||
}
|
||
if newDeviceHarness(t, mutated).runDecisionTable() {
|
||
t.Fatalf("★ 变异体幸存(%s)—— 该分支无覆盖", mu.name)
|
||
}
|
||
})
|
||
}
|
||
}
|