@fuzdev/fuz_app 0.74.0 → 0.76.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.
- package/dist/auth/CLAUDE.md +4 -0
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +19 -14
- package/dist/auth/bearer_auth.d.ts +5 -1
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +13 -1
- package/dist/db/CLAUDE.md +4 -3
- package/dist/db/cell_queries.d.ts +0 -23
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +0 -30
- package/dist/http/route_spec.d.ts +15 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/surface.d.ts +6 -0
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +1 -0
- package/dist/server/serve_fact_route.d.ts +84 -33
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +242 -141
- package/dist/testing/CLAUDE.md +5 -1
- package/dist/testing/cross_backend/setup.d.ts +33 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +19 -1
- package/dist/testing/cross_backend/standard.d.ts +19 -1
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +2 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +14 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +24 -1
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +78 -0
- package/dist/testing/round_trip.d.ts +19 -1
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +75 -3
- package/dist/testing/rpc_round_trip.d.ts +23 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +26 -1
- package/package.json +7 -7
|
@@ -43,7 +43,7 @@ import type { RoleSchemaResult } from '../../auth/role_schema.js';
|
|
|
43
43
|
import type { AppSurfaceSpec } from '../../http/surface.js';
|
|
44
44
|
import type { RpcEndpointsSuiteOption } from '../rpc_helpers.js';
|
|
45
45
|
import type { BackendCapabilities } from './capabilities.js';
|
|
46
|
-
import type { SetupTest } from './setup.js';
|
|
46
|
+
import type { SetupTest, TestFixture } from './setup.js';
|
|
47
47
|
/**
|
|
48
48
|
* Configuration for `describe_standard_cross_process_tests`.
|
|
49
49
|
*
|
|
@@ -94,6 +94,24 @@ export interface StandardCrossProcessTestOptions {
|
|
|
94
94
|
* / `info/refs`) which stream git protocol bytes.
|
|
95
95
|
*/
|
|
96
96
|
round_trip_skip_routes?: Array<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Forwarded to `describe_rpc_round_trip_tests` as `success_fixtures`
|
|
99
|
+
* (method name → async params factory). Drives a populated **success**
|
|
100
|
+
* body for referential RPC reads (`*_get`, `*_log`) the nil-id round-trip
|
|
101
|
+
* can only ever error on, and validates it against the method's `output`
|
|
102
|
+
* schema on each backend — the success-shape parity check. See
|
|
103
|
+
* `RpcRoundTripTestOptions.success_fixtures`.
|
|
104
|
+
*/
|
|
105
|
+
rpc_success_fixtures?: Map<string, (fixture: TestFixture) => Promise<Record<string, unknown>>>;
|
|
106
|
+
/**
|
|
107
|
+
* Forwarded to `describe_round_trip_validation` as `success_fixtures`
|
|
108
|
+
* (`'METHOD /path'` → async `{url?, body?}` factory) for referential REST
|
|
109
|
+
* routes. See `RoundTripTestOptions.success_fixtures`.
|
|
110
|
+
*/
|
|
111
|
+
rest_success_fixtures?: Map<string, (fixture: TestFixture) => Promise<{
|
|
112
|
+
url?: string;
|
|
113
|
+
body?: Record<string, unknown>;
|
|
114
|
+
}>>;
|
|
97
115
|
}
|
|
98
116
|
/**
|
|
99
117
|
* Run the cross-process standard test bundle — integration, admin (when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/standard.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAM1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/standard.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAM1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,YAAY,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,WAAW,+BAA+B;IAC/C,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/F;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,GAAG,CAC1B,MAAM,EACN,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAC,CAAC,CACjF,CAAC;CACF;AAED;;;;GAIG;AACH,eAAO,MAAM,qCAAqC,GACjD,SAAS,+BAA+B,KACtC,IAwCF,CAAC"}
|
|
@@ -23,6 +23,7 @@ export const describe_standard_cross_process_tests = (options) => {
|
|
|
23
23
|
surface_source: options.surface_source,
|
|
24
24
|
capabilities: options.capabilities,
|
|
25
25
|
skip_routes: options.round_trip_skip_routes,
|
|
26
|
+
success_fixtures: options.rest_success_fixtures,
|
|
26
27
|
});
|
|
27
28
|
describe_rpc_round_trip_tests({
|
|
28
29
|
setup_test: options.setup_test,
|
|
@@ -30,6 +31,7 @@ export const describe_standard_cross_process_tests = (options) => {
|
|
|
30
31
|
capabilities: options.capabilities,
|
|
31
32
|
session_options: options.session_options,
|
|
32
33
|
rpc_endpoints: options.rpc_endpoints,
|
|
34
|
+
success_fixtures: options.rpc_success_fixtures,
|
|
33
35
|
});
|
|
34
36
|
describe_data_exposure_tests({
|
|
35
37
|
setup_test: options.setup_test,
|
|
@@ -106,6 +106,16 @@ export declare const testing_reset_action_spec: {
|
|
|
106
106
|
password_value: z.ZodOptional<z.ZodString>;
|
|
107
107
|
roles: z.ZodArray<z.ZodString>;
|
|
108
108
|
}, z.core.$strict>>>;
|
|
109
|
+
/**
|
|
110
|
+
* Additional actor names to seed on the **keeper** account, beyond
|
|
111
|
+
* its single bootstrap actor. Drives the multi-actor `acting`
|
|
112
|
+
* selector branches (omitted `acting` + >1 actor ⇒ `actor_required`
|
|
113
|
+
* with the `available[]` list) that are otherwise unreachable
|
|
114
|
+
* cross-process — account creation only ever mints one actor, and no
|
|
115
|
+
* production wire path adds a second. Bootstrap-cradle seeding, same
|
|
116
|
+
* rationale as `extra_accounts`.
|
|
117
|
+
*/
|
|
118
|
+
extra_actors: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
109
119
|
}, z.core.$strict>;
|
|
110
120
|
readonly output: z.ZodObject<{
|
|
111
121
|
account: z.ZodObject<{
|
|
@@ -128,6 +138,10 @@ export declare const testing_reset_action_spec: {
|
|
|
128
138
|
api_token: z.ZodString;
|
|
129
139
|
session_cookie: z.ZodString;
|
|
130
140
|
}, z.core.$strict>>;
|
|
141
|
+
extra_actors: z.ZodArray<z.ZodObject<{
|
|
142
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
143
|
+
name: z.ZodString;
|
|
144
|
+
}, z.core.$strict>>;
|
|
131
145
|
}, z.core.$strict>;
|
|
132
146
|
readonly async: true;
|
|
133
147
|
readonly description: "Test-binary only — wipe auth tables, re-bootstrap a fresh keeper (+ optional extras), fire the domain-state reset.";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,gBAAgB,CAAC;AAmBvC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;QAiBpC;;;;;;;;WAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWyC,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;CAWA,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;CAeC,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,QAAO,SACiB,CAAC;AAEzE;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWF,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,qCAAqC,QAAO,SAGvD,CAAC;AAEH,4CAA4C;AAC5C,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,2BAA2B,KAClC,KAAK,CAAC,SAAS,CAsHjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,UAAmC,CAAC"}
|
|
@@ -64,6 +64,7 @@ import { rpc_action } from '../../actions/action_rpc.js';
|
|
|
64
64
|
import { ROLE_ADMIN, ROLE_KEEPER } from '../../auth/role_schema.js';
|
|
65
65
|
import { auth_integration_truncate_tables } from '../db.js';
|
|
66
66
|
import { query_schema_snapshot, SchemaSnapshot } from '../schema_introspect.js';
|
|
67
|
+
import { query_create_actor } from '../../auth/account_queries.js';
|
|
67
68
|
import { create_test_account_with_credentials, mint_test_session, DEFAULT_TEST_PASSWORD, } from '../app_server.js';
|
|
68
69
|
/** Output shape for an individual seeded account (keeper or extra). */
|
|
69
70
|
const SeededAccountShape = z.strictObject({
|
|
@@ -112,9 +113,21 @@ export const testing_reset_action_spec = {
|
|
|
112
113
|
roles: z.array(z.string()),
|
|
113
114
|
}))
|
|
114
115
|
.optional(),
|
|
116
|
+
/**
|
|
117
|
+
* Additional actor names to seed on the **keeper** account, beyond
|
|
118
|
+
* its single bootstrap actor. Drives the multi-actor `acting`
|
|
119
|
+
* selector branches (omitted `acting` + >1 actor ⇒ `actor_required`
|
|
120
|
+
* with the `available[]` list) that are otherwise unreachable
|
|
121
|
+
* cross-process — account creation only ever mints one actor, and no
|
|
122
|
+
* production wire path adds a second. Bootstrap-cradle seeding, same
|
|
123
|
+
* rationale as `extra_accounts`.
|
|
124
|
+
*/
|
|
125
|
+
extra_actors: z.array(z.string()).optional(),
|
|
115
126
|
}),
|
|
116
127
|
output: SeededAccountShape.extend({
|
|
117
128
|
extra_accounts: z.array(SeededAccountShape),
|
|
129
|
+
/** The keeper's additional actors (from input `extra_actors`), in order. */
|
|
130
|
+
extra_actors: z.array(z.strictObject({ id: Uuid, name: z.string() })),
|
|
118
131
|
}),
|
|
119
132
|
async: true,
|
|
120
133
|
description: 'Test-binary only — wipe auth tables, re-bootstrap a fresh keeper (+ optional extras), fire the domain-state reset.',
|
|
@@ -301,6 +314,16 @@ export const create_testing_actions = (deps, options) => {
|
|
|
301
314
|
});
|
|
302
315
|
extras.push(seeded);
|
|
303
316
|
}
|
|
317
|
+
// 5b. Seed any additional keeper actors. Same bootstrap-cradle
|
|
318
|
+
// bypass as extra_accounts — no production wire path mints a
|
|
319
|
+
// second actor, so the multi-actor `acting` branches need this
|
|
320
|
+
// direct insert. Order-preserving so the fixture can address
|
|
321
|
+
// them positionally.
|
|
322
|
+
const extra_actors = [];
|
|
323
|
+
for (const name of input.extra_actors ?? []) {
|
|
324
|
+
const seeded_actor = await query_create_actor({ db: ctx.db }, keeper.account.id, name);
|
|
325
|
+
extra_actors.push({ id: seeded_actor.id, name: seeded_actor.name });
|
|
326
|
+
}
|
|
304
327
|
// 6. Refresh the daemon-token cache so subsequent daemon-token
|
|
305
328
|
// requests resolve to the freshly seeded keeper. The
|
|
306
329
|
// middleware's lazy-refresh path only fires when the cached
|
|
@@ -314,7 +337,7 @@ export const create_testing_actions = (deps, options) => {
|
|
|
314
337
|
// against this open transaction under PGlite.
|
|
315
338
|
if (reset_state)
|
|
316
339
|
await reset_state(ctx.db);
|
|
317
|
-
return { ...keeper, extra_accounts: extras };
|
|
340
|
+
return { ...keeper, extra_accounts: extras, extra_actors };
|
|
318
341
|
}),
|
|
319
342
|
rpc_action(testing_mint_session_action_spec, async (input, ctx) => {
|
|
320
343
|
const { session_cookie } = await mint_test_session({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAM9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAM9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAC,KAAK,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAGxD;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;;;;OASG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,kEAAkE;IAClE,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;OAIG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,mCAAmC,GAC/C,SAAS,8BAA8B,KACrC,IAw6CF,CAAC"}
|
|
@@ -23,6 +23,7 @@ import { ErrorCoverageCollector, assert_error_coverage, DEFAULT_INTEGRATION_ERRO
|
|
|
23
23
|
import { is_public_auth } from '../http/auth_shape.js';
|
|
24
24
|
import { account_verify_action_spec, account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
|
|
25
25
|
import { invite_create_action_spec } from '../auth/admin_action_specs.js';
|
|
26
|
+
import { LoginOutput, AccountStatusOutput } from '../auth/account_routes.js';
|
|
26
27
|
import {} from './cross_backend/capabilities.js';
|
|
27
28
|
import { DEFAULT_TEST_PASSWORD } from './app_server.js';
|
|
28
29
|
/**
|
|
@@ -91,6 +92,14 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
91
92
|
});
|
|
92
93
|
assert_error_coverage(error_collector, auth_routes.length > 0 ? auth_routes : route_specs, {
|
|
93
94
|
min_coverage: options.error_coverage_min ?? DEFAULT_INTEGRATION_ERROR_COVERAGE,
|
|
95
|
+
// Authorization denials (403) on these scoped auth routes — the
|
|
96
|
+
// credential-channel gate on /logout + /password, the invite gate on
|
|
97
|
+
// /signup — are exercised by the conformance + attack-surface suites,
|
|
98
|
+
// not this lifecycle suite. Drop 403 from this collector's denominator
|
|
99
|
+
// (same spirit as the [401, 403, 429] ignore in the attack-surface
|
|
100
|
+
// tightness defaults); otherwise #10 adding /logout's 403 to the spine
|
|
101
|
+
// surface tips the ratio under threshold here though the gate is tested.
|
|
102
|
+
ignore_statuses: [403],
|
|
94
103
|
});
|
|
95
104
|
});
|
|
96
105
|
// --- 1. Login/logout lifecycle ---
|
|
@@ -269,6 +278,75 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
269
278
|
assert.deepStrictEqual(wrong_pw_keys, no_user_keys, 'Response keys must be identical to prevent account enumeration');
|
|
270
279
|
assert.strictEqual(wrong_pw_body.error, no_user_body.error, 'Error codes must be identical');
|
|
271
280
|
});
|
|
281
|
+
// Wire-shape gate: the successful `POST /login` body must strict-parse
|
|
282
|
+
// against `LoginOutput` (`{ok: true}`). `.strictObject` rejects any
|
|
283
|
+
// extra field, so a backend leaking `username` / `account_id` (the
|
|
284
|
+
// Rust spine's old shape) fails here on either impl.
|
|
285
|
+
test('successful login body strict-parses against LoginOutput', async () => {
|
|
286
|
+
const fixture = await options.setup_test();
|
|
287
|
+
const login_route = find_auth_route(route_specs, '/login', 'POST');
|
|
288
|
+
assert.ok(login_route, 'Expected POST /login route — ensure create_route_specs includes account routes');
|
|
289
|
+
const res = await fixture.transport(login_route.path, {
|
|
290
|
+
method: 'POST',
|
|
291
|
+
headers: {
|
|
292
|
+
host: 'localhost',
|
|
293
|
+
origin: 'http://localhost:5173',
|
|
294
|
+
'content-type': 'application/json',
|
|
295
|
+
},
|
|
296
|
+
body: JSON.stringify({
|
|
297
|
+
username: fixture.account.username,
|
|
298
|
+
password: DEFAULT_TEST_PASSWORD,
|
|
299
|
+
}),
|
|
300
|
+
});
|
|
301
|
+
assert.strictEqual(res.status, 200);
|
|
302
|
+
const body = await res.json();
|
|
303
|
+
// Throws on any extra or missing field — drift on either backend fails.
|
|
304
|
+
LoginOutput.parse(body);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
// --- 1b. Account status body (strict schema) ---
|
|
308
|
+
describe('account status response body', () => {
|
|
309
|
+
// Wire-shape gate: the authenticated `GET /api/account/status` body
|
|
310
|
+
// must strict-parse against `AccountStatusOutput` — the full shape
|
|
311
|
+
// `{account: SessionAccountJson, actor: ActorSummaryJson | null,
|
|
312
|
+
// role_grants: RoleGrantSummaryJson[]}`. `.strictObject` rejects any
|
|
313
|
+
// extra or missing field on `account` / `actor` / each role_grant, so
|
|
314
|
+
// a backend returning the old narrow shape (Rust's `{account:{id,
|
|
315
|
+
// username}, role_grants:[{role}]}`) fails here on either impl. The
|
|
316
|
+
// fixture keeper is single-actor, so `actor` must be non-null and
|
|
317
|
+
// `role_grants` populated (keeper holds keeper + admin globally).
|
|
318
|
+
//
|
|
319
|
+
// `/status` is mounted at `create_app_server` time (it needs the
|
|
320
|
+
// `bootstrap_available` runtime state), not by `create_account_route_specs`
|
|
321
|
+
// and not listed in the declared surface, so we can't gate on `route_specs`.
|
|
322
|
+
// A backend that mounts only the account-route factory (e.g. a minimal
|
|
323
|
+
// in-process route set) doesn't serve it — probe at runtime and skip on
|
|
324
|
+
// 404. The full spine surfaces (in-process + cross-process) serve it, so
|
|
325
|
+
// the gate runs there. `find_auth_route` can't be used: `/status` isn't a
|
|
326
|
+
// `RestAuthRouteSuffix`.
|
|
327
|
+
test('authenticated status body strict-parses against AccountStatusOutput', async (ctx) => {
|
|
328
|
+
const fixture = await options.setup_test();
|
|
329
|
+
const login_route = find_auth_route(route_specs, '/login', 'POST');
|
|
330
|
+
assert.ok(login_route, 'Expected POST /login route — ensure create_route_specs includes account routes');
|
|
331
|
+
// `/status` is the sibling of `/login` under the same account prefix.
|
|
332
|
+
const status_path = login_route.path.replace(/\/login$/, '/status');
|
|
333
|
+
const res = await fixture.transport(status_path, {
|
|
334
|
+
method: 'GET',
|
|
335
|
+
headers: fixture.create_session_headers({ host: 'localhost' }),
|
|
336
|
+
});
|
|
337
|
+
if (res.status === 404) {
|
|
338
|
+
// Backend doesn't mount /status (minimal route set) — nothing to gate.
|
|
339
|
+
ctx.skip();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
assert.strictEqual(res.status, 200);
|
|
343
|
+
const body = await res.json();
|
|
344
|
+
// Throws on any extra/missing field across account/actor/role_grants.
|
|
345
|
+
const parsed = AccountStatusOutput.parse(body);
|
|
346
|
+
// Single-actor keeper: actor resolved, role_grants populated.
|
|
347
|
+
assert.ok(parsed.actor, 'single-actor keeper must resolve a non-null actor');
|
|
348
|
+
assert.ok(parsed.role_grants.length > 0, 'single-actor keeper must have populated role_grants');
|
|
349
|
+
});
|
|
272
350
|
});
|
|
273
351
|
// --- 2. Cookie attributes ---
|
|
274
352
|
describe('cookie attributes', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
2
|
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
3
|
-
import type { SetupTest } from './cross_backend/setup.js';
|
|
3
|
+
import type { SetupTest, TestFixture } from './cross_backend/setup.js';
|
|
4
4
|
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
5
5
|
/** Options for `describe_round_trip_validation`. */
|
|
6
6
|
export interface RoundTripTestOptions {
|
|
@@ -22,6 +22,24 @@ export interface RoundTripTestOptions {
|
|
|
22
22
|
skip_routes?: Array<string>;
|
|
23
23
|
/** Override generated bodies for specific routes (`'METHOD /path'` → body). */
|
|
24
24
|
input_overrides?: Map<string, Record<string, unknown>>;
|
|
25
|
+
/**
|
|
26
|
+
* Success-case fixtures for routes whose **populated success body** the
|
|
27
|
+
* generic nil-id input can't reach — referential REST routes whose path
|
|
28
|
+
* params / body must point at existing rows. Maps `'METHOD /path'` to an
|
|
29
|
+
* async factory that receives the per-test `fixture` (so it can seed the
|
|
30
|
+
* referenced state) and returns `{url?, body?}`: an explicit resolved `url`
|
|
31
|
+
* (when the factory built it from the ids it just seeded) and/or a request
|
|
32
|
+
* `body`. Omit `url` to fall back to the generated valid path.
|
|
33
|
+
*
|
|
34
|
+
* Distinct from `input_overrides` (body-only, accepts a valid error
|
|
35
|
+
* envelope): a `success_fixtures` entry **asserts a 2xx response** and
|
|
36
|
+
* validates it against the route's `output` schema — the success-shape
|
|
37
|
+
* parity check the nil-id round-trip can't perform.
|
|
38
|
+
*/
|
|
39
|
+
success_fixtures?: Map<string, (fixture: TestFixture) => Promise<{
|
|
40
|
+
url?: string;
|
|
41
|
+
body?: Record<string, unknown>;
|
|
42
|
+
}>>;
|
|
25
43
|
}
|
|
26
44
|
/**
|
|
27
45
|
* Run schema-driven round-trip validation tests.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA2B7B,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGvD,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,6EAA6E;IAC7E,YAAY,EAAE,mBAAmB,CAAC;IAClC,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,GAAG,CACrB,MAAM,EACN,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAC,CAAC,CACjF,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,oBAAoB,KAAG,IAyI9E,CAAC"}
|
|
@@ -16,8 +16,9 @@ import './assert_dev_env.js';
|
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
import { describe, test, beforeAll } from 'vitest';
|
|
19
|
+
import { describe, test, beforeAll, assert } from 'vitest';
|
|
20
20
|
import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
21
|
+
import { is_public_auth, needs_actor, input_schema_declares_acting } from '../http/auth_shape.js';
|
|
21
22
|
import { assert_response_matches_spec, pick_auth_headers } from './integration_helpers.js';
|
|
22
23
|
import { resolve_valid_path, generate_valid_body } from './schema_generators.js';
|
|
23
24
|
/**
|
|
@@ -54,14 +55,51 @@ export const describe_round_trip_validation = (options) => {
|
|
|
54
55
|
roles: [ROLE_ADMIN],
|
|
55
56
|
});
|
|
56
57
|
});
|
|
58
|
+
// Mirror `pick_auth_headers`' account selection to recover the actor id
|
|
59
|
+
// of whichever account it authed as — so an `actor: 'required'` route
|
|
60
|
+
// that declares `acting?: ActingActor` gets the matching actor supplied
|
|
61
|
+
// explicitly (its sole actor, here), rather than relying on implicit
|
|
62
|
+
// single-actor resolution. Keeps such routes drivable without a
|
|
63
|
+
// consumer skip-list entry. Returns `null` for public routes.
|
|
64
|
+
const pick_acting_actor_id = (spec) => {
|
|
65
|
+
const { auth } = spec;
|
|
66
|
+
if (is_public_auth(auth))
|
|
67
|
+
return null;
|
|
68
|
+
if (auth.credential_types?.includes('daemon_token'))
|
|
69
|
+
return fixture.actor.id;
|
|
70
|
+
if (auth.roles?.length) {
|
|
71
|
+
return auth.roles.includes(ROLE_ADMIN) ? admin_account.actor.id : fixture.actor.id;
|
|
72
|
+
}
|
|
73
|
+
return authed_account.actor.id;
|
|
74
|
+
};
|
|
57
75
|
test.each(describe_time_specs)('$method $path produces schema-valid response', async (spec) => {
|
|
58
76
|
const route_key = `${spec.method} ${spec.path}`;
|
|
59
77
|
if (skip_set.has(route_key))
|
|
60
78
|
return;
|
|
79
|
+
// Raw-byte / streaming routes (git smart-HTTP, binary upload/download)
|
|
80
|
+
// can't be round-tripped — no meaningful body to synthesize, no JSON
|
|
81
|
+
// shape to assert. Auto-skip by the spec marker rather than making
|
|
82
|
+
// every consumer hand-list them in `skip_routes`.
|
|
83
|
+
if (spec.raw_body)
|
|
84
|
+
return;
|
|
61
85
|
const url = resolve_valid_path(spec.path, spec.params);
|
|
62
86
|
const override = options.input_overrides?.get(route_key);
|
|
63
|
-
|
|
87
|
+
let body = override ?? generate_valid_body(spec.input);
|
|
64
88
|
const headers = pick_auth_headers(spec, fixture, authed_account, admin_account);
|
|
89
|
+
// Auto-supply `acting` for actor-required routes that declare it. The
|
|
90
|
+
// `actor !== 'none' ⟺ acting declared` registry invariant means a
|
|
91
|
+
// route either declares `acting` in `query` (REST GET/body-less) or
|
|
92
|
+
// `input` — supply the picked account's actor in the matching channel.
|
|
93
|
+
let request_url = url;
|
|
94
|
+
const acting_id = needs_actor(spec.auth) ? pick_acting_actor_id(spec) : null;
|
|
95
|
+
if (acting_id !== null) {
|
|
96
|
+
if (spec.query && input_schema_declares_acting(spec.query)) {
|
|
97
|
+
request_url = `${url}${url.includes('?') ? '&' : '?'}acting=${acting_id}`;
|
|
98
|
+
}
|
|
99
|
+
else if (input_schema_declares_acting(spec.input) && body) {
|
|
100
|
+
body = { ...body, acting: acting_id };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
65
103
|
const request_init = {
|
|
66
104
|
method: spec.method,
|
|
67
105
|
headers: {
|
|
@@ -70,7 +108,7 @@ export const describe_round_trip_validation = (options) => {
|
|
|
70
108
|
},
|
|
71
109
|
...(body ? { body: JSON.stringify(body) } : {}),
|
|
72
110
|
};
|
|
73
|
-
const res = await fixture.transport(
|
|
111
|
+
const res = await fixture.transport(request_url, request_init);
|
|
74
112
|
if (res.headers.get('Content-Type')?.includes('text/event-stream')) {
|
|
75
113
|
await res.body?.cancel();
|
|
76
114
|
return;
|
|
@@ -82,5 +120,39 @@ export const describe_round_trip_validation = (options) => {
|
|
|
82
120
|
throw new Error(`Round-trip validation failed for ${route_key} (status ${res.status}): ${e.message}`);
|
|
83
121
|
}
|
|
84
122
|
});
|
|
123
|
+
test('declared success fixtures produce schema-valid success bodies', async () => {
|
|
124
|
+
const success_fixtures = options.success_fixtures;
|
|
125
|
+
if (!success_fixtures || success_fixtures.size === 0)
|
|
126
|
+
return;
|
|
127
|
+
for (const [route_key, build] of success_fixtures) {
|
|
128
|
+
const space = route_key.indexOf(' ');
|
|
129
|
+
const method = route_key.slice(0, space);
|
|
130
|
+
const path = route_key.slice(space + 1);
|
|
131
|
+
const spec = describe_time_specs.find((s) => s.method === method && s.path === path);
|
|
132
|
+
assert.ok(spec, `success_fixtures references unknown route '${route_key}'`);
|
|
133
|
+
const seeded = await build(fixture);
|
|
134
|
+
const url = seeded.url ?? resolve_valid_path(spec.path, spec.params);
|
|
135
|
+
const body = seeded.body;
|
|
136
|
+
const headers = pick_auth_headers(spec, fixture, authed_account, admin_account);
|
|
137
|
+
const res = await fixture.transport(url, {
|
|
138
|
+
method: spec.method,
|
|
139
|
+
headers: {
|
|
140
|
+
...headers,
|
|
141
|
+
...(body ? { 'content-type': 'application/json' } : {}),
|
|
142
|
+
},
|
|
143
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
144
|
+
});
|
|
145
|
+
try {
|
|
146
|
+
assert.ok(res.ok, `success fixture expected a 2xx response, got status ${res.status}: ${await res
|
|
147
|
+
.clone()
|
|
148
|
+
.text()
|
|
149
|
+
.catch(() => '<unreadable>')}`);
|
|
150
|
+
await assert_response_matches_spec(describe_time_specs, spec.method, url, res);
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
throw new Error(`Round-trip success-fixture failed for ${route_key} (status ${res.status}): ${e.message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
85
157
|
});
|
|
86
158
|
};
|
|
@@ -2,7 +2,7 @@ import './assert_dev_env.js';
|
|
|
2
2
|
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
3
3
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
4
4
|
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
5
|
-
import type { SetupTest } from './cross_backend/setup.js';
|
|
5
|
+
import type { SetupTest, TestFixture } from './cross_backend/setup.js';
|
|
6
6
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
7
7
|
/** Options for `describe_rpc_round_trip_tests`. */
|
|
8
8
|
export interface RpcRoundTripTestOptions {
|
|
@@ -34,6 +34,28 @@ export interface RpcRoundTripTestOptions {
|
|
|
34
34
|
skip_methods?: Array<string>;
|
|
35
35
|
/** Override generated params for specific methods (method name → params). */
|
|
36
36
|
input_overrides?: Map<string, Record<string, unknown>>;
|
|
37
|
+
/**
|
|
38
|
+
* Success-case fixtures for methods whose **populated success body** the
|
|
39
|
+
* generic nil-id input can't reach — referential reads (`*_get`, `*_log`)
|
|
40
|
+
* whose required ids must point at existing rows. Maps method name to an
|
|
41
|
+
* async factory that receives the per-test `fixture` (so it can seed the
|
|
42
|
+
* referenced state — e.g. create a repo via `fixture.transport` +
|
|
43
|
+
* `fixture.create_session_headers()`) and returns the params that drive a
|
|
44
|
+
* **success** response.
|
|
45
|
+
*
|
|
46
|
+
* Distinct from `input_overrides`, which only swaps the request params; the
|
|
47
|
+
* response may still be a valid *error* envelope (missing-row `not_found`),
|
|
48
|
+
* which the generic loop accepts. A `success_fixtures` entry **asserts the
|
|
49
|
+
* response is `ok`** and validates `result` against the method's `output`
|
|
50
|
+
* schema — so a backend that drops a field, or errors where the other
|
|
51
|
+
* backend succeeds, fails loud. This is the success-shape parity check the
|
|
52
|
+
* nil-id round-trip structurally cannot perform (it only ever sees error
|
|
53
|
+
* envelopes for referential methods).
|
|
54
|
+
*
|
|
55
|
+
* Fired as POST. The factory runs against the shared per-describe fixture,
|
|
56
|
+
* so it must not assume a clean slate between entries (seed unique state).
|
|
57
|
+
*/
|
|
58
|
+
success_fixtures?: Map<string, (fixture: TestFixture) => Promise<Record<string, unknown>>>;
|
|
37
59
|
}
|
|
38
60
|
/**
|
|
39
61
|
* Run schema-driven round-trip validation for RPC endpoints.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAyB7B,OAAO,KAAK,EAAC,cAAc,EAAsB,MAAM,oBAAoB,CAAC;AAE5E,OAAO,
|
|
1
|
+
{"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAyB7B,OAAO,KAAK,EAAC,cAAc,EAAsB,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAQN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;OAKG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,qDAAqD;IACrD,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3F;AAoDD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,uBAAuB,KAAG,IAwJhF,CAAC"}
|
|
@@ -20,7 +20,7 @@ import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
|
20
20
|
import { JSONRPC_METHOD_NOT_FOUND, JsonrpcErrorResponse } from '../http/jsonrpc.js';
|
|
21
21
|
import { generate_valid_body } from './schema_generators.js';
|
|
22
22
|
import { is_public_auth } from '../http/auth_shape.js';
|
|
23
|
-
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, assert_jsonrpc_success_response, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
|
|
23
|
+
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, assert_jsonrpc_success_response, resolve_rpc_endpoints_for_setup, find_rpc_action, find_rpc_method, } from './rpc_helpers.js';
|
|
24
24
|
/**
|
|
25
25
|
* Pick auth headers matching an RPC method's auth requirement. Accepts
|
|
26
26
|
* any `KeeperHeaderProvider` — both `TestApp` (in-process) and
|
|
@@ -170,5 +170,30 @@ export const describe_rpc_round_trip_tests = (options) => {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
|
+
test('declared success fixtures produce schema-valid success bodies', async () => {
|
|
174
|
+
const success_fixtures = options.success_fixtures;
|
|
175
|
+
if (!success_fixtures || success_fixtures.size === 0)
|
|
176
|
+
return;
|
|
177
|
+
for (const [method, build] of success_fixtures) {
|
|
178
|
+
const located = find_rpc_action(rpc_endpoints_for_setup, method);
|
|
179
|
+
assert.ok(located, `success_fixtures references unknown RPC method '${method}'`);
|
|
180
|
+
const surface = find_rpc_method(surface_rpc_endpoints, method);
|
|
181
|
+
assert.ok(surface, `success_fixtures method '${method}' missing from generated surface`);
|
|
182
|
+
const params = await build(fixture);
|
|
183
|
+
const headers = pick_rpc_auth_headers(surface.method_spec, fixture, authed_account, admin_account);
|
|
184
|
+
const init = create_rpc_post_init(method, params);
|
|
185
|
+
Object.assign(init.headers, headers);
|
|
186
|
+
const res = await fixture.transport(located.path, init);
|
|
187
|
+
const body = await res.json();
|
|
188
|
+
try {
|
|
189
|
+
assert_method_implemented(method, body);
|
|
190
|
+
assert.ok(res.ok, `success fixture expected a success response, got status ${res.status}: ${JSON.stringify(body)}`);
|
|
191
|
+
assert_jsonrpc_success_response(body, located.action.spec.output);
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
throw new Error(`RPC success-fixture failed for ${method} (status ${res.status}): ${e.message}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
173
198
|
});
|
|
174
199
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.76.0",
|
|
4
4
|
"description": "fullstack app library",
|
|
5
5
|
"glyph": "🗝",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -63,12 +63,12 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@electric-sql/pglite": "^0.4.5",
|
|
66
|
-
"@fuzdev/blake3_wasm": "^0.1.
|
|
66
|
+
"@fuzdev/blake3_wasm": "^0.1.1",
|
|
67
67
|
"@fuzdev/fuz_code": "^0.45.1",
|
|
68
|
-
"@fuzdev/fuz_css": "^0.
|
|
69
|
-
"@fuzdev/fuz_ui": "^0.
|
|
70
|
-
"@fuzdev/fuz_util": "^0.
|
|
71
|
-
"@fuzdev/gro": "^0.
|
|
68
|
+
"@fuzdev/fuz_css": "^0.61.1",
|
|
69
|
+
"@fuzdev/fuz_ui": "^0.197.0",
|
|
70
|
+
"@fuzdev/fuz_util": "^0.63.0",
|
|
71
|
+
"@fuzdev/gro": "^0.200.0",
|
|
72
72
|
"@hono/node-server": "^1.19.14",
|
|
73
73
|
"@hono/node-ws": "^1.3.1",
|
|
74
74
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"prettier-plugin-svelte": "^3.5.1",
|
|
95
95
|
"svelte": "^5.56.0",
|
|
96
96
|
"svelte-check": "^4.4.5",
|
|
97
|
-
"svelte-docinfo": "^0.1
|
|
97
|
+
"svelte-docinfo": "^0.2.1",
|
|
98
98
|
"svelte2tsx": "^0.7.52",
|
|
99
99
|
"tslib": "^2.8.1",
|
|
100
100
|
"typescript": "^5.9.3",
|