Files
xlgo-core/app_storage_test.go
杭州明婳科技 96ce883d41 feat: cron/storage/cache/ratelimit 实例化 + trace 纳入 App 生命周期 (v1.4.0)
完成 instance+facade 一致性收尾,闭合三个原始问题:
- cron/storage/cache/ratelimit 实例化(App 持 per-App 实例 + SwapDefault* 提升全局默认
  + 包级 facade 代理,照 database.Manager/jwt.Manager 模式),消除单进程多 App 互相污染。
- trace 纳入 App 生命周期(WithTrace + doInit trace.Init + doShutdown trace.Close +
  config.TraceConfig + 自动装 Middleware)。不做实例隔离(OTel 进程全局)。
- 删除 WithoutWire 空函数技术债。

附带修复:
- cache/ratelimit redis 注入(jwt 模型:注入优先、database.GetRedis() 兜底)。
- commitReplacedResources 不再关闭 previousDB/previousRedis/loggerSnapshot
  (多 App 下 previous 可能是另一 App 的活跃资源,关闭致 redis: client is closed)。
- app.RateLimitRegistry() + App-bound 限流中间件(多 App per-App 计数隔离)。
- StorageManager.Close() 闭合 Init/Close 对称性(io.Closer 断言,预留扩展点),
  closeResources 与 rollbackReplacedResources 对称收口。

新增公开 API:WithTrace / WithCronTask;app.Scheduler()/Cache()/RedisClient()/
RateLimitRegistry();cron/storage/cache/middleware 各 SwapDefault*;config.TraceConfig;
middleware.RateLimitRegistry + App-bound 方法;middleware.WithRedisClient;
storage.StorageManager.Close。

Breaking:删 WithoutWire;cron.AddTask pre-Init 注册失效(改用 WithCronTask);
middleware.StopRateLimiters 语义收窄;commitReplacedResources 不再关 previous;
cache.NewRedisCacheWithRedis / CacheManager.InitWithRedis 等签名变更。
详见 CHANGELOG [1.4.0] 升级说明。

验证:go build + go vet + go test -race ./... 全绿(-buildvcs=false)。
独立对抗性复审无 CRITICAL/HIGH。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 16:25:37 +08:00

72 lines
2.4 KiB
Go
Raw Permalink 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 xlgo_test
import (
"errors"
"testing"
xlgo "github.com/EthanCodeCraft/xlgo-core"
"github.com/EthanCodeCraft/xlgo-core/config"
"github.com/EthanCodeCraft/xlgo-core/storage"
)
// localStorageCfg 构造一个本地存储配置(tmpdir 由调用方提供)。
func localStorageCfg(port int, dir string) *config.Config {
cfg := testConfig(port)
cfg.Storage = config.StorageConfig{
Driver: "local",
Local: config.LocalStorageConfig{Path: dir},
}
return cfg
}
// TestAppWithStorageSwapsDefault 固化 Phase 2 契约:App.Init(WithStorage) 必须把 App 的
// StorageManager 提升为全局默认。证据:Init 前 GetStorage()=nil(空 manager),Init 后非 nil。
func TestAppWithStorageSwapsDefault(t *testing.T) {
// 用空 manager 作"Init 前"默认,确保 GetStorage() 起点 nil
saved := storage.SwapDefaultStorageManager(storage.NewStorageManager())
defer storage.SwapDefaultStorageManager(saved)
if s := storage.GetStorage(); s != nil {
t.Fatalf("precondition: GetStorage() should be nil, got %T", s)
}
dir := t.TempDir()
app := xlgo.New(xlgo.WithConfig(localStorageCfg(18094, dir)), xlgo.WithStorage())
if err := app.Init(); err != nil {
t.Fatalf("Init: %v", err)
}
defer app.Shutdown()
if s := storage.GetStorage(); s == nil {
t.Fatal("storage.GetStorage() = nil after App.Init with WithStorage(未把 App manager 提升为全局默认)")
}
}
// TestAppStorageRollbackOnInitFailure 固化 Phase 2 回滚契约:Init 在 storage 之后失败时,
// rollbackReplacedResources 必须把全局默认 swap 回 Init 前的旧 manager。
// 用 OnInit hook 触发失败(OnInit 在 storage init 之后、commitReplacedResources 之前)。
func TestAppStorageRollbackOnInitFailure(t *testing.T) {
saved := storage.SwapDefaultStorageManager(storage.NewStorageManager())
defer storage.SwapDefaultStorageManager(saved)
dir := t.TempDir()
app := xlgo.New(
xlgo.WithConfig(localStorageCfg(18095, dir)),
xlgo.WithStorage(),
xlgo.WithHook(xlgo.Hook{
Name: "force-fail",
OnInit: func(*xlgo.App) error { return errors.New("boom") },
}),
)
if err := app.Init(); err == nil {
_ = app.Shutdown()
t.Fatal("expected Init to fail via OnInit hook")
}
// 回滚后全局默认应恢复为 saved(空 manager-> GetStorage() 返回 nil
if s := storage.GetStorage(); s != nil {
t.Errorf("Init 失败回滚未恢复 storage 全局默认: GetStorage()=%T", s)
}
}