Skip to content

e2e: run Detox suite against local Worker + local D1 - #102

Closed
tpaulshippy wants to merge 1 commit into
mainfrom
fix/local-worker-e2e
Closed

tpaulshippy wants to merge 1 commit into
mainfrom
fix/local-worker-e2e

Conversation

@tpaulshippy

@tpaulshippy tpaulshippy commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Makes the Detox E2E suite runnable against a LOCAL Worker (wrangler dev + local D1) instead of local Django or prod workers.dev. DO NOT MERGE yet — orchestrator coordinates merge with the detox lane (they hold an untracked file at front/e2e/seedHelpers.js).

Runbook:
Terminal 1: cd worker && npx wrangler dev (needs worker/.dev.vars with JWT_SECRET — gitignored, local-only; then migrations + seed below)
Terminal 2: API_BASE_URL=http://127.0.0.1:8787/api npx detox test -c ios.sim.debug

What changed (2 new files, +567):

  • worker/scripts/seed-local-e2e.sql — inserts ONLY e2e-test-user (id 60) + e2e-teen-helper (id 61, maya@school.edu) with pbkdf2 testpassword123 hashes (cheap 10k-iter Django format, iteration-agnostic per crypto.ts), PIN-less bots_useraccount rows (sub level 0), plus one default bots_aimodel row (bootstrap/ensureBot need it). Idempotent (OR REPLACE/OR IGNORE). Load: wrangler d1 execute syft-db --local --file=./scripts/seed-local-e2e.sql
  • front/e2e/seedHelpers.js (CREATED fresh — absent on origin/main) — shared REST helpers plus D1-direct helpers (d1/resetPinLockoutState/resetOnboardingFresh/deleteAllChatsD1/ensureSubscriptionLevel) that shell out to wrangler d1 execute --local on 127.0.0.1:8787 bases, use Cloudflare D1 REST on workers.dev, and throw Django seed hints on localhost:8000.

Verification (no simulator; wrangler dev + migrations + seed loaded locally): node script logging in as both users, resetOnboardingFresh, ensurePin/reauth-wrong-then-right/resetPinLockoutState/ensureNoPin round-trip, deleteAllChatsD1, renames, deleteExtra helpers, getDefaultModelId, completeOnboarding, helper /login, Django-hint error — 19/19 PASS (one wrangler --json quirk: NULL TEXT reads back as string 'null'; harmless, worker treats it as unlocked).

Specs will need beyond seedHelpers: specs currently inline their own token/profile/bot fetching (no seedHelpers import on main) — adopting them to require('./seedHelpers') is the detox lane's call; helper PIN default is '1234'; teen-delegated minting (01 spec shells to Django venv) has no Worker equivalent yet — GET /api/login with the helper session returns delegated tokens only when a parent-owned profile binds maya@school.edu.


Summary by cubic

Makes the Detox E2E suite runnable against a local Worker (wrangler dev + local D1) instead of Django or prod workers.dev, via two new files: a Worker SQL seed and shared front-end helpers. Do not merge yet — the orchestrator coordinates with the detox lane, which holds an untracked copy of front/e2e/seedHelpers.js.

What changed

  • worker/scripts/seed-local-e2e.sql seeds only the two E2E users (ids 60/61, password testpassword123 via cheap 10k-iteration pbkdf2 hashes) with PIN-less accounts and one default AI model; re-runs are idempotent.
  • front/e2e/seedHelpers.js adds REST plus D1-direct helpers: local bases shell out to wrangler d1 execute --local, workers.dev bases use the Cloudflare D1 REST API, and Django bases throw a seed-command hint.
  • Verified without a simulator: 19/19 node-script checks pass against local wrangler dev; specs still inline their own token/profile/bot fetching, and adopting them to require('./seedHelpers') is the detox lane's call.

Migration (local runbook)

  • Terminal 1: cd worker && npx wrangler dev; first time, create worker/.dev.vars with a JWT_SECRET, apply local migrations, and load worker/scripts/seed-local-e2e.sql via npx wrangler d1 execute syft-db --local --file=./scripts/seed-local-e2e.sql.
  • Terminal 2: API_BASE_URL=http://127.0.0.1:8787/api npx detox test -c ios.sim.debug <spec>.

Written for commit ccae46e. Summary will update on new commits.

View guided diff Turn on auto-fix

- worker/scripts/seed-local-e2e.sql: seeds ONLY e2e users 60/61
  (testpassword123, pbkdf2 10k) + PIN-less accounts + default AI model.
  Load: wrangler d1 execute syft-db --local --file=./scripts/seed-local-e2e.sql
- front/e2e/seedHelpers.js: shared REST + D1-direct helpers. D1-direct
  helpers shell out to wrangler d1 execute --local on 127.0.0.1:8787
  bases, use Cloudflare D1 REST on workers.dev, and throw Django
  seed hints on localhost:8000.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="front/e2e/seedHelpers.js">

<violation number="1" location="front/e2e/seedHelpers.js:426">
P1: The manifest keys tokens by `API_BASE_URL`, but the app looks them up by `EXPO_PUBLIC_API_BASE_URL`. The documented local-worker invocation sets only `API_BASE_URL`, so the injected session is invisible unless the native app was separately built with the identical URL; make the helper use and validate the app's base URL, and document setting it for the Detox build.</violation>
</file>

<file name="worker/scripts/seed-local-e2e.sql">

<violation number="1" location="worker/scripts/seed-local-e2e.sql:35">
P2: `OR REPLACE` deletes each conflicting row before reinserting it. The schema cascades user deletion to profiles, bots, chats, and messages; account deletion also removes usage-limit rows. Rerunning this seed therefore erases E2E data; use `ON CONFLICT ... DO UPDATE` for both statements.</violation>

<violation number="2" location="worker/scripts/seed-local-e2e.sql:45">
P2: If local D1 already has another default model, this `OR IGNORE` is skipped by the `unique_default_model` partial index. Onboarding then assigns that existing model instead of the seeded Nova Micro; clear or update the old default before inserting.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread front/e2e/seedHelpers.js
function tokensManifest(tokens, opts = {}) {
const { profile = null, bot = null, initialRoute = null } = opts;
const manifest = {
tokens: JSON.stringify({ [apiBase()]: tokens }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The manifest keys tokens by API_BASE_URL, but the app looks them up by EXPO_PUBLIC_API_BASE_URL. The documented local-worker invocation sets only API_BASE_URL, so the injected session is invisible unless the native app was separately built with the identical URL; make the helper use and validate the app's base URL, and document setting it for the Detox build.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At front/e2e/seedHelpers.js, line 426:

<comment>The manifest keys tokens by `API_BASE_URL`, but the app looks them up by `EXPO_PUBLIC_API_BASE_URL`. The documented local-worker invocation sets only `API_BASE_URL`, so the injected session is invisible unless the native app was separately built with the identical URL; make the helper use and validate the app's base URL, and document setting it for the Detox build.</comment>

<file context>
@@ -0,0 +1,521 @@
+function tokensManifest(tokens, opts = {}) {
+  const { profile = null, bot = null, initialRoute = null } = opts;
+  const manifest = {
+    tokens: JSON.stringify({ [apiBase()]: tokens }),
+    e2eTestMode: 'true',
+  };
</file context>

(60, NULL, 0, NULL, 0, 'UTC', NULL),
(61, NULL, 0, NULL, 0, 'UTC', NULL);

INSERT OR IGNORE INTO bots_aimodel (model_id, name, input_token_cost, output_token_cost, is_default, supported_input_modalities, created_at, modified_at)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: If local D1 already has another default model, this OR IGNORE is skipped by the unique_default_model partial index. Onboarding then assigns that existing model instead of the seeded Nova Micro; clear or update the old default before inserting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At worker/scripts/seed-local-e2e.sql, line 45:

<comment>If local D1 already has another default model, this `OR IGNORE` is skipped by the `unique_default_model` partial index. Onboarding then assigns that existing model instead of the seeded Nova Micro; clear or update the old default before inserting.</comment>

<file context>
@@ -0,0 +1,46 @@
+  (60, NULL, 0, NULL, 0, 'UTC', NULL),
+  (61, NULL, 0, NULL, 0, 'UTC', NULL);
+
+INSERT OR IGNORE INTO bots_aimodel (model_id, name, input_token_cost, output_token_cost, is_default, supported_input_modalities, created_at, modified_at)
+VALUES ('us.amazon.nova-micro-v1:0', 'Nova Micro', 0.000035, 0.00014, 1, '["text"]', '2026-01-01T00:00:00.000Z', '2026-01-01T00:00:00.000Z');
</file context>
Suggested change
INSERT OR IGNORE INTO bots_aimodel (model_id, name, input_token_cost, output_token_cost, is_default, supported_input_modalities, created_at, modified_at)
UPDATE bots_aimodel SET is_default = 0 WHERE is_default = 1 AND model_id <> 'us.amazon.nova-micro-v1:0';
INSERT INTO bots_aimodel (model_id, name, input_token_cost, output_token_cost, is_default, supported_input_modalities, created_at, modified_at)
VALUES ('us.amazon.nova-micro-v1:0', 'Nova Micro', 0.000035, 0.00014, 1, '["text"]', '2026-01-01T00:00:00.000Z', '2026-01-01T00:00:00.000Z')
ON CONFLICT(model_id) DO UPDATE SET name = excluded.name, input_token_cost = excluded.input_token_cost, output_token_cost = excluded.output_token_cost, is_default = 1, supported_input_modalities = excluded.supported_input_modalities;

-- cd worker && npx wrangler d1 execute syft-db --local --file=./scripts/seed-local-e2e.sql
-- Idempotent: safe to re-run (INSERT OR REPLACE / OR IGNORE).

INSERT OR REPLACE INTO auth_user (id, password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: OR REPLACE deletes each conflicting row before reinserting it. The schema cascades user deletion to profiles, bots, chats, and messages; account deletion also removes usage-limit rows. Rerunning this seed therefore erases E2E data; use ON CONFLICT ... DO UPDATE for both statements.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At worker/scripts/seed-local-e2e.sql, line 35:

<comment>`OR REPLACE` deletes each conflicting row before reinserting it. The schema cascades user deletion to profiles, bots, chats, and messages; account deletion also removes usage-limit rows. Rerunning this seed therefore erases E2E data; use `ON CONFLICT ... DO UPDATE` for both statements.</comment>

<file context>
@@ -0,0 +1,46 @@
+--   cd worker && npx wrangler d1 execute syft-db --local --file=./scripts/seed-local-e2e.sql
+-- Idempotent: safe to re-run (INSERT OR REPLACE / OR IGNORE).
+
+INSERT OR REPLACE INTO auth_user (id, password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined)
+VALUES
+  (60, 'pbkdf2_sha256$10000$6WsxRJUeouaLspEo$tJ39t8OBwf/CeM9+7EZCU4d78HQC1fg9LaJGKzlpftE=', NULL, 0, 'e2e-test-user', '', '', '', 0, 1, '2026-01-01T00:00:00.000Z'),
</file context>

@tpaulshippy

Copy link
Copy Markdown
Owner Author

Superseded by #111, which consolidates this branch's seedHelpers.js (byte-identical) and seed SQL (plus a Nova Lite vision row) with the fixed Detox specs, verified 22/22 on a local worker + local D1 stack.

@tpaulshippy tpaulshippy closed this Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant