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