package console // claim_invariant_probes_read_test.go is the probe table for every read: each // is a valid request for a resource that exists (staged where the plain world // has none), accepted only on the exact success status with the resource named // in the answer. A read that comes back 404 because the id was made up, 401 // because the bearer was missing, or a redirect to sign-in is not coverage. import ( "bytes" "context" "encoding/json" "net/http" "testing" "time" "github.com/carlosframework/platform/internal/geo" "github.com/carlosframework/platform/internal/geoip" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/reqrec" ) // claimPageRead is a browser GET of a page, the owner signed in. func claimPageRead(setup func(*testing.T, *claimCase), path string, want ...string) claimProbe { return claimProbe{setup: setup, do: claimPageReply(path), body: want} } // claimAPIRead is the GET the CLI sends, as the owner's bearer. func claimAPIRead(setup func(*testing.T, *claimCase), path string, want ...string) claimProbe { return claimProbe{setup: setup, do: claimGetAPI(path), body: want} } // claimRedirectRead is a GET whose answer is the one named redirect. func claimRedirectRead(path string, status int, loc string) claimProbe { return claimProbe{do: claimPageReply(path), status: status, loc: loc} } // ---- the worlds reads need ------------------------------------------------------- func claimWorldInstance(t *testing.T, cx *claimCase) { t.Helper() cx.instanceWorld(t) cx.instance(t, `{"host":"acme.go.tito.io","region":"ie"}`) } func claimWorldFleet(t *testing.T, cx *claimCase) { t.Helper(); cx.fleet(t) } func claimWorldMove(t *testing.T, cx *claimCase) { t.Helper(); cx.movableInstance(t) } func claimWorldAlias(t *testing.T, cx *claimCase) { t.Helper() cx.claimedAlias(t, "tickets.example.com") } func claimWorldDoorKey(t *testing.T, cx *claimCase) { t.Helper() cx.mustAPI(t, http.MethodPost, "/api/cli/apps/bab/titogo/door-keys", `{"label":"router"}`, http.StatusCreated) } func claimWorldLedger(t *testing.T, cx *claimCase) { t.Helper() cx.mustAPI(t, http.MethodPost, "/api/cli/apps/bab/titogo/ledgers/carbon/append", `{"payload":{"kg_central":1}}`, http.StatusOK) } func claimWorldSchedule(t *testing.T, cx *claimCase) { t.Helper() cx.mustAPI(t, http.MethodPost, "/api/cli/apps/bab/titogo/schedules", `{"name":"sync","every":"6h","path":"/jobs/sync"}`, http.StatusOK) } func claimWorldTag(t *testing.T, cx *claimCase) { t.Helper() body, err := json.Marshal(release.Tag{Schema: 1, Version: "0.1.0", Release: "v1", TaggedAt: time.Now().UTC(), Actor: claimFxOwner, Changelog: "census"}) if err != nil { t.Fatal(err) } if _, err := cx.rel.Put(context.Background(), release.TagKey("bab", "kass", "0.1.0"), body, ""); err != nil { t.Fatalf("stage a tag: %v", err) } } func claimWorldApply(t *testing.T, cx *claimCase) { t.Helper() cx.mustAPI(t, http.MethodPost, "/api/cli/accounts/bab/store/apply", `{"note":"census"}`, http.StatusOK) } func claimWorldGeoip(t *testing.T, cx *claimCase) { t.Helper() claimMustRunAlone(t, "claimWorldGeoip") withFixtureProbes(t) if _, err := cx.srv.geoip.Publish(context.Background(), bytes.NewReader(fixture(t, "GeoIP2-City-Test.mmdb")), geoipPublishOpts{Origin: "manual"}); err != nil { t.Fatalf("stage a published geoip database: %v", err) } if !cx.bucketHas(geoip.PointerKey) { t.Fatal("stage a published geoip database: no pointer") } } func claimInvitePageProbe() claimProbe { return claimProbe{ setup: claimInviteWorld, do: func(t *testing.T, cx *claimCase) claimReply { // The invitation is kim's: its page is kim's own signed-in browser. cx.setUser("kim@keymail.dev") c := newBrowser(t) signUp(t, cx.consoleFixture, c, "code-claim-kim-page") return cx.pageReplyAs(t, c, "/invite/") }, body: []string{"

Join "}, } } var claimReadProbes = map[string]claimProbe{ // ---- methodless ---- "route /api/economics/backhaul": claimAPIRead(nil, "/api/economics/backhaul", `"period"`, `"accounts"`), "route /fleet/architecture": claimPageRead(nil, "/fleet/architecture", "

Architecture

"), // ---- browser pages ---- "route GET /a/{sqid}/apps/new": claimPageRead(nil, "/a/bab/apps/new", `action="/a/bab/apps/new"`), "route GET /a/{sqid}/apps/{app}": claimPageRead(nil, "/a/bab/apps/kass", "kass"), "route GET /a/{sqid}/apps/{app}/account-review": claimPageRead(nil, "/a/bab/apps/kass/account-review", "kass"), "route GET /a/{sqid}/apps/{app}/activity": claimPageRead(nil, "/a/bab/apps/kass/activity", "kass"), "route GET /a/{sqid}/apps/{app}/analytics": claimPageRead(nil, "/a/bab/apps/titogo/analytics", "titogo"), "route GET /a/{sqid}/apps/{app}/availability": claimPageRead(nil, "/a/bab/apps/titogo/availability", "titogo"), "route GET /a/{sqid}/apps/{app}/deployments": claimPageRead(nil, "/a/bab/apps/kass/deployments", "v1"), "route GET /a/{sqid}/apps/{app}/errors": claimPageRead(nil, "/a/bab/apps/titogo/errors", "titogo"), "route GET /a/{sqid}/apps/{app}/instances": claimPageRead(nil, "/a/bab/apps/kass/instances", "kass.carlosapp.dev"), "route GET /a/{sqid}/apps/{app}/instances/{host}": claimPageRead(nil, "/a/bab/apps/kass/instances/kass.carlosapp.dev", "kass.carlosapp.dev"), "route GET /a/{sqid}/apps/{app}/logs": claimPageRead(nil, "/a/bab/apps/titogo/logs", "titogo"), "route GET /a/{sqid}/apps/{app}/logs/data": claimPageRead(nil, "/a/bab/apps/titogo/logs/data", `"lines"`), "route GET /a/{sqid}/apps/{app}/pipeline": claimPageRead(nil, "/a/bab/apps/titogo/pipeline", "titogo"), "route GET /a/{sqid}/apps/{app}/releases": claimPageRead(nil, "/a/bab/apps/kass/releases", "v1"), "route GET /a/{sqid}/apps/{app}/settings": claimPageRead(nil, "/a/bab/apps/titogo/settings", "titogo"), "route GET /a/{sqid}/apps/{app}/settings/{section}": claimPageRead(nil, "/a/bab/apps/titogo/settings/advanced", "/apps/titogo/control-plane/set"), "route GET /a/{sqid}/fleet": claimPageRead(claimWorldFleet, "/a/bab/fleet", "pi"), "route GET /a/{sqid}/fleet/byo": claimPageRead(nil, "/a/bab/fleet/byo", "

Bring your own fleet

"), "route GET /a/{sqid}/fleets/{fleet}": claimPageRead(claimWorldFleet, "/a/bab/fleets/pi", "

pi

"), "route GET /a/{sqid}/settings": claimPageRead(nil, "/a/bab/settings", "

Account settings

"), "route GET /a/{sqid}/team": claimPageRead(nil, "/a/bab/team", "

Team

", claimFxOwner), "route GET /a/{sqid}/trash": claimPageRead(func(t *testing.T, cx *claimCase) { cx.trash(t, "titogo") }, "/a/bab/trash", "

Trash

", "titogo"), "route GET /a/{sqid}/{$}": claimPageRead(nil, "/a/bab/", "kass"), "route GET /accounts/new": claimPageRead(nil, "/accounts/new", "

New account

"), "route GET /settings": claimPageRead(nil, "/settings", "

Your settings

"), "route GET /auth/cli": claimPageRead(nil, "/auth/cli", "

Approve a terminal

"), "route GET /invite/{token}": claimInvitePageProbe(), "route GET /fleet": claimPageRead(nil, "/fleet", "

Fleet status

"), "route GET /fleet/economics": claimPageRead(nil, "/fleet/economics", "

Fleet economics

"), "route GET /fleet/economics/accounts": claimPageRead(nil, "/fleet/economics/accounts", "

Fleet economics: accounts

"), "route GET /fleet/metrics": claimPageRead(nil, "/fleet/metrics", "

Fleet metrics

"), "route GET /fleet/store": claimPageRead(claimWorldApply, "/fleet/store", "

Object storage applications

"), "route GET /healthz": claimPageRead(nil, "/healthz", "ok"), // ---- redirects ---- "route GET /apps": claimRedirectRead("/apps", http.StatusFound, "/a/"), "route GET /apps/{app}": claimRedirectRead("/apps/kass", http.StatusFound, "/a/"), "route GET /auth/cli/tokens": claimRedirectRead("/auth/cli/tokens", http.StatusSeeOther, "/settings"), "route GET /{$}": claimRedirectRead("/", http.StatusFound, "/a/"), // ---- the CLI's reads ---- "route GET /api/cli/apps": claimAPIRead(nil, "/api/cli/apps", `"sqid":"bab"`, `"kass"`), "route GET /api/cli/apps/{account}/list": claimAPIRead(nil, "/api/cli/apps/bab/list", `"kass"`), "route GET /api/cli/apps/{account}/{app}/analytics": claimAPIRead(nil, "/api/cli/apps/bab/titogo/analytics", `"pv"`), "route GET /api/cli/apps/{account}/{app}/availability": claimAPIRead(nil, "/api/cli/apps/bab/titogo/availability", `"groups"`), "route GET /api/cli/apps/{account}/{app}/channels": claimAPIRead(nil, "/api/cli/apps/bab/kass/channels", `"stable"`), "route GET /api/cli/apps/{account}/{app}/config": claimAPIRead(nil, "/api/cli/apps/bab/titogo/config", `"bundle"`), "route GET /api/cli/apps/{account}/{app}/db/{host}/ltx/{level}": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.dbWorld(t) }, "/api/cli/apps/bab/titogo/db/"+claimDBHost+"/ltx/0", `"files"`, `"handle"`, `"min_txid":1`), "route GET /api/cli/apps/{account}/{app}/domains": claimAPIRead(claimWorldAlias, "/api/cli/apps/bab/titogo/domains", "tickets.example.com"), "route GET /api/cli/apps/{account}/{app}/domains/{host}": claimAPIRead(claimWorldAlias, "/api/cli/apps/bab/titogo/domains/tickets.example.com", "tickets.example.com"), "route GET /api/cli/apps/{account}/{app}/door-keys": claimAPIRead(claimWorldDoorKey, "/api/cli/apps/bab/titogo/door-keys", `"label":"router"`), "route GET /api/cli/apps/{account}/{app}/email": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.emailDeclared(t) }, "/api/cli/apps/bab/titogo/email", claimMailDomain), "route GET /api/cli/apps/{account}/{app}/email/events": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.emailProvisioned(t) }, "/api/cli/apps/bab/titogo/email/events", `"events"`), "route GET /api/cli/apps/{account}/{app}/email/events/push": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.emailPush(t) }, "/api/cli/apps/bab/titogo/email/events/push", "/mail/events"), // An unsealed code needs no mesh key to decode, so the probe answers 200 // without seeding one; the record is simply not found. "route GET /api/cli/apps/{account}/{app}/edge-refs/{code}": claimAPIRead(nil, "/api/cli/apps/bab/titogo/edge-refs/"+reqrec.NewRef(time.Now(), reqrec.TierProbe, "probe-paths", "box1", nil), `"tier":"probe"`), "route GET /api/cli/apps/{account}/{app}/errors": claimAPIRead(nil, "/api/cli/apps/bab/titogo/errors", `"log_level"`), "route GET /api/cli/apps/{account}/{app}/features": claimAPIRead(nil, "/api/cli/apps/bab/titogo/features", `"features"`), "route GET /api/cli/apps/{account}/{app}/geo": claimAPIRead(func(t *testing.T, cx *claimCase) { db, err := geo.OpenMMDB("../geo/testdata/GeoIP2-City-Test.mmdb") if err != nil { t.Fatal(err) } sw := geo.NewSwappable() sw.Swap(db) t.Cleanup(func() { sw.Swap(nil) }) cx.srv.cfg.GeoIPReader = sw }, "/api/cli/apps/bab/titogo/geo?ip=81.2.69.142", `"found":true`, `"country":"GB"`), "route GET /api/cli/apps/{account}/{app}/instances": claimAPIRead(claimWorldInstance, "/api/cli/apps/bab/titogo/instances", "acme.go.tito.io"), "route GET /api/cli/apps/{account}/{app}/instances/{host}": claimAPIRead(claimWorldInstance, "/api/cli/apps/bab/titogo/instances/acme.go.tito.io", "acme.go.tito.io"), "route GET /api/cli/apps/{account}/{app}/instances/{host}/move": claimAPIRead(claimWorldMove, "/api/cli/apps/bab/titogo/instances/acme.go.tito.io/move", `"state":"none"`), "route GET /api/cli/apps/{account}/{app}/instances/{host}/updates": claimAPIRead(claimWorldInstance, "/api/cli/apps/bab/titogo/instances/acme.go.tito.io/updates", `"host":"acme.go.tito.io"`, `"updates":"auto"`), "route GET /api/cli/apps/{account}/{app}/ledgers": claimAPIRead(claimWorldLedger, "/api/cli/apps/bab/titogo/ledgers", `"carbon"`), "route GET /api/cli/apps/{account}/{app}/ledgers/{ledger}/head": claimAPIRead(claimWorldLedger, "/api/cli/apps/bab/titogo/ledgers/carbon/head", `"head_sha256"`), "route GET /api/cli/apps/{account}/{app}/logs": claimAPIRead(nil, "/api/cli/apps/bab/titogo/logs", `"records"`), "route GET /api/cli/apps/{account}/{app}/pending": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.proposal(t) }, "/api/cli/apps/bab/titogo/pending", ""), "route GET /api/cli/apps/{account}/{app}/pipeline": claimAPIRead(nil, "/api/cli/apps/bab/titogo/pipeline", `"pipeline"`), "route GET /api/cli/apps/{account}/{app}/regions": claimAPIRead(nil, "/api/cli/apps/bab/titogo/regions", `"regions"`), "route GET /api/cli/apps/{account}/{app}/relay": claimSerialProbe("relayEnabled stages the region's relay master through t.Setenv: process environment", claimAPIRead(func(t *testing.T, cx *claimCase) { cx.relayEnabled(t) }, "/api/cli/apps/bab/titogo/relay", `"app":"titogo"`, `"enabled":true`)), "route GET /api/cli/apps/{account}/{app}/releases": claimAPIRead(nil, "/api/cli/apps/bab/kass/releases", `"version":"v1"`), "route GET /api/cli/apps/{account}/{app}/releases/{version}/authorize": claimAPIRead(nil, "/api/cli/apps/bab/kass/releases/v1/authorize", `"version":"v1"`, `"manifest_digest":"`), "route GET /api/cli/apps/{account}/{app}/retention": claimAPIRead(nil, "/api/cli/apps/bab/titogo/retention", `"keep"`), "route GET /api/cli/apps/{account}/{app}/routes": claimAPIRead(nil, "/api/cli/apps/bab/kass/routes", `"kass.carlosapp.dev"`), "route GET /api/cli/apps/{account}/{app}/schedules": claimAPIRead(claimWorldSchedule, "/api/cli/apps/bab/titogo/schedules", `"sync"`), "route GET /api/cli/apps/{account}/{app}/sidecars": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.sidecar(t) }, "/api/cli/apps/bab/titogo/sidecars", `"bot"`), "route GET /api/cli/apps/{account}/{app}/sidecars/{name}": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.sidecar(t) }, "/api/cli/apps/bab/titogo/sidecars/bot", `"bot"`), "route GET /api/cli/apps/{account}/{app}/store": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.storeGranted(t) }, "/api/cli/apps/bab/titogo/store", `"app":"titogo"`, `"entitlement"`), "route GET /api/cli/apps/{account}/{app}/store/scan": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.scanGranted(t) }, "/api/cli/apps/bab/titogo/store/scan", `"app":"titogo"`, `"scanning"`), "route GET /api/cli/apps/{account}/{app}/store/scan/object": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.scanObjectStaged(t) cx.mustAPI(t, http.MethodPost, "/api/cli/apps/bab/titogo/store/scan/sweep", `{}`, http.StatusOK) }, "/api/cli/apps/bab/titogo/store/scan/object?key=claim-fixture.txt", `"state":"clean"`), "route GET /api/cli/apps/{account}/{app}/tags": claimAPIRead(claimWorldTag, "/api/cli/apps/bab/kass/tags", `"version":"0.1.0"`), "route GET /api/cli/apps/{account}/{app}/tags/{version}": claimAPIRead(claimWorldTag, "/api/cli/apps/bab/kass/tags/0.1.0", `"version":"0.1.0"`, `"changelog":"census"`), "route GET /api/cli/apps/{account}/{app}/version-target": claimAPIRead(nil, "/api/cli/apps/bab/titogo/version-target", `"target"`), "route GET /api/cli/fleets/{account}": claimAPIRead(claimWorldFleet, "/api/cli/fleets/bab", `"pi"`, `"pi-fleet-data"`), "route GET /api/cli/services/{account}": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.service(t) }, "/api/cli/services/bab", `"svc-a"`), "route GET /api/cli/status/instances": claimAPIRead(nil, "/api/cli/status/instances", `"instances"`), "route GET /api/cli/status/layout": claimAPIRead(nil, "/api/cli/status/layout", `"boxes"`), "route GET /api/cli/store/approvals": claimAPIRead(claimWorldApply, "/api/cli/store/approvals", `"applications"`, claimFxOwner), "route GET /api/cli/accounts/{account}/plan": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.srv.cfg.PlanCatalog = examplePlanCatalog(t) }, "/api/cli/accounts/bab/plan", `"plan":"free"`), "route GET /api/cli/system/antivirus": claimAPIRead(nil, "/api/cli/system/antivirus", `"wired":true`), "route GET /api/cli/system/geoip": func() claimProbe { p := claimAPIRead(claimWorldGeoip, "/api/cli/system/geoip", `"pointer"`, "GeoIP2-City") p.serial = "claimWorldGeoip swaps the package variable geoipProbeAddrs (withFixtureProbes)" return p }(), "route GET /api/cli/system/jobs/{id}": { setup: func(t *testing.T, cx *claimCase) { cx.vars["job"] = cx.systemRelease(t, "canary", "66587fa") }, do: claimGetAPI("/api/cli/system/jobs/"), body: []string{`"steps"`, `"done":true`}, }, "route GET /api/cli/system/layout": claimAPIRead(nil, "/api/cli/system/layout", `"mode":"none"`), "route GET /api/cli/system/pointer-digests": claimAPIRead(nil, "/api/cli/system/pointer-digests", `"account":"bab"`, `"app":"kass","channel":"stable"`), "route GET /api/cli/system/status": claimAPIRead(func(t *testing.T, cx *claimCase) { cx.systemRelease(t, "canary", "66587fa") }, "/api/cli/system/status", `"pointers"`, `"version":"66587fa"`), "route GET /auth/cli/whoami": claimAPIRead(nil, "/auth/cli/whoami", `"address":"`+claimFxOwner+`"`), // ---- POSTs that only read ---- "route POST /a/{sqid}/apps/new/region": { do: claimFormReply("/a/bab/apps/new/region", "tz", "Europe/Dublin"), body: []string{`"code"`, `"source"`}, }, "route POST /api/cli/apps/{account}/{app}/regions/guess": { do: claimAPIReply(http.MethodPost, "/api/cli/apps/bab/titogo/regions/guess", `{"tz":"Europe/Dublin"}`), body: []string{`"guess"`}, }, "route POST /api/cli/ops/hosts/{host}/resolve/inspect": { setup: func(t *testing.T, cx *claimCase) { cx.hostLossWorld(t) }, do: claimAPIReply(http.MethodPost, "/api/cli/ops/hosts/"+claimLossHost+"/resolve/inspect", `{"action":"declare-lost","box":"`+claimLossBox+`"}`), body: []string{`"challenge"`, `"details"`, `"` + claimLossBox + `"`}, }, }