package console import ( "context" "crypto/ed25519" "encoding/json" "errors" "net/http" "net/http/httptest" "os" "testing" "time" "github.com/carlosframework/platform/internal/hostops" "github.com/carlosframework/platform/internal/release" ) type aliasWriteSpy struct { release.Store writes int } func (s *aliasWriteSpy) Put(context.Context, string, []byte, string) (string, error) { s.writes++ return "changed", nil } func (s *aliasWriteSpy) Delete(context.Context, string) error { s.writes++; return nil } func TestAliasOwnershipCannotRebaseOrBareDelete(t *testing.T) { base := &aliasWriteSpy{} guarded := claimAliasStore{data: base, key: "domains/alias.example.com.json", etag: "original"} for _, etag := range []string{"", "successor"} { if _, err := guarded.Put(context.Background(), guarded.key, []byte(`{}`), etag); !errors.Is(err, hostops.ErrPending) { t.Fatalf("rebase %q: %v", etag, err) } } if _, err := guarded.Put(context.Background(), "domains/other.example.com.json", []byte(`{}`), "original"); !errors.Is(err, hostops.ErrPending) { t.Fatal(err) } if err := guarded.Delete(context.Background(), guarded.key); !errors.Is(err, hostops.ErrContract) { t.Fatal(err) } if base.writes != 0 { t.Fatalf("unauthorized writes: %d", base.writes) } } func (s *aliasWriteSpy) Get(context.Context, string) ([]byte, string, error) { return nil, "", os.ErrNotExist } func TestAliasMarkerPutCannotStartAfterPurgeDrain(t *testing.T) { ctx := context.Background() base := &aliasWriteSpy{} raw := release.NewDirStore(t.TempDir()) pub, _, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } now := time.Now() w, err := hostops.NewControlWriter(raw, pub, func() time.Time { return now }) if err != nil { t.Fatal(err) } open := hostops.AppAdmission{V: 1, AppID: "app-id", Revision: 1, Epoch: 1, State: "open", Registrations: map[string]hostops.Registration{}, Effects: map[string]hostops.Effect{}} putAdmission := func(a hostops.AppAdmission) { body, _ := json.Marshal(a) _, tag, _ := raw.Get(ctx, "apps/bab/site/admission.json") if _, err := raw.Put(ctx, "apps/bab/site/admission.json", body, tag); err != nil { t.Fatal(err) } } putAdmission(open) owner := hostops.RegistrationOwner{Console: "console", Enrollment: "enrolled", Controller: "controller"} h, err := w.RegisterStart(ctx, "bab", "site", "app-id", "old-registration", hostops.Registration{Kind: "create", Host: "alias.bab.example.com", Claim: hostops.SavedClaim{Absent: true}, Owner: owner, AppID: "app-id", Epoch: 1, CreatedAt: now}, now) if err != nil { t.Fatal(err) } // The purge drained the registration and closed the gate. drained := open drained.State, drained.Revision = "closed", 2 drained.Op = &hostops.AppOperation{ID: "purge", Kind: hostops.OpPurging, AppID: "app-id"} putAdmission(drained) guarded := claimAliasStore{data: base, key: "domains/alias.example.com.json", etag: "original", account: "bab", app: "site", acquisition: "acq", incarnation: "inc", effects: claimStoreEffects{OperationID: "old-registration", Runner: h.Runner(time.Now)}} _, err = guarded.Put(ctx, guarded.key, []byte(`{"account":"bab","app":"site"}`), "original") if !errors.Is(err, hostops.ErrPending) { t.Fatalf("drained registration issued effect: %v", err) } if base.writes != 0 { t.Fatalf("writes after drain: %d", base.writes) } } func TestFlaggedDomainCapabilitiesRefuseBeforeBucketRead(t *testing.T) { s := &Server{cfg: Config{HostClaim: true}} w := httptest.NewRecorder() yes := true if s.setDomainCapabilities(w, httptest.NewRequest("POST", "/", nil), context.Background(), nil, "site", Principal{}, "alias.example.com", &yes, nil) { t.Fatal("capability mutation admitted") } if w.Code != http.StatusConflict { t.Fatalf("status=%d", w.Code) } }