SC-FG7 (#2197, epic #492). Last updated: 2026-05-16 UTC.
The founders gate limits signup to a fixed cohort size. When the cohort fills
(threshold=250 Founder seats) the signup form and landing page CTAs are hidden;
visitors see a waitlist CTA instead. The gate is controlled by FLAG_FOUNDERS_GATE
and is fail-open: any Queue outage allows signup to proceed.
This document describes the full test matrix, file locations, and how to run each suite locally.
| File | Scope |
|---|---|
backend_v2/tests/test_founders_gate_fg7_2197.py |
SC-FG7 combined integration tests: threshold transition, fail-open, flag OFF |
backend_v2/tests/test_founders_gate_sc_fg2_2192.py |
SC-FG2 unit + integration: founders_gate_service + POST /api/auth/register/options |
backend_v2/tests/test_founders_gate_state_sc_fg3_2193.py |
SC-FG3 unit + integration: GET /api/auth/founders-gate-state + rate limit |
frontend/trademaster_ui/)| File | Scope |
|---|---|
src/pages/Signup/__tests__/FoundersGateFG7.test.js |
SC-FG7: threshold boundary + 60s poll transition + fail-open (SignupPage) |
src/pages/Signup/__tests__/SignupPage.test.js (AC-FG describe block) |
SC-FG4: gate hide/show on SignupPage |
src/pages/Signup/__tests__/FoundersGateClosed.test.js |
SC-FG4: FoundersGateClosed component render |
src/api/__tests__/foundersGateAPI.test.js |
SC-FG4: foundersGateAPI client unit tests |
frontend/getraxx-landing/)| File | Scope |
|---|---|
src/__tests__/HeroSectionFoundersGate.test.jsx |
SC-FG5: HeroSection CTA hide/show |
src/__tests__/useFoundersGate.test.js |
SC-FG5: useFoundersGate hook unit tests |
| ID | Scenario | Count | Expected — signup (POST) | Expected — probe (GET) | Test file(s) |
|---|---|---|---|---|---|
| T1 | Last open seat | 249 | 202 | {"open": true} |
test_founders_gate_fg7_2197.py::TestThresholdTransition::test_T1*, FoundersGateFG7.test.js::FG7-T1 |
| T2 | Cohort full | 250 | 403 founders_gate_closed |
{"open": false} |
test_founders_gate_fg7_2197.py::TestThresholdTransition::test_T2*, FoundersGateFG7.test.js::FG7-T2 |
| T3 | Mid-session open→closed | — | Cache expires, new Queue call, both endpoints reflect closed | test_founders_gate_fg7_2197.py::TestThresholdTransition::test_T3*, FoundersGateFG7.test.js::FG7-T3 |
|
| T4 | Mid-session closed→open | — | Second poll restores open state | FoundersGateFG7.test.js::FG7-T4 |
| ID | Failure mode | Expected — signup | Expected — probe | Test file(s) |
|---|---|---|---|---|
| F1 | URLError / timeout | 202 | {"open": true} |
test_founders_gate_fg7_2197.py::TestFailOpenOnQueueOutage::test_F1*, FoundersGateFG7.test.js::FG7-F1 |
| F2 | HTTPError 5xx | 202 | {"open": true} |
test_founders_gate_fg7_2197.py::TestFailOpenOnQueueOutage::test_F2*, FoundersGateFG7.test.js::FG7-F2 |
| F3 | Missing QUEUE_INTERNAL_BASE_URL | 202 | — | test_founders_gate_fg7_2197.py::TestFailOpenOnQueueOutage::test_queue_missing_base_url_signup_returns_202 |
| ID | Scenario | Expected — signup | Expected — probe | Queue calls |
|---|---|---|---|---|
| O1 | FLAG_FOUNDERS_GATE=false |
202 | {"open": true} |
0 |
Test file: test_founders_gate_fg7_2197.py::TestFlagOff, FoundersGateFG7.test.js::FG7-O1
| ID | Invariant | Test file |
|---|---|---|
| S1 | Probe body is exactly {"open": <bool>} — no count/threshold |
test_founders_gate_fg7_2197.py::TestResponseShapeInvariants::test_probe_response_contains_only_open_field |
| S2 | Probe never exposes count or threshold (ADR-0094 info-leak avoidance) | test_founders_gate_fg7_2197.py::TestResponseShapeInvariants::test_probe_response_does_not_expose_count_or_threshold |
| S3 | Signup 403 body has error, message, waitlist_url |
test_founders_gate_fg7_2197.py::TestResponseShapeInvariants::test_signup_403_body_has_required_fields |
| S4 | waitlist_url in 403 matches FOUNDERS_WAITLIST_URL env var |
test_founders_gate_fg7_2197.py::TestResponseShapeInvariants::test_signup_403_waitlist_url_matches_env_var |
python -m pytest backend_v2/tests/test_founders_gate_fg7_2197.py \
backend_v2/tests/test_founders_gate_sc_fg2_2192.py \
backend_v2/tests/test_founders_gate_state_sc_fg3_2193.py \
-v
Run only SC-FG7:
python -m pytest backend_v2/tests/test_founders_gate_fg7_2197.py -v
Run just the fail-open class:
python -m pytest backend_v2/tests/test_founders_gate_fg7_2197.py::TestFailOpenOnQueueOutage -v
npm --prefix frontend/trademaster_ui run test:auth
Or run only the FG7 file:
npm --prefix frontend/trademaster_ui test -- --watchAll=false \
src/pages/Signup/__tests__/FoundersGateFG7.test.js
npm --prefix frontend/getraxx-landing test -- --watchAll=false \
src/__tests__/HeroSectionFoundersGate.test.jsx \
src/__tests__/useFoundersGate.test.js
./scripts/ci/run_smoke.sh
FLAG_FOUNDERS_GATE (defined in backend_v2/api/feature_flags.yaml).
falseheroku config:set FLAG_FOUNDERS_GATE=1 --app raxx-api-prod >/dev/null 2>&1When false: gate is bypassed entirely. Queue is never called. Signup always
allowed. Probe always returns {"open": true}.
These tests use mocks — no real Queue DB is needed. The urlopen patch injects
the desired count/threshold/open values directly into the service. If you
need a live-stack test with a real Queue, seed the founder_cohort_members table
in Queue's database to 249 or 250 rows and point QUEUE_INTERNAL_BASE_URL at the
live Queue instance.