package mail import ( "context" "errors" "testing" "time" "github.com/carlosframework/platform/internal/dnsrec" "github.com/carlosframework/platform/internal/release" ) const consoleMailDomain = "console-mail.bab.oncarlos.com" // consoleMailFixture ensures the console's own sending domain the way the // console does: ops account, referrer ConsoleMailApp, a platform name. func consoleMailFixture(t *testing.T) (context.Context, release.Store, *MemProvisioner, *dnsrec.MemWriter) { t.Helper() ctx := context.Background() s, p, w := release.NewDirStore(t.TempDir()), NewMemProvisioner("shared-aws-account"), dnsrec.NewMemWriter() now := time.Date(2026, 10, 2, 12, 0, 0, 0, time.UTC) if _, _, err := EnsureDomain(ctx, s, p, w, testDeployment(), release.OpsAccount, ConsoleMailApp, consoleMailDomain, now); err != nil { t.Fatalf("EnsureDomain: %v", err) } return ctx, s, p, w } func assertConsoleMailIntact(t *testing.T, ctx context.Context, s release.Store, p *MemProvisioner) { t.Helper() rec, _, err := GetDomain(ctx, s, release.OpsAccount, consoleMailDomain) if err != nil || rec == nil { t.Fatalf("the console's domain record is gone: %+v (err %v)", rec, err) } if len(rec.Apps) != 1 || rec.Apps[0] != ConsoleMailApp { t.Fatalf("referrers = %v, want [%s]", rec.Apps, ConsoleMailApp) } if _, ok := p.Identities[identityKey("eu-west-1", consoleMailDomain)]; !ok { t.Fatal("the console's SES identity was deleted") } if claim, err := getDomainClaim(ctx, s, consoleMailDomain); err != nil || claim == nil { t.Fatalf("the console's domain claim was released: %+v (err %v)", claim, err) } } // An app that lands on the reserved name in the ops account (a move, a // migrate, a row from before the reservation) is still an app, and deleting // it runs Teardown. That must not take the console's own sending domain // with it. func TestTeardownKeepsTheConsoleMailDomain(t *testing.T) { ctx, s, p, w := consoleMailFixture(t) if err := Teardown(ctx, s, p, w, testDeployment(), release.OpsAccount, ConsoleMailApp); err != nil { t.Fatalf("Teardown: %v", err) } assertConsoleMailIntact(t, ctx, s, p) } // The same name in any other account is an ordinary app: its Teardown // releases its domains as usual. func TestTeardownOfConsoleMailNameElsewhereIsOrdinary(t *testing.T) { ctx := context.Background() s, p, w := release.NewDirStore(t.TempDir()), NewMemProvisioner("shared-aws-account"), dnsrec.NewMemWriter() now := time.Date(2026, 10, 2, 12, 0, 0, 0, time.UTC) if _, _, err := EnsureDomain(ctx, s, p, w, testDeployment(), "aaa", ConsoleMailApp, "console-mail.aaa.oncarlos.com", now); err != nil { t.Fatalf("EnsureDomain: %v", err) } if err := Teardown(ctx, s, p, w, testDeployment(), "aaa", ConsoleMailApp); err != nil { t.Fatalf("Teardown: %v", err) } if rec, _, err := GetDomain(ctx, s, "aaa", "console-mail.aaa.oncarlos.com"); err != nil || rec != nil { t.Fatalf("an ordinary app's domain survived its teardown: %+v (err %v)", rec, err) } } func TestRemoveDomainRefusesTheConsoleMailDomain(t *testing.T) { ctx, s, p, w := consoleMailFixture(t) out, err := RemoveDomain(ctx, s, p, w, testDeployment(), release.OpsAccount, ConsoleMailApp, consoleMailDomain) if !errors.Is(err, ErrConsoleMailReserved) { t.Fatalf("RemoveDomain = %+v, %v; want ErrConsoleMailReserved", out, err) } assertConsoleMailIntact(t, ctx, s, p) } // A claim with no record (an ensure that failed after claiming) is released // by RemoveDomain for any app in the account. The console's claim is not: // the console re-runs its own ensure and picks it back up. func TestRemoveDomainRefusesTheConsoleMailOrphanClaim(t *testing.T) { ctx, s, p, w := consoleMailFixture(t) if err := s.Delete(ctx, DomainKey(release.OpsAccount, consoleMailDomain)); err != nil { t.Fatal(err) } if _, err := RemoveDomain(ctx, s, p, w, testDeployment(), release.OpsAccount, "blog", consoleMailDomain); !errors.Is(err, ErrConsoleMailReserved) { t.Fatalf("RemoveDomain(orphan) = %v, want ErrConsoleMailReserved", err) } if claim, err := getDomainClaim(ctx, s, consoleMailDomain); err != nil || claim == nil { t.Fatalf("the console's orphan claim was released: %+v (err %v)", claim, err) } if _, ok := p.Identities[identityKey("eu-west-1", consoleMailDomain)]; !ok { t.Fatal("the console's SES identity was deleted") } } // Teardown's orphan-claim sweep is scoped to the app that made the claim, // so it too must skip the console's. func TestTeardownKeepsTheConsoleMailOrphanClaim(t *testing.T) { ctx, s, p, w := consoleMailFixture(t) if err := s.Delete(ctx, DomainKey(release.OpsAccount, consoleMailDomain)); err != nil { t.Fatal(err) } if err := Teardown(ctx, s, p, w, testDeployment(), release.OpsAccount, ConsoleMailApp); err != nil { t.Fatalf("Teardown: %v", err) } if claim, err := getDomainClaim(ctx, s, consoleMailDomain); err != nil || claim == nil { t.Fatalf("the console's orphan claim was swept: %+v (err %v)", claim, err) } if _, ok := p.Identities[identityKey("eu-west-1", consoleMailDomain)]; !ok { t.Fatal("the console's SES identity was deleted") } }