@fuzdev/fuz_app 0.64.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/actions/CLAUDE.md +513 -928
  2. package/dist/actions/broadcast_api.d.ts +1 -1
  3. package/dist/actions/broadcast_api.js +1 -1
  4. package/dist/actions/cancel.d.ts +2 -2
  5. package/dist/actions/cancel.js +3 -3
  6. package/dist/actions/connection_closer.d.ts +1 -4
  7. package/dist/actions/connection_closer.d.ts.map +1 -1
  8. package/dist/actions/connection_closer.js +1 -4
  9. package/dist/actions/register_action_ws.d.ts +2 -2
  10. package/dist/actions/register_ws_endpoint.d.ts +1 -1
  11. package/dist/actions/transports_ws_auth_guard.d.ts +1 -2
  12. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  13. package/dist/actions/transports_ws_auth_guard.js +1 -2
  14. package/dist/auth/CLAUDE.md +591 -1871
  15. package/dist/auth/account_schema.d.ts +1 -1
  16. package/dist/auth/account_schema.d.ts.map +1 -1
  17. package/dist/auth/api_token_queries.js +1 -1
  18. package/dist/auth/audit_log_ddl.d.ts +1 -1
  19. package/dist/auth/audit_log_ddl.d.ts.map +1 -1
  20. package/dist/auth/audit_log_ddl.js +1 -1
  21. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  22. package/dist/auth/bootstrap_account.js +1 -5
  23. package/dist/auth/bootstrap_routes.d.ts +7 -1
  24. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  25. package/dist/auth/bootstrap_routes.js +15 -11
  26. package/dist/auth/keyring.d.ts +6 -6
  27. package/dist/auth/keyring.js +8 -8
  28. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  29. package/dist/auth/role_grant_offer_actions.js +4 -2
  30. package/dist/db/create_db.d.ts.map +1 -1
  31. package/dist/db/create_db.js +13 -0
  32. package/dist/dev/setup.d.ts +2 -2
  33. package/dist/dev/setup.js +3 -3
  34. package/dist/http/CLAUDE.md +224 -498
  35. package/dist/http/error_schemas.d.ts +0 -4
  36. package/dist/http/error_schemas.d.ts.map +1 -1
  37. package/dist/http/error_schemas.js +0 -4
  38. package/dist/http/ip_canonical.d.ts +5 -4
  39. package/dist/http/ip_canonical.d.ts.map +1 -1
  40. package/dist/http/ip_canonical.js +8 -4
  41. package/dist/http/origin.d.ts +1 -1
  42. package/dist/http/origin.js +1 -1
  43. package/dist/runtime/mock.js +1 -1
  44. package/dist/server/app_server.d.ts +41 -10
  45. package/dist/server/app_server.d.ts.map +1 -1
  46. package/dist/server/app_server.js +10 -4
  47. package/dist/server/env.d.ts +7 -7
  48. package/dist/server/env.d.ts.map +1 -1
  49. package/dist/server/env.js +14 -14
  50. package/dist/server/static.d.ts +4 -4
  51. package/dist/server/static.js +7 -7
  52. package/dist/testing/CLAUDE.md +220 -46
  53. package/dist/testing/admin_integration.d.ts +18 -23
  54. package/dist/testing/admin_integration.d.ts.map +1 -1
  55. package/dist/testing/admin_integration.js +159 -201
  56. package/dist/testing/app_server.d.ts +125 -38
  57. package/dist/testing/app_server.d.ts.map +1 -1
  58. package/dist/testing/app_server.js +140 -42
  59. package/dist/testing/audit_completeness.d.ts +23 -22
  60. package/dist/testing/audit_completeness.d.ts.map +1 -1
  61. package/dist/testing/audit_completeness.js +199 -156
  62. package/dist/testing/bootstrap_success.d.ts +28 -0
  63. package/dist/testing/bootstrap_success.d.ts.map +1 -0
  64. package/dist/testing/bootstrap_success.js +144 -0
  65. package/dist/testing/cross_backend/capabilities.d.ts +64 -0
  66. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -0
  67. package/dist/testing/cross_backend/capabilities.js +47 -0
  68. package/dist/testing/cross_backend/setup.d.ts +215 -0
  69. package/dist/testing/cross_backend/setup.d.ts.map +1 -0
  70. package/dist/testing/cross_backend/setup.js +101 -0
  71. package/dist/testing/data_exposure.d.ts +14 -15
  72. package/dist/testing/data_exposure.d.ts.map +1 -1
  73. package/dist/testing/data_exposure.js +127 -146
  74. package/dist/testing/db_entities.d.ts +11 -1
  75. package/dist/testing/db_entities.d.ts.map +1 -1
  76. package/dist/testing/db_entities.js +13 -1
  77. package/dist/testing/integration.d.ts +35 -21
  78. package/dist/testing/integration.d.ts.map +1 -1
  79. package/dist/testing/integration.js +231 -291
  80. package/dist/testing/integration_helpers.d.ts +16 -6
  81. package/dist/testing/integration_helpers.d.ts.map +1 -1
  82. package/dist/testing/integration_helpers.js +7 -7
  83. package/dist/testing/mock_fs.d.ts.map +1 -1
  84. package/dist/testing/mock_fs.js +0 -2
  85. package/dist/testing/rate_limiting.d.ts.map +1 -1
  86. package/dist/testing/rate_limiting.js +9 -0
  87. package/dist/testing/role_grant_helpers.d.ts +31 -0
  88. package/dist/testing/role_grant_helpers.d.ts.map +1 -0
  89. package/dist/testing/role_grant_helpers.js +46 -0
  90. package/dist/testing/round_trip.d.ts +21 -16
  91. package/dist/testing/round_trip.d.ts.map +1 -1
  92. package/dist/testing/round_trip.js +65 -86
  93. package/dist/testing/rpc_round_trip.d.ts +24 -21
  94. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  95. package/dist/testing/rpc_round_trip.js +91 -104
  96. package/dist/testing/schema_introspect.d.ts +106 -0
  97. package/dist/testing/schema_introspect.d.ts.map +1 -0
  98. package/dist/testing/schema_introspect.js +123 -0
  99. package/dist/testing/schema_parity.d.ts +144 -0
  100. package/dist/testing/schema_parity.d.ts.map +1 -0
  101. package/dist/testing/schema_parity.js +233 -0
  102. package/dist/testing/standard.d.ts +57 -25
  103. package/dist/testing/standard.d.ts.map +1 -1
  104. package/dist/testing/standard.js +62 -5
  105. package/dist/testing/stubs.d.ts +11 -3
  106. package/dist/testing/stubs.d.ts.map +1 -1
  107. package/dist/testing/stubs.js +24 -21
  108. package/dist/testing/transports/surface_source.d.ts +51 -0
  109. package/dist/testing/transports/surface_source.d.ts.map +1 -0
  110. package/dist/testing/transports/surface_source.js +19 -0
  111. package/package.json +4 -4
@@ -28,18 +28,15 @@ import './assert_dev_env.js';
28
28
  import { describe, test, assert, afterAll } from 'vitest';
29
29
  import { ROLE_KEEPER, ROLE_ADMIN } from '../auth/role_schema.js';
30
30
  import { GRANT_PATH_ADMIN } from '../auth/grant_path_schema.js';
31
- import { auth_migration_ns } from '../auth/migrations.js';
32
- import { create_test_app } from './app_server.js';
33
- import { create_pglite_factory, create_describe_db, auth_integration_truncate_tables, } from './db.js';
31
+ import {} from './app_server.js';
32
+ import { create_test_role_grant_direct } from './db_entities.js';
33
+ import { role_grant_offer_and_accept } from './role_grant_helpers.js';
34
34
  import { find_auth_route } from './integration_helpers.js';
35
- import { run_migrations } from '../db/migrate.js';
36
35
  import { ErrorCoverageCollector, assert_error_coverage, DEFAULT_INTEGRATION_ERROR_COVERAGE, } from './error_coverage.js';
37
36
  import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
38
37
  import { role_grant_offer_create_action_spec, role_grant_revoke_action_spec, } from '../auth/role_grant_offer_action_specs.js';
39
38
  import { admin_account_list_action_spec, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec, } from '../auth/admin_action_specs.js';
40
39
  import { account_token_create_action_spec, account_verify_action_spec, } from '../auth/account_action_specs.js';
41
- import { query_create_role_grant } from '../auth/role_grant_queries.js';
42
- import { query_accept_offer } from '../auth/role_grant_offer_queries.js';
43
40
  /**
44
41
  * Pick a role for admin-grant testing, preferring a non-admin app-defined
45
42
  * role whose `RoleSpec.grant_paths` includes `'admin'` (the
@@ -53,17 +50,6 @@ const pick_grantable_role = (role_specs) => {
53
50
  }
54
51
  return ROLE_ADMIN; // fallback
55
52
  };
56
- /**
57
- * Build `CreateTestAppOptions` from admin test options plus a database and roles.
58
- */
59
- const build_admin_test_app_options = (options, db, roles) => ({
60
- session_options: options.session_options,
61
- create_route_specs: options.create_route_specs,
62
- db,
63
- roles: roles ?? [ROLE_KEEPER, ROLE_ADMIN],
64
- rpc_endpoints: options.rpc_endpoints,
65
- app_options: options.app_options,
66
- });
67
53
  /**
68
54
  * Standard admin integration test suite for fuz_app admin routes.
69
55
  *
@@ -79,56 +65,44 @@ const build_admin_test_app_options = (options, db, roles) => ({
79
65
  * see a clear setup error rather than `method not found` mid-suite.
80
66
  */
81
67
  export const describe_standard_admin_integration_tests = (options) => {
68
+ if (options.surface_source.kind !== 'inline') {
69
+ throw new Error("describe_standard_admin_integration_tests requires surface_source.kind === 'inline' — " +
70
+ 'the cross-process snapshot variant lands with the spawned-backend transport');
71
+ }
72
+ const route_specs = options.surface_source.spec.route_specs;
82
73
  // Hard-fail early so consumers see a clear setup error instead of a
83
- // confusing test failure when `rpc_endpoints` is missing. Factory-form
84
- // callers are resolved with a stub ctx purely to extract the endpoint
85
- // path; real handlers run per-test via the top-level `rpc_endpoints`
86
- // slot on `CreateTestAppOptions`.
74
+ // confusing test failure when `rpc_endpoints` is missing.
87
75
  const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
88
76
  const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
89
- const init_schema = async (db) => {
90
- await run_migrations(db, [auth_migration_ns]);
91
- };
92
- const factories = options.db_factories ?? [create_pglite_factory(init_schema)];
93
- const describe_db = create_describe_db(factories, auth_integration_truncate_tables);
94
- describe_db('standard_admin_integration', (get_db) => {
77
+ void options.capabilities;
78
+ describe('standard_admin_integration', () => {
95
79
  const { cookie_name } = options.session_options;
96
80
  const { role_specs } = options.roles;
97
81
  const grantable_role = pick_grantable_role(role_specs);
98
82
  // Error coverage tracking across test groups
99
83
  const error_collector = new ErrorCoverageCollector();
100
- let captured_route_specs = null;
101
84
  afterAll(() => {
102
- if (captured_route_specs) {
103
- // Scope coverage to admin auth-related routes. Account listing,
104
- // session/token revoke-all, audit-log reads, and invite CRUD all
105
- // live on the RPC surface; the only admin REST route remaining
106
- // is the optional `GET /audit/stream` SSE (admin RPC methods
107
- // live behind spec-level role auth on the shared endpoint path).
108
- // The `/audit/stream` suffix tracks the hardcoded path in
109
- // `auth/audit_log_routes.ts` if consumers ever need to mount
110
- // the audit SSE at a different suffix, promote this to an
111
- // `audit_log_path_suffix` option on
112
- // `StandardAdminIntegrationTestOptions`.
113
- const admin_routes = captured_route_specs.filter((s) => s.path.endsWith('/audit/stream') && (s.auth.roles?.includes('admin') ?? false));
114
- // Adaptive threshold: when the scoped admin REST surface is
115
- // effectively empty (0–1 routes typical for the RPC-first
116
- // admin surface), the 20% baseline is meaningless — a single
117
- // SSE route that can't be exercised against an error schema
118
- // drops the ratio to 0.0%. Log an informational skip instead
119
- // of asserting.
120
- // The admin RPC surface is covered by
121
- // `describe_rpc_round_trip_tests`, not this collector.
122
- if (admin_routes.length <= 1) {
123
- console.log(`[error coverage] skipped admin REST coverage assertion — ` +
124
- `scoped surface has ${admin_routes.length} route(s); ` +
125
- `admin RPC surface is covered by describe_rpc_round_trip_tests`);
126
- return;
127
- }
128
- assert_error_coverage(error_collector, admin_routes, {
129
- min_coverage: DEFAULT_INTEGRATION_ERROR_COVERAGE,
130
- });
85
+ // Scope coverage to admin auth-related routes. Account listing,
86
+ // session/token revoke-all, audit-log reads, and invite CRUD all
87
+ // live on the RPC surface; the only admin REST route remaining
88
+ // is the optional `GET /audit/stream` SSE (admin RPC methods
89
+ // live behind spec-level role auth on the shared endpoint path).
90
+ const admin_routes = route_specs.filter((s) => s.path.endsWith('/audit/stream') && (s.auth.roles?.includes('admin') ?? false));
91
+ // Adaptive threshold: when the scoped admin REST surface is
92
+ // effectively empty (0–1 routes typical for the RPC-first
93
+ // admin surface), the 20% baseline is meaningless a single
94
+ // SSE route that can't be exercised against an error schema
95
+ // drops the ratio to 0.0%. Log an informational skip instead
96
+ // of asserting.
97
+ if (admin_routes.length <= 1) {
98
+ console.log(`[error coverage] skipped admin REST coverage assertion ` +
99
+ `scoped surface has ${admin_routes.length} route(s); ` +
100
+ `admin RPC surface is covered by describe_rpc_round_trip_tests`);
101
+ return;
131
102
  }
103
+ assert_error_coverage(error_collector, admin_routes, {
104
+ min_coverage: DEFAULT_INTEGRATION_ERROR_COVERAGE,
105
+ });
132
106
  });
133
107
  /** Make request headers for a given session cookie. */
134
108
  const create_headers = (session_cookie, extra) => ({
@@ -138,41 +112,22 @@ export const describe_standard_admin_integration_tests = (options) => {
138
112
  ...extra,
139
113
  });
140
114
  /**
141
- * Drive the full consent flow (admin offer → recipient accept) and
142
- * return the materialized role_grant id. Accept is a direct transactional
143
- * `query_accept_offer` call because the suite focuses on the admin
144
- * side; exercising the recipient's UI-wired accept path is covered by
145
- * `describe_rpc_round_trip_tests` + fuz_app's own action suite.
115
+ * Suite-local wrapper around `role_grant_offer_and_accept` that closes
116
+ * over `rpc_path` so call sites stay terse. See the shared helper for
117
+ * the cross-suite contract.
146
118
  */
147
- const offer_and_accept = async (args) => {
148
- const res = await rpc_call_for_spec({
149
- app: args.app,
150
- path: rpc_path,
151
- spec: role_grant_offer_create_action_spec,
152
- params: { to_account_id: args.to_account_id, role: args.role },
153
- headers: args.admin_headers,
154
- });
155
- assert.ok(res.ok, `role_grant_offer_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
156
- const { offer } = res.result;
157
- const accept_result = await get_db().transaction(async (tx) => query_accept_offer({ db: tx }, {
158
- offer_id: offer.id,
159
- to_account_id: args.to_account_id,
160
- actor_id: args.to_actor_id,
161
- ip: null,
162
- }));
163
- return { offer_id: offer.id, role_grant_id: accept_result.role_grant.id };
164
- };
119
+ const offer_and_accept = (args) => role_grant_offer_and_accept({ ...args, rpc_path });
165
120
  // --- 1. Admin account listing (RPC) ---
166
121
  describe('admin account listing', () => {
167
122
  test('admin can list all accounts', async () => {
168
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
169
- const user_two = await test_app.create_account({ username: 'user_two' });
123
+ const fixture = await options.setup_test();
124
+ const user_two = await fixture.create_account({ username: 'user_two' });
170
125
  const res = await rpc_call_for_spec({
171
- app: test_app.app,
126
+ app: { request: fixture.transport },
172
127
  path: rpc_path,
173
128
  spec: admin_account_list_action_spec,
174
129
  params: {},
175
- headers: test_app.create_session_headers(),
130
+ headers: fixture.create_session_headers(),
176
131
  });
177
132
  assert.ok(res.ok, `admin_account_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
178
133
  assert.ok(Array.isArray(res.result.accounts), 'Expected accounts array');
@@ -183,14 +138,19 @@ export const describe_standard_admin_integration_tests = (options) => {
183
138
  assert.ok(found, 'Expected user_two in accounts listing');
184
139
  });
185
140
  test('non-admin cannot list accounts', async () => {
186
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db(), [ROLE_KEEPER]));
187
- captured_route_specs ??= test_app.route_specs;
141
+ const fixture = await options.setup_test();
142
+ // Default keeper has both ROLE_KEEPER + ROLE_ADMIN; mint a fresh
143
+ // account with only ROLE_KEEPER (no admin) to probe the 403 path.
144
+ const keeper_only = await fixture.create_account({
145
+ username: 'keeper_only',
146
+ roles: [ROLE_KEEPER],
147
+ });
188
148
  const res = await rpc_call_for_spec({
189
- app: test_app.app,
149
+ app: { request: fixture.transport },
190
150
  path: rpc_path,
191
151
  spec: admin_account_list_action_spec,
192
152
  params: {},
193
- headers: test_app.create_session_headers(),
153
+ headers: keeper_only.create_session_headers(),
194
154
  });
195
155
  assert.ok(!res.ok, 'Expected admin_account_list to fail for non-admin');
196
156
  assert.strictEqual(res.status, 403);
@@ -207,25 +167,25 @@ export const describe_standard_admin_integration_tests = (options) => {
207
167
  // --- 3. Admin session management ---
208
168
  describe('admin session management', () => {
209
169
  test('admin can list all active sessions', async () => {
210
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
211
- await test_app.create_account({ username: 'user_two' });
170
+ const fixture = await options.setup_test();
171
+ await fixture.create_account({ username: 'user_two' });
212
172
  const res = await rpc_call_for_spec({
213
- app: test_app.app,
173
+ app: { request: fixture.transport },
214
174
  path: rpc_path,
215
175
  spec: admin_session_list_action_spec,
216
176
  params: {},
217
- headers: test_app.create_session_headers(),
177
+ headers: fixture.create_session_headers(),
218
178
  });
219
179
  assert.ok(res.ok, `admin_session_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
220
180
  assert.ok(Array.isArray(res.result.sessions), 'Expected sessions array');
221
181
  assert.ok(res.result.sessions.length >= 2, 'Expected sessions from multiple accounts');
222
182
  });
223
183
  test('admin can revoke all sessions for another account', async () => {
224
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
225
- const user_two = await test_app.create_account({ username: 'user_two' });
184
+ const fixture = await options.setup_test();
185
+ const user_two = await fixture.create_account({ username: 'user_two' });
226
186
  // Verify user_two's session works via `account_verify` RPC
227
187
  const before = await rpc_call_for_spec({
228
- app: test_app.app,
188
+ app: { request: fixture.transport },
229
189
  path: rpc_path,
230
190
  spec: account_verify_action_spec,
231
191
  params: undefined,
@@ -234,18 +194,18 @@ export const describe_standard_admin_integration_tests = (options) => {
234
194
  assert.strictEqual(before.status, 200);
235
195
  // Admin revokes all sessions for user_two via RPC
236
196
  const res = await rpc_call_for_spec({
237
- app: test_app.app,
197
+ app: { request: fixture.transport },
238
198
  path: rpc_path,
239
199
  spec: admin_session_revoke_all_action_spec,
240
200
  params: { account_id: user_two.account.id },
241
- headers: test_app.create_session_headers(),
201
+ headers: fixture.create_session_headers(),
242
202
  });
243
203
  assert.ok(res.ok, `admin_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
244
204
  assert.strictEqual(res.result.ok, true);
245
205
  assert.ok(res.result.count >= 1, 'Expected at least 1 revoked session');
246
206
  // Verify user_two's session no longer works
247
207
  const after = await rpc_call_for_spec({
248
- app: test_app.app,
208
+ app: { request: fixture.transport },
249
209
  path: rpc_path,
250
210
  spec: account_verify_action_spec,
251
211
  params: undefined,
@@ -254,25 +214,25 @@ export const describe_standard_admin_integration_tests = (options) => {
254
214
  assert.strictEqual(after.status, 401);
255
215
  });
256
216
  test('admin revoking own sessions invalidates own session', async () => {
257
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
217
+ const fixture = await options.setup_test();
258
218
  // Admin revokes own sessions via RPC
259
219
  const res = await rpc_call_for_spec({
260
- app: test_app.app,
220
+ app: { request: fixture.transport },
261
221
  path: rpc_path,
262
222
  spec: admin_session_revoke_all_action_spec,
263
- params: { account_id: test_app.backend.account.id },
264
- headers: test_app.create_session_headers(),
223
+ params: { account_id: fixture.account.id },
224
+ headers: fixture.create_session_headers(),
265
225
  });
266
226
  assert.ok(res.ok, `admin_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
267
227
  assert.strictEqual(res.result.ok, true);
268
228
  assert.ok(res.result.count >= 1, 'Expected at least 1 revoked session');
269
229
  // Admin's own session should no longer work
270
230
  const after = await rpc_call_for_spec({
271
- app: test_app.app,
231
+ app: { request: fixture.transport },
272
232
  path: rpc_path,
273
233
  spec: account_verify_action_spec,
274
234
  params: undefined,
275
- headers: test_app.create_session_headers(),
235
+ headers: fixture.create_session_headers(),
276
236
  });
277
237
  assert.strictEqual(after.status, 401);
278
238
  });
@@ -280,11 +240,11 @@ export const describe_standard_admin_integration_tests = (options) => {
280
240
  // --- 4. Admin token management ---
281
241
  describe('admin token management', () => {
282
242
  test('admin can revoke all tokens for another account', async () => {
283
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
284
- const user_two = await test_app.create_account({ username: 'user_two' });
243
+ const fixture = await options.setup_test();
244
+ const user_two = await fixture.create_account({ username: 'user_two' });
285
245
  // Verify user_two's bearer token works via `account_verify` RPC
286
246
  const before = await rpc_call_for_spec({
287
- app: test_app.app,
247
+ app: { request: fixture.transport },
288
248
  path: rpc_path,
289
249
  spec: account_verify_action_spec,
290
250
  params: undefined,
@@ -294,18 +254,18 @@ export const describe_standard_admin_integration_tests = (options) => {
294
254
  assert.strictEqual(before.status, 200);
295
255
  // Admin revokes all tokens for user_two via RPC
296
256
  const res = await rpc_call_for_spec({
297
- app: test_app.app,
257
+ app: { request: fixture.transport },
298
258
  path: rpc_path,
299
259
  spec: admin_token_revoke_all_action_spec,
300
260
  params: { account_id: user_two.account.id },
301
- headers: test_app.create_session_headers(),
261
+ headers: fixture.create_session_headers(),
302
262
  });
303
263
  assert.ok(res.ok, `admin_token_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
304
264
  assert.strictEqual(res.result.ok, true);
305
265
  assert.ok(res.result.count >= 1, 'Expected at least 1 revoked token');
306
266
  // Verify user_two's bearer token no longer works
307
267
  const after = await rpc_call_for_spec({
308
- app: test_app.app,
268
+ app: { request: fixture.transport },
309
269
  path: rpc_path,
310
270
  spec: account_verify_action_spec,
311
271
  params: undefined,
@@ -318,36 +278,36 @@ export const describe_standard_admin_integration_tests = (options) => {
318
278
  // --- 5. Audit log RPC reads ---
319
279
  describe('audit log RPC reads', () => {
320
280
  test('admin can list audit log events', async () => {
321
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
281
+ const fixture = await options.setup_test();
322
282
  const res = await rpc_call_for_spec({
323
- app: test_app.app,
283
+ app: { request: fixture.transport },
324
284
  path: rpc_path,
325
285
  spec: audit_log_list_action_spec,
326
286
  params: {},
327
- headers: test_app.create_session_headers(),
287
+ headers: fixture.create_session_headers(),
328
288
  });
329
289
  assert.ok(res.ok, `audit_log_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
330
290
  assert.ok(Array.isArray(res.result.events), 'Expected events array');
331
291
  });
332
292
  test('audit log supports event_type filter', async () => {
333
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
293
+ const fixture = await options.setup_test();
334
294
  // Admin offer emits `role_grant_offer_create`. The downstream
335
295
  // `role_grant_create` only fires on accept — out of scope for this test.
336
- const user_two = await test_app.create_account({ username: 'user_two' });
296
+ const user_two = await fixture.create_account({ username: 'user_two' });
337
297
  const offer_res = await rpc_call_for_spec({
338
- app: test_app.app,
298
+ app: { request: fixture.transport },
339
299
  path: rpc_path,
340
300
  spec: role_grant_offer_create_action_spec,
341
301
  params: { to_account_id: user_two.account.id, role: grantable_role },
342
- headers: test_app.create_session_headers(),
302
+ headers: fixture.create_session_headers(),
343
303
  });
344
304
  assert.ok(offer_res.ok, 'role_grant_offer_create should succeed');
345
305
  const res = await rpc_call_for_spec({
346
- app: test_app.app,
306
+ app: { request: fixture.transport },
347
307
  path: rpc_path,
348
308
  spec: audit_log_list_action_spec,
349
309
  params: { event_type: 'role_grant_offer_create' },
350
- headers: test_app.create_session_headers(),
310
+ headers: fixture.create_session_headers(),
351
311
  });
352
312
  assert.ok(res.ok, `audit_log_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
353
313
  assert.ok(res.result.events.length >= 1, 'Expected at least 1 role_grant_offer_create event');
@@ -356,23 +316,22 @@ export const describe_standard_admin_integration_tests = (options) => {
356
316
  }
357
317
  });
358
318
  test('admin can view role_grant history', async () => {
359
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
319
+ const fixture = await options.setup_test();
360
320
  // Drive the full consent flow so `role_grant_create` lands in the audit log
361
321
  // — `query_audit_log_list_role_grant_history` filters to (role_grant_create, role_grant_revoke).
362
- const user_two = await test_app.create_account({ username: 'user_two' });
322
+ const user_two = await fixture.create_account({ username: 'user_two' });
363
323
  await offer_and_accept({
364
- app: test_app.app,
365
- admin_headers: test_app.create_session_headers(),
366
- to_account_id: user_two.account.id,
367
- to_actor_id: user_two.actor.id,
324
+ app: { request: fixture.transport },
325
+ grantor: fixture,
326
+ recipient: user_two,
368
327
  role: grantable_role,
369
328
  });
370
329
  const res = await rpc_call_for_spec({
371
- app: test_app.app,
330
+ app: { request: fixture.transport },
372
331
  path: rpc_path,
373
332
  spec: audit_log_role_grant_history_action_spec,
374
333
  params: {},
375
- headers: test_app.create_session_headers(),
334
+ headers: fixture.create_session_headers(),
376
335
  });
377
336
  assert.ok(res.ok, `audit_log_role_grant_history failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
378
337
  assert.ok(res.result.events.length >= 1, 'Expected at least 1 role_grant history event');
@@ -381,93 +340,94 @@ export const describe_standard_admin_integration_tests = (options) => {
381
340
  // --- 6. Admin audit trail ---
382
341
  describe('admin audit trail', () => {
383
342
  test('role_grant revoke creates audit event', async () => {
384
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
385
- const user_two = await test_app.create_account({ username: 'user_two' });
386
- const role_grant = await query_create_role_grant({ db: get_db() }, {
343
+ const fixture = await options.setup_test();
344
+ assert(fixture.in_process, 'direct role_grant seed requires in-process db');
345
+ const user_two = await fixture.create_account({ username: 'user_two' });
346
+ const role_grant = await create_test_role_grant_direct(fixture.backend_internals.deps.db, {
387
347
  actor_id: user_two.actor.id,
388
348
  role: grantable_role,
389
- granted_by: test_app.backend.actor.id,
349
+ granted_by: fixture.actor.id,
390
350
  });
391
351
  // Revoke via RPC
392
352
  const revoke_res = await rpc_call_for_spec({
393
- app: test_app.app,
353
+ app: { request: fixture.transport },
394
354
  path: rpc_path,
395
355
  spec: role_grant_revoke_action_spec,
396
356
  params: { actor_id: user_two.actor.id, role_grant_id: role_grant.id },
397
- headers: test_app.create_session_headers(),
357
+ headers: fixture.create_session_headers(),
398
358
  });
399
359
  assert.ok(revoke_res.ok, `role_grant_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
400
360
  // Check audit log for role_grant_revoke event
401
361
  const audit_res = await rpc_call_for_spec({
402
- app: test_app.app,
362
+ app: { request: fixture.transport },
403
363
  path: rpc_path,
404
364
  spec: audit_log_list_action_spec,
405
365
  params: { event_type: 'role_grant_revoke' },
406
- headers: test_app.create_session_headers(),
366
+ headers: fixture.create_session_headers(),
407
367
  });
408
368
  assert.ok(audit_res.ok, `audit_log_list failed: ${audit_res.ok ? '' : JSON.stringify(audit_res.error)}`);
409
369
  assert.ok(audit_res.result.events.length >= 1, 'Expected role_grant_revoke audit event');
410
370
  assert.strictEqual(audit_res.result.events[0].event_type, 'role_grant_revoke');
411
371
  });
412
372
  test('admin session revoke-all creates audit event', async () => {
413
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
414
- const user_two = await test_app.create_account({ username: 'user_two' });
373
+ const fixture = await options.setup_test();
374
+ const user_two = await fixture.create_account({ username: 'user_two' });
415
375
  // Revoke all sessions for user_two via RPC
416
376
  const revoke_res = await rpc_call_for_spec({
417
- app: test_app.app,
377
+ app: { request: fixture.transport },
418
378
  path: rpc_path,
419
379
  spec: admin_session_revoke_all_action_spec,
420
380
  params: { account_id: user_two.account.id },
421
- headers: test_app.create_session_headers(),
381
+ headers: fixture.create_session_headers(),
422
382
  });
423
383
  assert.ok(revoke_res.ok, `admin_session_revoke_all failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
424
384
  // Check audit log
425
385
  const audit_res = await rpc_call_for_spec({
426
- app: test_app.app,
386
+ app: { request: fixture.transport },
427
387
  path: rpc_path,
428
388
  spec: audit_log_list_action_spec,
429
389
  params: { event_type: 'session_revoke_all' },
430
- headers: test_app.create_session_headers(),
390
+ headers: fixture.create_session_headers(),
431
391
  });
432
392
  assert.ok(audit_res.ok, 'audit_log_list should succeed');
433
393
  assert.ok(audit_res.result.events.length >= 1, 'Expected session_revoke_all audit event');
434
394
  assert.strictEqual(audit_res.result.events[0].event_type, 'session_revoke_all');
435
395
  });
436
396
  test('admin token revoke-all creates audit event', async () => {
437
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
438
- const user_two = await test_app.create_account({ username: 'user_two' });
397
+ const fixture = await options.setup_test();
398
+ const user_two = await fixture.create_account({ username: 'user_two' });
439
399
  // Revoke all tokens for user_two via RPC
440
400
  const revoke_res = await rpc_call_for_spec({
441
- app: test_app.app,
401
+ app: { request: fixture.transport },
442
402
  path: rpc_path,
443
403
  spec: admin_token_revoke_all_action_spec,
444
404
  params: { account_id: user_two.account.id },
445
- headers: test_app.create_session_headers(),
405
+ headers: fixture.create_session_headers(),
446
406
  });
447
407
  assert.ok(revoke_res.ok, `admin_token_revoke_all failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
448
408
  // Check audit log
449
409
  const audit_res = await rpc_call_for_spec({
450
- app: test_app.app,
410
+ app: { request: fixture.transport },
451
411
  path: rpc_path,
452
412
  spec: audit_log_list_action_spec,
453
413
  params: { event_type: 'token_revoke_all' },
454
- headers: test_app.create_session_headers(),
414
+ headers: fixture.create_session_headers(),
455
415
  });
456
416
  assert.ok(audit_res.ok, 'audit_log_list should succeed');
457
417
  assert.ok(audit_res.result.events.length >= 1, 'Expected token_revoke_all audit event');
458
418
  assert.strictEqual(audit_res.result.events[0].event_type, 'token_revoke_all');
459
419
  });
460
420
  test('admin session revoke-all 404 emits failure audit', async () => {
461
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
421
+ const fixture = await options.setup_test();
462
422
  // `Uuid = z.uuid()` is v4-strict; use a valid v4 shape so we hit the
463
423
  // handler's account lookup rather than failing at param validation.
464
424
  const missing_id = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaa01';
465
425
  const res = await rpc_call_for_spec({
466
- app: test_app.app,
426
+ app: { request: fixture.transport },
467
427
  path: rpc_path,
468
428
  spec: admin_session_revoke_all_action_spec,
469
429
  params: { account_id: missing_id },
470
- headers: test_app.create_session_headers(),
430
+ headers: fixture.create_session_headers(),
471
431
  });
472
432
  assert.ok(!res.ok, 'Expected 404 for missing account');
473
433
  assert.strictEqual(res.status, 404);
@@ -476,11 +436,11 @@ export const describe_standard_admin_integration_tests = (options) => {
476
436
  // `target_account_id` is null (FK prevents referencing a missing id)
477
437
  // — the probed id is preserved under `metadata.attempted_account_id`.
478
438
  const audit_res = await rpc_call_for_spec({
479
- app: test_app.app,
439
+ app: { request: fixture.transport },
480
440
  path: rpc_path,
481
441
  spec: audit_log_list_action_spec,
482
442
  params: { event_type: 'session_revoke_all' },
483
- headers: test_app.create_session_headers(),
443
+ headers: fixture.create_session_headers(),
484
444
  });
485
445
  assert.ok(audit_res.ok, 'audit_log_list should succeed');
486
446
  const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
@@ -491,24 +451,24 @@ export const describe_standard_admin_integration_tests = (options) => {
491
451
  assert.strictEqual(failure_meta.attempted_account_id, missing_id);
492
452
  });
493
453
  test('admin token revoke-all 404 emits failure audit', async () => {
494
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
454
+ const fixture = await options.setup_test();
495
455
  const missing_id = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaa02';
496
456
  const res = await rpc_call_for_spec({
497
- app: test_app.app,
457
+ app: { request: fixture.transport },
498
458
  path: rpc_path,
499
459
  spec: admin_token_revoke_all_action_spec,
500
460
  params: { account_id: missing_id },
501
- headers: test_app.create_session_headers(),
461
+ headers: fixture.create_session_headers(),
502
462
  });
503
463
  assert.ok(!res.ok, 'Expected 404 for missing account');
504
464
  assert.strictEqual(res.status, 404);
505
465
  assert.strictEqual(res.error.data.reason, 'account_not_found');
506
466
  const audit_res = await rpc_call_for_spec({
507
- app: test_app.app,
467
+ app: { request: fixture.transport },
508
468
  path: rpc_path,
509
469
  spec: audit_log_list_action_spec,
510
470
  params: { event_type: 'token_revoke_all' },
511
- headers: test_app.create_session_headers(),
471
+ headers: fixture.create_session_headers(),
512
472
  });
513
473
  assert.ok(audit_res.ok, 'audit_log_list should succeed');
514
474
  const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
@@ -521,19 +481,19 @@ export const describe_standard_admin_integration_tests = (options) => {
521
481
  });
522
482
  // --- 7. Audit log completeness ---
523
483
  describe('audit log completeness', () => {
524
- test('auth mutations each produce exactly one audit event', async () => {
525
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
526
- const login_route = find_auth_route(test_app.route_specs, '/login', 'POST');
527
- const logout_route = find_auth_route(test_app.route_specs, '/logout', 'POST');
528
- const password_route = find_auth_route(test_app.route_specs, '/password', 'POST');
484
+ test('auth mutations each produce at least one audit event', async () => {
485
+ const fixture = await options.setup_test();
486
+ const login_route = find_auth_route(route_specs, '/login', 'POST');
487
+ const logout_route = find_auth_route(route_specs, '/logout', 'POST');
488
+ const password_route = find_auth_route(route_specs, '/password', 'POST');
529
489
  // skip if required routes are missing (consumer may not wire all routes).
530
490
  // Token creation goes through `account_token_create` RPC — always wired
531
491
  // because `rpc_endpoints` is required at the suite level.
532
492
  if (!login_route || !logout_route || !password_route)
533
493
  return;
534
- const user_two = await test_app.create_account({ username: 'audit_user' });
494
+ const user_two = await fixture.create_account({ username: 'audit_user' });
535
495
  // 1. login (user_two logs in)
536
- const login_res = await test_app.app.request(login_route.path, {
496
+ const login_res = await fixture.transport(login_route.path, {
537
497
  method: 'POST',
538
498
  headers: {
539
499
  host: 'localhost',
@@ -549,7 +509,7 @@ export const describe_standard_admin_integration_tests = (options) => {
549
509
  const user_two_cookie = cookie_match?.[1];
550
510
  // 2. logout (user_two logs out)
551
511
  if (user_two_cookie) {
552
- await test_app.app.request(logout_route.path, {
512
+ await fixture.transport(logout_route.path, {
553
513
  method: 'POST',
554
514
  headers: {
555
515
  host: 'localhost',
@@ -562,34 +522,33 @@ export const describe_standard_admin_integration_tests = (options) => {
562
522
  // consentful flow: offer + accept so both `role_grant_offer_create` and
563
523
  // `role_grant_create` audit events land.
564
524
  const { role_grant_id } = await offer_and_accept({
565
- app: test_app.app,
566
- admin_headers: test_app.create_session_headers(),
567
- to_account_id: user_two.account.id,
568
- to_actor_id: user_two.actor.id,
525
+ app: { request: fixture.transport },
526
+ grantor: fixture,
527
+ recipient: user_two,
569
528
  role: grantable_role,
570
529
  });
571
530
  // 4. revoke role_grant (RPC)
572
531
  const revoke_res = await rpc_call_for_spec({
573
- app: test_app.app,
532
+ app: { request: fixture.transport },
574
533
  path: rpc_path,
575
534
  spec: role_grant_revoke_action_spec,
576
535
  params: { actor_id: user_two.actor.id, role_grant_id },
577
- headers: test_app.create_session_headers(),
536
+ headers: fixture.create_session_headers(),
578
537
  });
579
538
  assert.ok(revoke_res.ok, `role_grant_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
580
539
  // 5. create token (RPC)
581
540
  const token_res = await rpc_call_for_spec({
582
- app: test_app.app,
541
+ app: { request: fixture.transport },
583
542
  path: rpc_path,
584
543
  spec: account_token_create_action_spec,
585
544
  params: { name: 'audit-test-token' },
586
- headers: test_app.create_session_headers(),
545
+ headers: fixture.create_session_headers(),
587
546
  });
588
547
  assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
589
548
  // 6. password change
590
- await test_app.app.request(password_route.path, {
549
+ await fixture.transport(password_route.path, {
591
550
  method: 'POST',
592
- headers: test_app.create_session_headers({
551
+ headers: fixture.create_session_headers({
593
552
  'content-type': 'application/json',
594
553
  }),
595
554
  body: JSON.stringify({
@@ -599,7 +558,7 @@ export const describe_standard_admin_integration_tests = (options) => {
599
558
  });
600
559
  // query audit log and verify events
601
560
  // re-login as admin since password change revoked sessions
602
- const relogin_res = await test_app.app.request(login_route.path, {
561
+ const relogin_res = await fixture.transport(login_route.path, {
603
562
  method: 'POST',
604
563
  headers: {
605
564
  host: 'localhost',
@@ -607,7 +566,7 @@ export const describe_standard_admin_integration_tests = (options) => {
607
566
  'content-type': 'application/json',
608
567
  },
609
568
  body: JSON.stringify({
610
- username: test_app.backend.account.username,
569
+ username: fixture.account.username,
611
570
  password: 'new-audit-password-789',
612
571
  }),
613
572
  });
@@ -621,7 +580,7 @@ export const describe_standard_admin_integration_tests = (options) => {
621
580
  cookie: `${cookie_name}=${relogin_match[1]}`,
622
581
  };
623
582
  const audit_res = await rpc_call_for_spec({
624
- app: test_app.app,
583
+ app: { request: fixture.transport },
625
584
  path: rpc_path,
626
585
  spec: audit_log_list_action_spec,
627
586
  params: {},
@@ -652,23 +611,23 @@ export const describe_standard_admin_integration_tests = (options) => {
652
611
  // --- 8. Admin-to-admin isolation ---
653
612
  describe('admin-to-admin isolation', () => {
654
613
  test('admin B revoking own role_grant via RPC succeeds', async () => {
655
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
656
- captured_route_specs ??= test_app.route_specs;
614
+ const fixture = await options.setup_test();
657
615
  // Bootstrap user is admin A. Create admin B.
658
- const admin_b = await test_app.create_account({
616
+ const admin_b = await fixture.create_account({
659
617
  username: 'admin_b_iso',
660
618
  roles: ['admin'],
661
619
  });
662
620
  // Seed an active role_grant directly — the revoke IDOR check is the
663
621
  // subject of this test, not the grant→accept cycle.
664
- const role_grant = await query_create_role_grant({ db: get_db() }, {
622
+ assert(fixture.in_process, 'direct role_grant seed requires in-process db');
623
+ const role_grant = await create_test_role_grant_direct(fixture.backend_internals.deps.db, {
665
624
  actor_id: admin_b.actor.id,
666
625
  role: grantable_role,
667
- granted_by: test_app.backend.actor.id,
626
+ granted_by: fixture.actor.id,
668
627
  });
669
628
  // Admin B revokes their own role_grant via RPC — should succeed
670
629
  const revoke_res = await rpc_call_for_spec({
671
- app: test_app.app,
630
+ app: { request: fixture.transport },
672
631
  path: rpc_path,
673
632
  spec: role_grant_revoke_action_spec,
674
633
  params: { actor_id: admin_b.actor.id, role_grant_id: role_grant.id },
@@ -678,32 +637,32 @@ export const describe_standard_admin_integration_tests = (options) => {
678
637
  assert.strictEqual(revoke_res.result.revoked, true);
679
638
  });
680
639
  test('admin revoke-all sessions for another admin works', async () => {
681
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
682
- const admin_b = await test_app.create_account({
640
+ const fixture = await options.setup_test();
641
+ const admin_b = await fixture.create_account({
683
642
  username: 'admin_b_sess',
684
643
  roles: ['admin'],
685
644
  });
686
645
  // Admin A revokes all of admin B's sessions via RPC
687
646
  const res = await rpc_call_for_spec({
688
- app: test_app.app,
647
+ app: { request: fixture.transport },
689
648
  path: rpc_path,
690
649
  spec: admin_session_revoke_all_action_spec,
691
650
  params: { account_id: admin_b.account.id },
692
- headers: create_headers(test_app.backend.session_cookie),
651
+ headers: fixture.create_session_headers(),
693
652
  });
694
653
  assert.ok(res.ok, `admin_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
695
654
  assert.ok(typeof res.result.count === 'number', 'Expected count field in response');
696
655
  assert.ok(res.result.count >= 1, 'Expected at least 1 session revoked');
697
656
  });
698
657
  test('admin revoke-all tokens for another admin works', async () => {
699
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
700
- const admin_b = await test_app.create_account({
658
+ const fixture = await options.setup_test();
659
+ const admin_b = await fixture.create_account({
701
660
  username: 'admin_b_tok',
702
661
  roles: ['admin'],
703
662
  });
704
663
  // Admin B creates an API token via RPC
705
664
  const token_res = await rpc_call_for_spec({
706
- app: test_app.app,
665
+ app: { request: fixture.transport },
707
666
  path: rpc_path,
708
667
  spec: account_token_create_action_spec,
709
668
  params: { name: 'admin-b-token' },
@@ -712,11 +671,11 @@ export const describe_standard_admin_integration_tests = (options) => {
712
671
  assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
713
672
  // Admin A revokes all of admin B's tokens via RPC
714
673
  const res = await rpc_call_for_spec({
715
- app: test_app.app,
674
+ app: { request: fixture.transport },
716
675
  path: rpc_path,
717
676
  spec: admin_token_revoke_all_action_spec,
718
677
  params: { account_id: admin_b.account.id },
719
- headers: create_headers(test_app.backend.session_cookie),
678
+ headers: fixture.create_session_headers(),
720
679
  });
721
680
  assert.ok(res.ok, `admin_token_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
722
681
  assert.ok(typeof res.result.count === 'number', 'Expected count field in response');
@@ -724,11 +683,11 @@ export const describe_standard_admin_integration_tests = (options) => {
724
683
  assert.ok(res.result.count >= 1, 'Expected at least 1 token revoked');
725
684
  });
726
685
  test('non-admin cannot access admin routes for another account', async () => {
727
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
728
- const regular_user = await test_app.create_account({ username: 'regular_user_iso' });
686
+ const fixture = await options.setup_test();
687
+ const regular_user = await fixture.create_account({ username: 'regular_user_iso' });
729
688
  // Regular user tries to list accounts via the admin RPC — should 403
730
689
  const res = await rpc_call_for_spec({
731
- app: test_app.app,
690
+ app: { request: fixture.transport },
732
691
  path: rpc_path,
733
692
  spec: admin_account_list_action_spec,
734
693
  params: {},
@@ -741,23 +700,22 @@ export const describe_standard_admin_integration_tests = (options) => {
741
700
  // --- 8a. Error coverage: unauthenticated access to admin routes ---
742
701
  describe('error coverage breadth', () => {
743
702
  test('exercises 401/403 on admin routes for error coverage', async () => {
744
- const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
745
- captured_route_specs ??= test_app.route_specs;
703
+ const fixture = await options.setup_test();
746
704
  // `/api/admin` is nearly empty — admin reads and mutations live
747
705
  // on the RPC endpoint behind spec-level role auth. The path-prefix carve is still the right scope here
748
706
  // because error coverage is tracked against REST `RouteSpec`s,
749
707
  // not RPC method specs (`describe_rpc_round_trip_tests` covers
750
708
  // the admin RPC surface separately).
751
709
  const prefix = options.admin_prefix ?? '/api/admin';
752
- const admin_routes = test_app.route_specs.filter((s) => s.path.startsWith(prefix) && (s.auth.roles?.includes('admin') ?? false));
710
+ const admin_routes = route_specs.filter((s) => s.path.startsWith(prefix) && (s.auth.roles?.includes('admin') ?? false));
753
711
  // Hit admin routes without auth to exercise 401 error schemas.
754
712
  for (const route of admin_routes) {
755
- const res = await test_app.app.request(route.path, {
713
+ const res = await fixture.transport(route.path, {
756
714
  method: route.method,
757
715
  headers: { host: 'localhost' },
758
716
  });
759
717
  if (res.status === 401 || res.status === 403) {
760
- error_collector.record(test_app.route_specs, route.method, route.path, res.status);
718
+ error_collector.record(route_specs, route.method, route.path, res.status);
761
719
  }
762
720
  }
763
721
  });