package bootstrap import ( "os" "path/filepath" "strings" "testing" "chat-agent-system/internal/config" ) func testCfg(t *testing.T, id, dataDir, vault string) *config.Config { t.Helper() return &config.Config{InstanceID: id, DataDir: dataDir, VaultStateRoot: vault} } func TestEnsureInstanceCreatesMarkerAndLock(t *testing.T) { dir := t.TempDir() cfg := testCfg(t, "alpha", filepath.Join(dir, "alpha"), "") own, err := EnsureInstance(cfg) if err != nil { t.Fatalf("ensure: %v", err) } raw, err := os.ReadFile(filepath.Join(dir, "alpha", "instance-marker.json")) if err != nil { t.Fatalf("marker missing: %v", err) } if !strings.Contains(string(raw), "\"instance_id\": \"alpha\"") { t.Fatalf("marker content: %s", raw) } // 二次取得同实例:同进程内 flock 可重入(同 fd 语义),但模拟误重复 // 启动需要另一个进程;此处验证所有权对象可用并释放。 own.Release() } func TestEnsureInstanceRejectsForeignMarker(t *testing.T) { dir := t.TempDir() alpha := filepath.Join(dir, "alpha") if _, err := EnsureInstance(testCfg(t, "alpha", alpha, "")); err != nil { t.Fatal(err) } // 复用同一 data dir 但自称另一实例:必须拒绝(KTD18 不自动认领) if _, err := EnsureInstance(testCfg(t, "beta", alpha, "")); err == nil { t.Fatal("foreign instance marker must be rejected") } } func TestEnsureInstanceRejectsVaultOverlap(t *testing.T) { dir := t.TempDir() data := filepath.Join(dir, "data") vault := filepath.Join(data, "vault") // vault 在 data 内:非独立 if _, err := EnsureInstance(testCfg(t, "gamma", data, vault)); err == nil { t.Fatal("vault inside data dir must be rejected") } vaultOut := filepath.Join(dir, "vault") if _, err := EnsureInstance(testCfg(t, "gamma", data, vaultOut)); err != nil { t.Fatalf("independent vault should pass: %v", err) } } func TestEnsureInstanceRejectsInvalidID(t *testing.T) { dir := t.TempDir() for _, bad := range []string{"", "a/b", "..", "a b", "."} { if _, err := EnsureInstance(testCfg(t, bad, filepath.Join(dir, "x"), "")); err == nil { t.Fatalf("invalid instance id %q must be rejected", bad) } } } func TestEnsureInstanceRejectsRootAsDataDir(t *testing.T) { if _, err := EnsureInstance(testCfg(t, "root-try", "/", "")); err == nil { t.Fatal("filesystem root as data dir must be rejected") } } func TestEnsureInstanceDetectsDuplicateStart(t *testing.T) { dir := t.TempDir() data := filepath.Join(dir, "dup") own, err := EnsureInstance(testCfg(t, "dup", data, "")) if err != nil { t.Fatalf("first acquire: %v", err) } defer own.Release() // 同机第二次取得(独立 open file description):必须拒绝误重复启动 if _, err := EnsureInstance(testCfg(t, "dup", data, "")); err == nil { t.Fatal("duplicate start must be detected and rejected") } }