package console
// The /fleet/architecture page (archpage.go): its gate, its honest empty
// state, and its refresh door. What these tests own is the DOOR's shape —
// who may see the page at all, what it says when it has never had an
// inventory, and that the refresh button cannot be driven from another
// site. The graph itself is proven in archgraph_test.go and the drawing in
// archlayout_test.go.
import (
"context"
"errors"
"net/http"
"net/url"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"time"
"github.com/carlosframework/platform/internal/economics"
"github.com/carlosframework/platform/internal/infraview"
"github.com/carlosframework/platform/internal/registry"
"github.com/carlosframework/platform/internal/release"
"github.com/carlosframework/platform/internal/status"
)
// TestFleetOperatorOnly's shape against /fleet/architecture: signed out,
// signed in but not an operator, and a revoked operator all get the exact
// 404 an unrouted path gets — byte-identical, so the route's existence is
// not probeable.
func TestArchitectureOperatorOnly(t *testing.T) {
fx := newConsoleFixture(t)
out := newBrowser(t)
ghostStatus, _, ghost := get(t, out, fx.ts.URL+"/this-route-does-not-exist")
if ghostStatus != http.StatusNotFound {
t.Fatalf("sanity: missing route = %d", ghostStatus)
}
if st, _, body := get(t, out, fx.ts.URL+"/fleet/architecture"); st != http.StatusNotFound || body != ghost {
t.Fatalf("signed-out = %d %q, want the same 404 as a missing route", st, body)
}
owner := signInPaul(t, fx)
if st, _, body := get(t, owner, fx.ts.URL+"/fleet/architecture"); st != http.StatusNotFound || body != ghost {
t.Fatalf("non-operator = %d %q, want the same 404", st, body)
}
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
st, _, body := get(t, owner, fx.ts.URL+"/fleet/architecture")
if st != 200 || !strings.Contains(body, "archsvg") {
t.Fatalf("operator view = %d, want 200 with the diagram:\n%s", st, body)
}
if err := fx.st.SetOperator("paul@keymail.dev", false); err != nil {
t.Fatal(err)
}
if st, _, body := get(t, owner, fx.ts.URL+"/fleet/architecture"); st != http.StatusNotFound || body != ghost {
t.Fatalf("revoked = %d %q, want the same 404", st, body)
}
}
// The refresh door's gate is the page's, and a wrong-method request is
// byte-identical to a missing route: a "POST /path" registration would
// answer a GET with 405 + Allow, which leaks that the route exists.
func TestArchitectureRefreshOperatorOnly(t *testing.T) {
fx := newConsoleFixture(t)
out := newBrowser(t)
ghostStatus, _, ghost := get(t, out, fx.ts.URL+"/this-route-does-not-exist")
if ghostStatus != http.StatusNotFound {
t.Fatalf("sanity: missing route = %d", ghostStatus)
}
if st, _, body := postForm(t, out, fx.ts.URL+"/fleet/architecture/refresh", url.Values{}); st != http.StatusNotFound || body != ghost {
t.Fatalf("signed-out refresh = %d %q, want the same 404 as a missing route", st, body)
}
c := signInPaul(t, fx)
if st, _, body := postForm(t, c, fx.ts.URL+"/fleet/architecture/refresh", url.Values{}); st != http.StatusNotFound || body != ghost {
t.Fatalf("non-operator refresh = %d %q, want the same 404", st, body)
}
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
// A GET on the door is a missing route too, for the same reason.
if st, _, body := get(t, c, fx.ts.URL+"/fleet/architecture/refresh"); st != http.StatusNotFound || body != ghost {
t.Fatalf("GET on the refresh door = %d %q, want the same 404", st, body)
}
}
// With no inventory ever taken, the page still renders and says WHICH of
// the three states it is in — never a confident "no resources".
func TestArchitecturePageWithoutInventory(t *testing.T) {
fx := newConsoleFixture(t)
c := signInPaul(t, fx)
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
st, _, body := get(t, c, fx.ts.URL+"/fleet/architecture")
if st != 200 {
t.Fatalf("= %d, want 200", st)
}
if !strings.Contains(body, "No inventory has been taken yet") {
t.Errorf("page must name the never-taken state:\n%s", body)
}
if strings.Contains(body, "no resources") {
t.Errorf("page must not claim an empty estate it never read")
}
}
// A deployment whose inventory grant was refused says so in those words —
// the second of the three states, and the one that must never be rendered
// as "nothing here".
func TestArchitecturePageUngranted(t *testing.T) {
fx := newConsoleFixture(t)
c := signInPaul(t, fx)
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
fx.srv.infraDisabled.Store(true)
st, _, body := get(t, c, fx.ts.URL+"/fleet/architecture")
if st != 200 {
t.Fatalf("= %d, want 200", st)
}
if !strings.Contains(body, "no infrastructure read grant") {
t.Errorf("page must name the ungranted state:\n%s", body)
}
if strings.Contains(body, "No inventory has been taken yet") {
t.Errorf("ungranted must not be reported as never-taken")
}
}
// The refresh door: CSRF-checked, single-flighted, and it writes an
// inventory the page then reads back.
func TestArchitectureRefreshDoor(t *testing.T) {
fx := newConsoleFixture(t)
c := signInPaul(t, fx)
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
called := 0
fx.srv.inventory = func(ctx context.Context) (infraview.Inventory, error) {
called++
return infraview.Inventory{Provider: "aws", Account: "123456789012", TakenAt: time.Now().UTC()}, nil
}
// No CSRF token: refused, and the pass must not have run.
st, _, _ := postForm(t, c, fx.ts.URL+"/fleet/architecture/refresh", url.Values{})
if st != http.StatusForbidden {
t.Fatalf("no-CSRF refresh = %d, want 403", st)
}
if called != 0 {
t.Fatalf("a refused refresh must not run the pass")
}
// With the token: runs once and redirects back to the page.
csrf := sessionToken(t, fx, c)
st, loc, body := postForm(t, c, fx.ts.URL+"/fleet/architecture/refresh", url.Values{"csrf": {csrf}})
if st != http.StatusSeeOther || loc != "/fleet/architecture" {
t.Fatalf("refresh = %d -> %q, want 303 back to the page:\n%s", st, loc, body)
}
if called != 1 {
t.Fatalf("pass ran %d times, want 1", called)
}
// And the page now reports the estate rather than one of the three
// missing states.
st, _, body = get(t, c, fx.ts.URL+"/fleet/architecture")
if st != 200 || !strings.Contains(body, "123456789012") {
t.Fatalf("page after refresh = %d, want the inventoried account:\n%s", st, body)
}
if strings.Contains(body, "No inventory has been taken yet") {
t.Errorf("page still claims no inventory after a successful refresh")
}
}
// The refresh door does NOT clear the auth latch: a deployment with no IAM
// grant gets the same honest answer however many times the button is
// pressed, rather than a fresh AWS attempt per click.
func TestArchitectureRefreshKeepsLatch(t *testing.T) {
fx := newConsoleFixture(t)
c := signInPaul(t, fx)
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
called := 0
fx.srv.inventory = func(ctx context.Context) (infraview.Inventory, error) {
called++
return infraview.Inventory{}, nil
}
fx.srv.infraDisabled.Store(true)
csrf := sessionToken(t, fx, c)
st, loc, body := postForm(t, c, fx.ts.URL+"/fleet/architecture/refresh", url.Values{"csrf": {csrf}})
if st != http.StatusSeeOther || loc != "/fleet/architecture" {
t.Fatalf("refresh = %d -> %q, want 303 back to the page:\n%s", st, loc, body)
}
if called != 0 {
t.Fatalf("a latched-off deployment must not call the provider (called %d)", called)
}
if !fx.srv.infraDisabled.Load() {
t.Errorf("the refresh door cleared the auth latch")
}
if fx.srv.infraRefreshing.Load() {
t.Errorf("the single-flight latch was left set after the door returned")
}
}
// The deployment bucket reaches the graph through Config, not the
// environment: a console configured with one draws the box -> bucket edge
// the spec mandates.
func TestArchitectureDrawsConfiguredBucket(t *testing.T) {
fx := newConsoleFixture(t)
c := signInPaul(t, fx)
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
fx.srv.cfg.Bucket = "carlos-deploy-fixture"
st, _, body := get(t, c, fx.ts.URL+"/fleet/architecture")
if st != 200 {
t.Fatalf("= %d, want 200", st)
}
if !strings.Contains(body, "carlos-deploy-fixture") {
t.Errorf("the configured deployment bucket is not drawn:\n%s", body)
}
}
// The page is two estates, both headed and both drawn — including on a
// deployment where the second one is empty, which is what most accounts
// are. A page that dropped the second heading when it had nothing under it
// would be the page quietly deciding the account holds nothing else, which
// is a claim rather than a read.
func TestArchitectureDrawsBothEstates(t *testing.T) {
fx := newConsoleFixture(t)
c := signInPaul(t, fx)
if err := fx.st.SetOperator("paul@keymail.dev", true); err != nil {
t.Fatal(err)
}
st, _, body := get(t, c, fx.ts.URL+"/fleet/architecture")
if st != 200 {
t.Fatalf("= %d, want 200", st)
}
if !strings.Contains(body, "
CARLOS
") {
t.Errorf("the CARLOS estate is not headed:\n%s", body)
}
if !strings.Contains(body, "
Also in this account
") {
t.Errorf("the second estate is not headed:\n%s", body)
}
// Estate-neutral wording: this console serves carloku, keymail and
// woodstar too, and "legacy" describes only Tito's own account.
if strings.Contains(body, "legacy") || strings.Contains(body, "Legacy") {
t.Errorf("the second bank must not be named for one deployment's history:\n%s", body)
}
// One diagram per REGION BAND, each under its own heading — not one per
// estate. That is what makes the page scroll down instead of across, so
// a page that had quietly gone back to one picture per estate would draw
// the same content and be the bug this shape exists to fix.
bands := strings.Count(body, `
`)
if bands < 2 {
t.Fatalf("page draws %d bands — a page back to one picture per estate is the bug this shape exists to fix:\n%s", bands, body)
}
if n := strings.Count(body, `class="archsvg"`); n != bands {
t.Errorf("page draws %d diagrams under %d band headings — every band has exactly one picture", n, bands)
}
// Every drawn picture fits the console's 64rem main column, which is the
// whole user-visible point: the page grows downwards, never sideways.
for _, w := range regexp.MustCompile(`