package host import ( "errors" "testing" "time" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rollout" "github.com/carlosframework/platform/internal/status" ) func TestPlan14RootRuntimeFailureAndCompletion(t *testing.T) { for _, outcome := range []string{"failed", "healthy", "crash", "not-ready", "replaced-after-ready"} { t.Run(outcome, func(t *testing.T) { state := testRootFailureStore(t) book, err := rollout.InitializeRootFailures(state, "box/en/1", nil) if err != nil { t.Fatal(err) } rt := registry.Route{Host: "h.example.com", Account: "bab", App: "km"} saved := rootUpdateRecord{Schema: 1, Installation: "install", Route: rt, Consumed: &release.Consumed{Resolved: &release.ResolvedRequest{ID: 7, Target: "v1"}}} p := rootUpdateSource{state: state, book: book} if outcome == "not-ready" { p.ready = func(registry.Route) error { return errors.New("not ready") } } a, err := p.beginRuntime(&saved, func() error { return nil }, "v1", "digest", "attempt") if err != nil { t.Fatal(err) } if outcome == "crash" { book, err = rollout.OpenRootFailures(state, "box/en/1") if err != nil { t.Fatal(err) } if !book.HeldDown("bab", "km", "v1", rt.Host) { t.Fatal("crash erased root intent") } return } var failure error if outcome == "failed" { failure = errors.New("readiness failed") } var verify func() error if outcome == "replaced-after-ready" { verify = func() error { return errors.New("invocation replaced") } } if err = a("invocation", failure, verify); (err != nil) != (outcome == "not-ready" || outcome == "replaced-after-ready") { t.Fatal(err) } if outcome == "failed" || outcome == "not-ready" || outcome == "replaced-after-ready" { if !book.HeldDown("bab", "km", "v1", rt.Host) { t.Fatal("root runtime failure lost") } if saved.Build.State == nil || saved.Build.State.Completed != 7 || !saved.Build.State.CompletedFailed { t.Fatal("failed request not completed", saved.Build) } } else { if saved.Build.State == nil || saved.Build.State.Completed != 7 || saved.Build.State.CompletedFailed || saved.Build.State.LastExecuted != "v1" { t.Fatal("healthy request not completed", saved.Build) } if saved.Served["v1"].Invocation != "invocation" { t.Fatal("served evidence missing") } } }) } } func TestPlan14RootRetryProjection(t *testing.T) { state := testRootFailureStore(t) book, err := rollout.InitializeRootFailures(state, "box/en/1", nil) if err != nil { t.Fatal(err) } rt := registry.Route{Host: "h.example.com", Account: "bab", App: "km"} if _, err = book.Record(rollout.FailureRecord{Account: rt.Account, App: rt.App, Host: rt.Host, Build: "v1", HeldDown: []string{rt.Host}, At: time.Now()}); err != nil { t.Fatal(err) } p := rootUpdateSource{book: book} projected := p.retryStatus(rootUpdateRecord{Route: rt}, "v1") if projected.HeldBook != book.ID() || projected.HeldEpoch == 0 || projected.RetryV != status.HostRetryV { t.Fatal("wrong retry authority", projected) } seed, public, _ := release.GenerateKey() priv, _ := release.ParseSigningKey(seed, "test") pub, _ := release.ParsePublicKey(public, "test") wire, err := release.SignHostRetry(release.RootSigner(priv), rt.Account, rt.App, rt.Host, projected.HeldBy, projected.HeldBook, projected.HeldEpoch, time.Now()) if err != nil { t.Fatal(err) } if err = book.VerifiedRetry(pub, wire, rt.Account, rt.App, rt.Host, time.Now()); err != nil { t.Fatal(err) } if book.HeldDown(rt.Account, rt.App, "v1", rt.Host) { t.Fatal("projected retry refused") } if _, err = book.Record(rollout.FailureRecord{Account: rt.Account, App: rt.App, Host: rt.Host, Build: "v1", HeldDown: []string{rt.Host}}); err != nil { t.Fatal(err) } if err = book.VerifiedRetry(pub, wire, rt.Account, rt.App, rt.Host, time.Now()); err != nil { t.Fatal(err) } if !book.HeldDown(rt.Account, rt.App, "v1", rt.Host) { t.Fatal("stale projection cleared new failure") } } func TestPlan14RootFailureDeliveryCannotHoldActivation(t *testing.T) { for _, ready := range []bool{false, true} { t.Run(map[bool]string{false: "unready", true: "ready"}[ready], func(t *testing.T) { book, err := rollout.InitializeRootFailures(testRootFailureStore(t), "box/en/1", nil) if err != nil { t.Fatal(err) } saved := rootUpdateRecord{Route: registry.Route{Host: "h.example.com", Account: "bab", App: "km"}, Installation: "install"} delivered, verified := 0, 0 p := rootUpdateSource{book: book, publish: func(string, rootRetryStatusRecord) error { delivered++; return errors.New("delivery unavailable") }, ready: func(registry.Route) error { if !ready { return errors.New("unready") } return nil }} finish, err := p.beginRuntime(&saved, func() error { return nil }, "v1", "digest", "attempt") if err != nil { t.Fatal(err) } err = finish("invocation", nil, func() error { verified++; return nil }) if (err == nil) != ready || delivered != 1 { t.Fatal("delivery became launch authority or readiness was waived", err, delivered) } if ready && (verified != 1 || saved.Served["v1"].Invocation != "invocation") { t.Fatal("settlement lost") } if !ready && !book.HeldDown("bab", "km", "v1", "h.example.com") { t.Fatal("failure attribution lost") } }) } }