e2e test report · staging

Share Service — Live Endpoint Verification

Every valid create case from the PR contract suite (share.dto.spec.ts), executed for real against staging-api.phantom.app: OAuth login → POST /share → CockroachDB → public GET /share/:shareId.

env staging image backend/main-22 sha-5c9c8fe run 2026-07-31 15:17 PDT service share · team growth-engagement
15 / 15
cases passed — every create accepted (201) and read back publicly (200)
8
payload kinds covered — the full discriminated union
6
checks per case, incl. snapshot round-trip & spoof-stripping
90
assertions total against the live deployment
Auth — real gateway path, no bypass
P-256 keygen
Fresh ECDSA wallet key = brand-new staging user
login_hint JWT
ES256-signed, kid = raw public key (hydra load-test recipe)
/login/start + PKCE
staging-auth.phantom.app, S256 challenge, manual redirect follow
code → token
/oauth2/token exchange with verifier
Bearer → POST /share
Istio JWT policy + gateway userId forwarding exercised
Results — all assets create & read back
#CaseKindHTTPshareIdResult
1Plain token card
+ spoofed sharer injected — stripped by server
tokenPOST 201 · GET 200KzrtsZMSNBPASS
2Token + open spot positiontokenPOST 201 · GET 200btuokFwyJVPASS
3Token + closed spot positiontokenPOST 201 · GET 200V3VfS583QsPASS
4Plain perp market cardperpMarketPOST 201 · GET 200nUNvDRvDXyPASS
5Perp + open long 10x positionperpMarketPOST 201 · GET 200h7YhqoBaGIPASS
6Perp + closed short 5x positionperpMarketPOST 201 · GET 2000T1JjSKmtfPASS
7Prediction + open yes positionpredictionMarketPOST 201 · GET 200Go8qNxOmdnPASS
8Prediction + closed no positionpredictionMarketPOST 201 · GET 200eJ10v9YWLdPASS
9Open token position cardopenTokenPositionPOST 201 · GET 200A2iaxnzbVSPASS
10Open perp position cardopenPerpPositionPOST 201 · GET 200lEyqMJbGL5PASS
11Open prediction position cardopenPredictionPositionPOST 201 · GET 200Kk0QagipO3PASS
12Profile cardprofilePOST 201 · GET 200DXW29udVUxPASS
13Chat cardchatPOST 201 · GET 200bLaMoiGCKQPASS
14Token + price series (sparkline)tokenPOST 201 · GET 200AV2OwZdAKSPASS
15Open perp position + price seriesopenPerpPositionPOST 201 · GET 2004RmaHbImUKPASS
15 / 15 passed · 0 failed · shareIds link to the live public records
Checks applied to every case
POST returns 201
Create accepted with shareId + canonical phantom.com/ul/share/<id> URL.
Public GET returns 200
Anonymous read of the record — no auth header sent.
Payload round-trips exactly
Deep-equality of the stored snapshot vs. what was posted (key-order insensitive — JSONB normalizes).
Spoofed sharer stripped
Case 1 injected sharer: "spoofed-victim" in the body; the server discarded it (sharer: {} comes back).
Schema fields correct
v = 1 and deepLinkPath echoed verbatim on every record.
shareId format valid
All fifteen ids match [A-Za-z0-9]{10}.
Sample record — perp open long (h7YhqoBaGI)
GET https://staging-api.phantom.app/share/h7YhqoBaGI200 · cache-control: public, max-age=3600, immutable
{
  "shareId": "h7YhqoBaGI",
  "v": 1,
  "payload": {
    "kind": "perpMarket",
    "caip19": "hypercore:mainnet/perp:BTC",
    "priceUsd": "67450.50",
    "change24hPct": "-1.8",
    "position": {
      "kind": "open", "side": "long", "leverage": "10",
      "entryPriceUsd": "64200.00", "pnlUsd": "3250.50", "pnlPct": "5.06"
    }
  },
  "sharer": {},
  "deepLinkPath": "v1/perps/btc-usd",
  "createdAt": "2026-07-31T22:17:41.769Z"
}
Context — how staging got here today
12:43 — PR #29920 merges; first sync fails
The share service ships with three standard blocks missing (overlays written by hand, not from the template). PreSync migrate job dies immediately.
ERROR  DATABASE_URL environment variable is required
Fix 1 — infra#6851: provision the database
No share CRDB client cert, database, or role existed in either environment. Added to the Pulumi stacks; merged 13:37.
Fix 2 — backend#30560: wire DATABASE_URL
Added cockroachdb: {enabled: true, name: "share"} to both overlays. Merged 13:45 — migrate job succeeds, shares table created. The app pod then crash-loops on the next gap:
AccessDeniedException: eks-nodegroup-role … not authorized … secretsmanager:GetSecretValue on phantom-secrets
Fix 3 — backend#30566: run under its own service account
Added serviceAccount: {create: true, name: share}. Crossplane provisioned the IRSA role; the pod restarted with secrets access. Merged 14:28.
14:59 — backend-share Healthy · 15:17 — this e2e run: 15/15
Rollout green under the share service account. Production carries all three fixes, so it should come up cleanly first try.
Notes
First run showed 14 false failures — comparison bug in the harness, not the service
CockroachDB JSONB stores object keys alphabetically, and the initial round-trip check used key-order-sensitive JSON.stringify equality. Values were byte-identical. Switched to a canonicalizing deep-equal and everything passed.
30 test records were created on staging (2 runs × 15)
Share records are immutable by design and there is no delete endpoint yet. They are harmless schema-valid fixtures under two throwaway test users; clean up via SQL on the share staging database if desired.