Files
sub2api/backend/internal/plugin/audit_regression_test.go
T
shaw 8ba2fbd786 feat(plugin): Phase-1/1.5 进程内插件内核 + 开发套件后端
Caddy 式进程内模块系统(详见 docs/plugin-architecture/MODULE-AUTHOR-GUIDE.md):
- internal/plugin:命名空间注册表(init 注册/重复 panic)、Provision→Validate→Start→Stop
  生命周期(Start 半途逆序回滚、Stop 逆序+ctx deadline)、Host 四项 ports 能力面、
  modules: 配置子树(enabled 三态;viper 含点 key 手工提取)、Runtime 状态机
- internal/plugin/plugintest:模块测试夹具(NewHost Options + RunLifecycle)
- internal/modules:standard/imports.go 唯一插装清单 + job.hello 示例模块(默认 disabled)
- Wire/main 接入:Build+Start 失败 fail-fast(显式 Cleanup 防 leader 锁泄漏)、
  runtime.Stop 在 cleanup 并行组首位(先于 Redis/Ent)
- admin 可观测:GET /api/v1/admin/modules(只读,错误文本脱敏)
- 默认配置零行为变更(Phase-0 安全网 + bench allocs 零漂移验证)

含两轮对抗式审计修复(1×P1 启动失败泄漏 leader 锁 + 11×P2)与回归测试。
2026-06-11 22:58:51 +08:00

60 lines
2.4 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 plugin
// Phase-1.5 对抗式审计(2026-06-11)发现的 P2 问题回归测试:
// A-1: 失败的 Build 不得被静默重跑(会对已 Provision 实例不 Stop 即覆盖)
// C-1: enabled 空值(YAML null)按"未显式配置"处理,走模块默认值
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/require"
)
// TestRuntimeBuildFailureBlocksRetry 固化审计 A-1 修复:
// Build 失败后,二次 Build 与 Start 均被拒绝,且不重新驱动任何生命周期。
func TestRuntimeBuildFailureBlocksRetry(t *testing.T) {
log := &lifecycleLog{}
okMod := &fakeModule{id: "job.a", enabledByDefault: true, log: log}
badMod := &fakeModule{id: "job.b", enabledByDefault: true, log: log, provisionErr: errors.New("boom")}
rt := newTestRuntime(t, Config{}, okMod, badMod)
require.Error(t, rt.Build(), "首次 Build 应因 job.b Provision 失败而报错")
firstLog := log.snapshot()
err := rt.Build()
require.Error(t, err)
require.Contains(t, err.Error(), "cannot be rebuilt", "失败后的二次 Build 应被明确拒绝")
require.Equal(t, firstLog, log.snapshot(), "二次 Build 不得重新 Provision 任何模块")
require.Error(t, rt.Start(context.Background()), "Build 未成功时 Start 应被拒绝")
}
// TestRuntimeBuildSuccessThenRebuildStillRejected 确认成功路径的原有 guard 语义不变。
func TestRuntimeBuildSuccessThenRebuildStillRejected(t *testing.T) {
rt := newTestRuntime(t, Config{}, &plainModule{id: "job.ok"})
require.NoError(t, rt.Build())
err := rt.Build()
require.Error(t, err)
require.Contains(t, err.Error(), "already built")
}
// TestParseConfigEnabledNilTreatedAsUnset 固化审计 C-1 修复:
// `enabled:`(YAML 空值 → nil)等价于未配置,三态语义回落到模块默认值,
// 且不影响同条目下私有配置的解析。
func TestParseConfigEnabledNilTreatedAsUnset(t *testing.T) {
cfg, err := ParseConfig(map[string]map[string]any{
"job.hello": {"enabled": nil, "greeting": "hi"},
})
require.NoError(t, err)
mc, ok := cfg["job.hello"]
require.True(t, ok)
require.Nil(t, mc.Enabled, "enabled 空值应视为未显式配置")
require.Equal(t, "hi", mc.Raw["greeting"], "私有配置不受 enabled 空值影响")
require.True(t, cfg.enabledFor(ModuleInfo{ID: "job.hello", EnabledByDefault: true}))
require.False(t, cfg.enabledFor(ModuleInfo{ID: "job.hello", EnabledByDefault: false}))
}