package activator import ( "context" "errors" "os" "strings" "testing" "time" "github.com/carlosframework/platform/internal/release" ) func TestPlan14GenerationWorkerStartBoundary(t *testing.T) { t.Parallel() for _, claimed := range []bool{false, true} { for _, staged := range []bool{false, true} { for _, pinned := range []bool{false, true} { name := "legacy" if claimed { name = "claimed" } if staged { name += "/staged" } else { name += "/plain" } if pinned { name += "/pinned" } else { name += "/live" } t.Run(name, func(t *testing.T) { a, b, _, reg, _ := stagedActivator(t) t.Cleanup(func() { a.Shutdown(context.Background()); reg.Close() }) a.cfg.HostWrites.Enabled = claimed if !staged { a.backend = b.fakeBackend } a.GenerationsFile = release.GenerationsPath(t.TempDir()) var onto *release.StagedIdentity if pinned { id := fakeIdentity("v2") onto = &id } gates := 0 p, id, pending, err := a.startWorker(t.Context(), workerRoute("bot.x.internal"), nil, onto, func() error { gates++; return nil }) if p != nil { defer p.Stop(context.Background()) } if claimed { if !errors.Is(err, ErrClaimIsolationUnavailable) || p != nil || id != (release.StagedIdentity{}) || pending != 0 || b.workerStarts() != 0 || gates != 0 { t.Fatalf("unreserved worker reached backend: proc=%v id=%+v pending=%v starts=%d gates=%d err=%v", p, id, pending, b.workerStarts(), gates, err) } if _, err := os.Stat(a.GenerationsFile); !os.IsNotExist(err) { t.Fatalf("refused start published agent generations: %v", err) } } else if err != nil || p == nil || b.workerStarts() != 1 || gates == 0 { t.Fatalf("legacy worker changed: proc=%v starts=%d gates=%d err=%v", p, b.workerStarts(), gates, err) } }) } } } } func TestPlan14GenerationWorkerRealBackendRefusal(t *testing.T) { t.Parallel() for _, pinned := range []bool{false, true} { name := "live" if pinned { name = "pinned" } t.Run(name, func(t *testing.T) { a, b, rt, output, _ := workerGateService(t, nil) a.cfg.HostWrites.Enabled = true var onto *release.StagedIdentity if pinned { id, err := release.ResolveLive(b.OptDir, rt.Host) if err != nil { t.Fatal(err) } onto = &id } p, _, _, err := a.startWorker(t.Context(), rt, nil, onto, nil) if p != nil { defer p.Stop(context.Background()) } if !errors.Is(err, ErrClaimIsolationUnavailable) || p != nil { t.Fatalf("real backend admitted unreserved worker: %v %v", p, err) } if _, err := os.Stat(output); !os.IsNotExist(err) { t.Fatalf("refused worker executed: %v", err) } if _, err := os.Stat(a.GenerationsFile); !os.IsNotExist(err) { t.Fatalf("refused worker published agent generations: %v", err) } }) } } func TestPlan14GenerationWorkerReplacementBoundary(t *testing.T) { shrinkFirstWake(t, 55*time.Second, 25*time.Second, 20*time.Millisecond) for _, claimed := range []bool{false, true} { for _, cause := range []Cause{CauseRelease, CauseRestart, CauseConfig} { name := "legacy/" + string(cause) if claimed { name = "claimed/" + string(cause) } t.Run(name, func(t *testing.T) { a, b, _, reg, _ := stagedActivator(t) t.Cleanup(func() { a.Shutdown(context.Background()); reg.Close() }) if err := a.WakeWorker(alwaysOnWorkerRoute("bot.x.internal")); err != nil { t.Fatal(err) } old := b.lastWorker() a.cfg.HostWrites.Enabled = claimed a.GenerationsFile = release.GenerationsPath(t.TempDir()) fallback := fakeIdentity("v1") got := a.ReplaceWorker(t.Context(), workerAttempt(cause, ModeOverlap, fakeIdentity("v2")), &fallback, one) if claimed { if got.Outcome != SwapAbandoned || got.Executed || !strings.Contains(got.Reason, ErrClaimIsolationUnavailable.Error()) || b.workerStarts() != 1 { t.Fatalf("unreserved replacement: %+v starts=%d", got, b.workerStarts()) } if exited := old.Exited(); exited { t.Fatal("refused replacement stopped the serving worker") } if a.entryServing("bot.x.internal") != fallback { t.Fatal("refused replacement changed serving identity") } if _, err := os.Stat(a.GenerationsFile); !os.IsNotExist(err) { t.Fatalf("refused replacement published agent generations: %v", err) } } else if got.Outcome != SwapDone || !got.Executed || b.workerStarts() != 2 { t.Fatalf("legacy replacement changed: %+v starts=%d", got, b.workerStarts()) } }) } } } func TestPlan14GenerationWorkerSupervisionBoundary(t *testing.T) { t.Parallel() for _, claimed := range []bool{false, true} { for _, cause := range []string{"crash", "max-run", "converge"} { name := "legacy/" + cause if claimed { name = "claimed/" + cause } t.Run(name, func(t *testing.T) { a, b, clk, reg, ver := stagedActivator(t) t.Cleanup(func() { a.Shutdown(context.Background()); reg.Close() }) rt := alwaysOnWorkerRoute("bot.x.internal") if cause == "max-run" { rt.MaxRunSeconds = 30 } if err := a.WakeWorker(rt); err != nil { t.Fatal(err) } old := b.lastWorker() a.cfg.HostWrites.Enabled = claimed switch cause { case "crash": old.kill() case "max-run": clk.advance(31 * time.Second) case "converge": ver.Store("v2") a.sweepOnce() clk.advance(ConvergeGrace + time.Minute) } a.sweepOnce() a.mu.Lock() done := a.entries[rt.Host].respawnDone a.mu.Unlock() if done != nil { select { case <-done: case <-time.After(5 * time.Second): t.Fatal("respawn did not settle") } } if claimed { if b.workerStarts() != 1 { t.Fatalf("supervision spawned without root reservation: %d", b.workerStarts()) } if exited := old.Exited(); exited && cause != "crash" { t.Fatal("refused recycle stopped the serving worker") } } else if b.workerStarts() != 2 { t.Fatalf("legacy supervision changed: %d", b.workerStarts()) } }) } } }