@fuzdev/fuz_app 0.68.0 → 0.70.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/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +10 -3
- package/dist/auth/admin_action_specs.d.ts +2 -3
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +2 -3
- package/dist/auth/admin_actions.d.ts +4 -14
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +28 -36
- package/dist/auth/signup_routes.d.ts +0 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +9 -3
- package/dist/auth/standard_rpc_actions.d.ts +5 -5
- package/dist/auth/standard_rpc_actions.js +4 -4
- package/dist/server/app_server.d.ts +1 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +1 -5
- package/dist/testing/CLAUDE.md +98 -10
- package/dist/testing/app_server.d.ts +34 -0
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +31 -6
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +69 -1
- package/dist/testing/cross_backend/actor_lookup.d.ts +10 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -0
- package/dist/testing/cross_backend/actor_lookup.js +83 -0
- package/dist/testing/cross_backend/actor_search.d.ts +6 -0
- package/dist/testing/cross_backend/actor_search.d.ts.map +1 -0
- package/dist/testing/cross_backend/actor_search.js +92 -0
- package/dist/testing/cross_backend/app_settings.d.ts +6 -0
- package/dist/testing/cross_backend/app_settings.d.ts.map +1 -0
- package/dist/testing/cross_backend/app_settings.js +95 -0
- package/dist/testing/cross_backend/backend_config.d.ts +1 -1
- package/dist/testing/cross_backend/capabilities.d.ts +0 -9
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +0 -1
- package/dist/testing/cross_backend/cell_grant_role.d.ts +8 -0
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_grant_role.js +102 -0
- package/dist/testing/cross_backend/conformance_case.d.ts +144 -0
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_case.js +132 -0
- package/dist/testing/cross_backend/conformance_table.d.ts +46 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_table.js +199 -0
- package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +57 -0
- package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -0
- package/dist/testing/cross_backend/create_cross_backend_global_setup.js +31 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts +13 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +4 -6
- package/dist/testing/cross_backend/default_spine_surface.d.ts +17 -9
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +20 -12
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts +72 -0
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -0
- package/dist/testing/cross_backend/make_cross_backend_project.js +51 -0
- package/dist/testing/cross_backend/origin.d.ts +10 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin.js +73 -0
- package/dist/testing/cross_backend/setup.d.ts +22 -40
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +34 -5
- package/dist/testing/cross_backend/standard.d.ts +8 -0
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +1 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +102 -10
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +96 -5
- package/dist/testing/cross_backend/xfail.d.ts +15 -0
- package/dist/testing/cross_backend/xfail.d.ts.map +1 -0
- package/dist/testing/cross_backend/xfail.js +37 -0
- package/dist/testing/integration.d.ts +2 -3
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +40 -88
- package/dist/testing/rate_limiting.d.ts +1 -1
- package/dist/testing/rpc_helpers.d.ts +3 -3
- package/dist/testing/sse_round_trip.d.ts +1 -1
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +0 -1
- package/dist/ui/AdminAccounts.svelte +74 -83
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +21 -23
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +17 -26
- package/dist/ui/OpenSignupToggle.svelte +2 -5
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +9 -10
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +7 -17
- package/dist/ui/admin_accounts_state.svelte.d.ts +12 -19
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +10 -24
- package/dist/ui/admin_invites_state.svelte.d.ts +8 -11
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +7 -16
- package/dist/ui/admin_sessions_state.svelte.d.ts +6 -10
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +4 -14
- package/dist/ui/app_settings_state.svelte.d.ts +8 -12
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +6 -16
- package/dist/ui/audit_log_state.svelte.d.ts +9 -8
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +8 -20
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Table runner for the declarative cross-backend conformance suite.
|
|
4
|
+
*
|
|
5
|
+
* `describe_conformance_table_tests` takes a list of `ConformanceCase`
|
|
6
|
+
* rows plus the standard `{setup_test, surface_source, capabilities}`
|
|
7
|
+
* fixture protocol every Tier 1 suite uses — so **one runner drives both
|
|
8
|
+
* transports**: in-process via `default_in_process_setup` (fast, every
|
|
9
|
+
* `gro test`) and cross-process via `default_cross_process_setup` (the
|
|
10
|
+
* conformance gate, exercising each impl's real auth resolution over real
|
|
11
|
+
* HTTP). Same case definition, transport-parameterized.
|
|
12
|
+
*
|
|
13
|
+
* Each row references a `method`; the runner resolves its `input` /
|
|
14
|
+
* `output` schema from the live spec registry (RPC) or `RouteSpec` (the 6
|
|
15
|
+
* REST auth routes) — the row never carries a schema. The principal the
|
|
16
|
+
* row runs `as` resolves to a `TestFixture` accessor via
|
|
17
|
+
* `resolve_principal` — no inline credential minting.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { assert, describe, test } from 'vitest';
|
|
22
|
+
import { find_auth_route, rest_auth_route_suffixes, } from '../integration_helpers.js';
|
|
23
|
+
import { find_rpc_action, rpc_call, resolve_rpc_endpoints_for_setup, } from '../rpc_helpers.js';
|
|
24
|
+
import {} from './conformance_case.js';
|
|
25
|
+
import { xfail_until } from './xfail.js';
|
|
26
|
+
/**
|
|
27
|
+
* Map a `ConformancePrincipal` onto the transport + headers it
|
|
28
|
+
* authenticates with, reading exclusively from the per-test `TestFixture`.
|
|
29
|
+
*
|
|
30
|
+
* The five always-available principals resolve from fixture accessors;
|
|
31
|
+
* `role_holder` / `wrong_role` read a seeded `extra_accounts` entry named
|
|
32
|
+
* via `options.principals` (throws a clear setup error when unconfigured).
|
|
33
|
+
*/
|
|
34
|
+
const resolve_principal = async (fixture, as, principals) => {
|
|
35
|
+
switch (as) {
|
|
36
|
+
case 'keeper':
|
|
37
|
+
// Keeper carries its session cookie via `create_session_headers`
|
|
38
|
+
// (in-process the transport is stateless; cross-process the jar
|
|
39
|
+
// also holds it — the explicit header is the same value).
|
|
40
|
+
return { transport: fixture.transport, headers: fixture.create_session_headers() };
|
|
41
|
+
case 'daemon':
|
|
42
|
+
// Daemon-token is a non-browser credential — empty jar + no Origin.
|
|
43
|
+
return {
|
|
44
|
+
transport: fixture.fresh_transport({ origin: null }),
|
|
45
|
+
headers: fixture.create_daemon_token_headers(),
|
|
46
|
+
suppress_default_origin: true,
|
|
47
|
+
};
|
|
48
|
+
case 'token':
|
|
49
|
+
// Bearer is discarded in a browser context — empty jar + no Origin.
|
|
50
|
+
return {
|
|
51
|
+
transport: fixture.fresh_transport({ origin: null }),
|
|
52
|
+
headers: fixture.create_bearer_headers(),
|
|
53
|
+
suppress_default_origin: true,
|
|
54
|
+
};
|
|
55
|
+
case 'anonymous':
|
|
56
|
+
// Fresh jar so the keeper cookie (cross-process) can't leak in.
|
|
57
|
+
return { transport: fixture.fresh_transport(), headers: {} };
|
|
58
|
+
case 'fresh_non_admin': {
|
|
59
|
+
const account = await fixture.create_account();
|
|
60
|
+
return { transport: fixture.fresh_transport(), headers: account.create_session_headers() };
|
|
61
|
+
}
|
|
62
|
+
case 'expired_session': {
|
|
63
|
+
// The keeper presented via an expired server-side session — fresh
|
|
64
|
+
// jar so only the (expired) cookie this seam returns is sent. The
|
|
65
|
+
// minted cookie payload is valid; the backdated `auth_session` row
|
|
66
|
+
// is what the DB-row expiry gate refuses.
|
|
67
|
+
const cookie = await fixture.mint_expired_session();
|
|
68
|
+
return { transport: fixture.fresh_transport(), headers: { cookie } };
|
|
69
|
+
}
|
|
70
|
+
case 'role_holder':
|
|
71
|
+
case 'wrong_role': {
|
|
72
|
+
const username = principals?.[as];
|
|
73
|
+
if (!username) {
|
|
74
|
+
throw new Error(`conformance: principal '${as}' requires options.principals.${as} naming a seeded ` +
|
|
75
|
+
`extra_accounts username (declare the account at setup via extra_accounts).`);
|
|
76
|
+
}
|
|
77
|
+
const extra = fixture.extra_accounts[username];
|
|
78
|
+
if (!extra) {
|
|
79
|
+
throw new Error(`conformance: extra_accounts['${username}'] not seeded for principal '${as}' — ` +
|
|
80
|
+
`declare it in the suite's extra_accounts option.`);
|
|
81
|
+
}
|
|
82
|
+
return { transport: fixture.fresh_transport(), headers: extra.create_session_headers() };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/** Assert each expected field deep-equals the corresponding response field. */
|
|
87
|
+
const assert_fields = (actual, fields, label) => {
|
|
88
|
+
assert.ok(actual !== null && typeof actual === 'object', `${label}: expected an object to read fields from, got ${JSON.stringify(actual)}`);
|
|
89
|
+
const record = actual;
|
|
90
|
+
for (const [key, expected] of Object.entries(fields)) {
|
|
91
|
+
assert.deepEqual(record[key], expected, `${label}: field '${key}'`);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const is_success_status = (status) => status >= 200 && status < 300;
|
|
95
|
+
/**
|
|
96
|
+
* Run one conformance case end-to-end: resolve the principal, dispatch the
|
|
97
|
+
* request, and assert the expected status / reason / fields.
|
|
98
|
+
*/
|
|
99
|
+
const run_case = async (c, options, resolved_rpc_endpoints) => {
|
|
100
|
+
const fixture = await options.setup_test();
|
|
101
|
+
const { transport, headers, suppress_default_origin } = await resolve_principal(fixture, c.request.as, options.principals);
|
|
102
|
+
if (c.request.method.startsWith('/')) {
|
|
103
|
+
await run_rest_case(c, options, transport, headers);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
await run_rpc_case(c, transport, headers, suppress_default_origin, resolved_rpc_endpoints);
|
|
107
|
+
};
|
|
108
|
+
/** Dispatch + assert a case targeting an RPC method. */
|
|
109
|
+
const run_rpc_case = async (c, transport, headers, suppress_default_origin, resolved_rpc_endpoints) => {
|
|
110
|
+
const found = find_rpc_action(resolved_rpc_endpoints, c.request.method);
|
|
111
|
+
if (!found) {
|
|
112
|
+
throw new Error(`conformance: RPC method '${c.request.method}' not found on the surface — ` +
|
|
113
|
+
`check the method name or that the action is registered on rpc_endpoints.`);
|
|
114
|
+
}
|
|
115
|
+
const res = await rpc_call({
|
|
116
|
+
app: transport,
|
|
117
|
+
path: found.path,
|
|
118
|
+
method: c.request.method,
|
|
119
|
+
params: c.request.params,
|
|
120
|
+
headers,
|
|
121
|
+
...(c.request.verb && { verb: c.request.verb }),
|
|
122
|
+
...(suppress_default_origin && { suppress_default_origin: true }),
|
|
123
|
+
});
|
|
124
|
+
if (is_success_status(c.expect.status)) {
|
|
125
|
+
assert.ok(res.ok, `${c.name}: expected success (${c.expect.status}) but got error ${JSON.stringify(res.ok ? undefined : res.error)}`);
|
|
126
|
+
assert.strictEqual(res.status, c.expect.status, `${c.name}: status`);
|
|
127
|
+
const parsed = found.action.spec.output.safeParse(res.result);
|
|
128
|
+
assert.ok(parsed.success, `${c.name}: result does not match spec.output: ${JSON.stringify(parsed.success ? undefined : parsed.error.issues)}`);
|
|
129
|
+
if (c.expect.fields)
|
|
130
|
+
assert_fields(res.result, c.expect.fields, c.name);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
assert.ok(!res.ok, `${c.name}: expected error status ${c.expect.status} but got success`);
|
|
134
|
+
assert.strictEqual(res.status, c.expect.status, `${c.name}: error status`);
|
|
135
|
+
if (c.expect.error_reason !== undefined) {
|
|
136
|
+
const reason = res.error.data?.reason;
|
|
137
|
+
// Most RPC denials carry `error.data.reason` (incl. the pre-validation
|
|
138
|
+
// 401 now); a denial that genuinely omits it falls back to the status
|
|
139
|
+
// assertion above to pin the denial class.
|
|
140
|
+
if (reason !== undefined) {
|
|
141
|
+
assert.strictEqual(reason, c.expect.error_reason, `${c.name}: error.data.reason`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (c.expect.fields)
|
|
145
|
+
assert_fields(res.error.data, c.expect.fields, c.name);
|
|
146
|
+
};
|
|
147
|
+
/** Dispatch + assert a case targeting one of the 6 REST auth routes. */
|
|
148
|
+
const run_rest_case = async (c, options, transport, headers) => {
|
|
149
|
+
const suffix = c.request.method;
|
|
150
|
+
if (!rest_auth_route_suffixes.includes(suffix)) {
|
|
151
|
+
throw new Error(`conformance: REST method '${c.request.method}' is not a known auth-route suffix ` +
|
|
152
|
+
`(${rest_auth_route_suffixes.join(', ')}). Use an RPC method name for RPC actions.`);
|
|
153
|
+
}
|
|
154
|
+
const verb = c.request.verb ?? 'POST';
|
|
155
|
+
const route = find_auth_route(options.surface_source.route_specs, suffix, verb);
|
|
156
|
+
if (!route) {
|
|
157
|
+
throw new Error(`conformance: no REST route spec for ${verb} ${suffix} on the surface.`);
|
|
158
|
+
}
|
|
159
|
+
const init = {
|
|
160
|
+
method: verb,
|
|
161
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
162
|
+
...(verb !== 'GET' &&
|
|
163
|
+
c.request.params !== undefined && { body: JSON.stringify(c.request.params) }),
|
|
164
|
+
};
|
|
165
|
+
const response = await transport(route.path, init);
|
|
166
|
+
assert.strictEqual(response.status, c.expect.status, `${c.name}: status`);
|
|
167
|
+
const body = await response.json().catch(() => undefined);
|
|
168
|
+
if (is_success_status(c.expect.status)) {
|
|
169
|
+
const parsed = route.output.safeParse(body);
|
|
170
|
+
assert.ok(parsed.success, `${c.name}: body does not match route output: ${JSON.stringify(parsed.success ? undefined : parsed.error.issues)}`);
|
|
171
|
+
}
|
|
172
|
+
else if (c.expect.error_reason !== undefined) {
|
|
173
|
+
const error = body?.error;
|
|
174
|
+
assert.strictEqual(error, c.expect.error_reason, `${c.name}: body.error`);
|
|
175
|
+
}
|
|
176
|
+
if (c.expect.fields)
|
|
177
|
+
assert_fields(body, c.expect.fields, c.name);
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Register a `describe` block running every `ConformanceCase` as a
|
|
181
|
+
* vitest `test` (or `xfail_until` for deferred-by-design rows). Drives
|
|
182
|
+
* either transport via the shared `{setup_test, surface_source,
|
|
183
|
+
* capabilities}` protocol.
|
|
184
|
+
*/
|
|
185
|
+
export const describe_conformance_table_tests = (options) => {
|
|
186
|
+
const resolved_rpc_endpoints = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
187
|
+
describe(options.suite_name ?? 'conformance table', () => {
|
|
188
|
+
for (const c of options.cases) {
|
|
189
|
+
const label = c.note ? `${c.name} — ${c.note}` : c.name;
|
|
190
|
+
const body = () => run_case(c, options, resolved_rpc_endpoints);
|
|
191
|
+
if (c.xfail) {
|
|
192
|
+
xfail_until(c.xfail.tracking_id, c.xfail.reason, label, body);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
test(label, body);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generic vitest `globalSetup` factory for cross-backend integration suites.
|
|
4
|
+
*
|
|
5
|
+
* Pairs with `make_cross_backend_project`: each cross-backend vitest project
|
|
6
|
+
* sets its own `test.name`, and this factory derives the backend name from
|
|
7
|
+
* that project name (vitest 4 passes the `TestProject` to globalSetup), picks
|
|
8
|
+
* the matching `BackendConfig`, spawns + bootstraps it via `bootstrap_backend`,
|
|
9
|
+
* and `provide`s a serializable handle that `*.cross.test.ts` files
|
|
10
|
+
* `inject` and rebuild with `reconstruct_bootstrapped_handle`.
|
|
11
|
+
*
|
|
12
|
+
* A consumer's `global_setup.ts` collapses to:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import {create_cross_backend_global_setup} from
|
|
16
|
+
* '@fuzdev/fuz_app/testing/cross_backend/create_cross_backend_global_setup.js';
|
|
17
|
+
* import {deno_backend_config, rust_backend_config} from './my_backend_config.js';
|
|
18
|
+
* import './cross_test_types.js'; // augments inject('backend_handle')
|
|
19
|
+
*
|
|
20
|
+
* export default create_cross_backend_global_setup({
|
|
21
|
+
* configs: {deno: deno_backend_config, rust: rust_backend_config},
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* vitest 4's `provide` hard-rejects non-serializable values, so the live
|
|
26
|
+
* `child` / `teardown` / `keeper_transport` are stripped via
|
|
27
|
+
* `serialize_bootstrapped_handle`; the teardown closure stays in the
|
|
28
|
+
* globalSetup process and is returned for vitest to fire after the suite.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import type { TestProject } from 'vitest/node';
|
|
33
|
+
import type { BackendConfig } from './backend_config.js';
|
|
34
|
+
export interface CrossBackendGlobalSetupOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Map of derived backend name → `BackendConfig` factory. The derived
|
|
37
|
+
* name (see `derive_name`) selects the factory; unknown names throw with
|
|
38
|
+
* the full supported list so a misnamed project surfaces clearly.
|
|
39
|
+
*/
|
|
40
|
+
readonly configs: Readonly<Record<string, () => BackendConfig>>;
|
|
41
|
+
/**
|
|
42
|
+
* Derive the backend name from the vitest project name. Default strips
|
|
43
|
+
* `cross_backend_(ts_)?`.
|
|
44
|
+
*/
|
|
45
|
+
readonly derive_name?: (project_name: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* Key passed to `project.provide` (and read by `inject` in test files).
|
|
48
|
+
* Default `'backend_handle'`. Augment vitest's `ProvidedContext` for it.
|
|
49
|
+
*/
|
|
50
|
+
readonly provide_key?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a vitest `globalSetup` default export. Returns the
|
|
54
|
+
* `(project) => teardown` function vitest 4 expects.
|
|
55
|
+
*/
|
|
56
|
+
export declare const create_cross_backend_global_setup: ({ configs, derive_name, provide_key, }: CrossBackendGlobalSetupOptions) => ((project: TestProject) => Promise<() => Promise<void>>);
|
|
57
|
+
//# sourceMappingURL=create_cross_backend_global_setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create_cross_backend_global_setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/create_cross_backend_global_setup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAYvD,MAAM,WAAW,8BAA8B;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,aAAa,CAAC,CAAC,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAAI,wCAI/C,8BAA8B,KAAG,CAAC,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAqB1F,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { bootstrap_backend } from './bootstrap_backend.js';
|
|
3
|
+
import { serialize_bootstrapped_handle } from './setup.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default project-name → backend-name reduction: strips the
|
|
6
|
+
* `cross_backend_` prefix plus an optional `ts_` discriminator (the TS
|
|
7
|
+
* canonical backends carry it to distinguish JS runtimes). So
|
|
8
|
+
* `cross_backend_ts_deno` → `deno` and `cross_backend_rust` → `rust`.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_PROJECT_NAME_PREFIX = /^cross_backend_(?:ts_)?/;
|
|
11
|
+
/**
|
|
12
|
+
* Build a vitest `globalSetup` default export. Returns the
|
|
13
|
+
* `(project) => teardown` function vitest 4 expects.
|
|
14
|
+
*/
|
|
15
|
+
export const create_cross_backend_global_setup = ({ configs, derive_name = (project_name) => project_name.replace(DEFAULT_PROJECT_NAME_PREFIX, ''), provide_key = 'backend_handle', }) => {
|
|
16
|
+
return async (project) => {
|
|
17
|
+
const name = derive_name(project.name);
|
|
18
|
+
const factory = configs[name];
|
|
19
|
+
if (!factory) {
|
|
20
|
+
throw new Error(`Could not derive backend config from vitest project '${project.name}' ` +
|
|
21
|
+
`(derived name '${name}') — expected one of: ${Object.keys(configs).join(', ')}`);
|
|
22
|
+
}
|
|
23
|
+
const bootstrapped = await bootstrap_backend(factory());
|
|
24
|
+
// vitest's `provide` is keyed on the consumer-augmented `ProvidedContext`;
|
|
25
|
+
// the generic key is a plain string, so cast past the keyof constraint.
|
|
26
|
+
project.provide(provide_key, serialize_bootstrapped_handle(bootstrapped));
|
|
27
|
+
return async () => {
|
|
28
|
+
await bootstrapped.teardown();
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -70,6 +70,14 @@ export interface MakeDefaultTsBackendConfigOptions {
|
|
|
70
70
|
* override.
|
|
71
71
|
*/
|
|
72
72
|
readonly port_env_var?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Session cookie name the binary's `create_session_config` uses.
|
|
75
|
+
* Defaults to `'fuz_session'`. Must match the consumer's session config
|
|
76
|
+
* — the harness threads the `_testing_reset`-returned keeper cookie into
|
|
77
|
+
* its jar under this name, so a mismatch surfaces as 401s on the
|
|
78
|
+
* `create_account` path (e.g. fuz_forge uses `'fuz_forge_session'`).
|
|
79
|
+
*/
|
|
80
|
+
readonly cookie_name?: string;
|
|
73
81
|
}
|
|
74
82
|
/**
|
|
75
83
|
* Shared builder for TS-family backends (Deno + Node). Owns the common
|
|
@@ -111,6 +119,11 @@ export interface MakeDefaultRustBackendConfigOptions {
|
|
|
111
119
|
* (e.g. `'info,zzz_server=info,testing_zzz_server=info'`).
|
|
112
120
|
*/
|
|
113
121
|
readonly rust_log?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Session cookie name the binary uses. Defaults to `'fuz_session'`.
|
|
124
|
+
* Must match the consumer's session config (see the TS builder's note).
|
|
125
|
+
*/
|
|
126
|
+
readonly cookie_name?: string;
|
|
114
127
|
}
|
|
115
128
|
/**
|
|
116
129
|
* Shared builder for Rust-family backends. Owns the common env baseline
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBASpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAStC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
|
|
@@ -16,7 +16,6 @@ export const ts_default_capabilities = Object.freeze({
|
|
|
16
16
|
cell_crud: true,
|
|
17
17
|
cell_relations: true,
|
|
18
18
|
account_lifecycle: true,
|
|
19
|
-
in_process_only: false,
|
|
20
19
|
});
|
|
21
20
|
/**
|
|
22
21
|
* Capabilities for the Rust family. Adds `trusted_proxy: true` (the
|
|
@@ -33,7 +32,6 @@ export const rust_default_capabilities = Object.freeze({
|
|
|
33
32
|
cell_crud: true,
|
|
34
33
|
cell_relations: true,
|
|
35
34
|
account_lifecycle: true,
|
|
36
|
-
in_process_only: false,
|
|
37
35
|
});
|
|
38
36
|
/** Bootstrap block built from the default secrets + supplied paths. */
|
|
39
37
|
const build_default_bootstrap = (paths, overrides) => ({
|
|
@@ -51,7 +49,7 @@ const build_default_bootstrap = (paths, overrides) => ({
|
|
|
51
49
|
* genuinely differs.
|
|
52
50
|
*/
|
|
53
51
|
export const make_default_ts_backend_config = (opts) => {
|
|
54
|
-
const { name, port, start_command, database_url = 'memory://', extra_env, capabilities = ts_default_capabilities, paths = build_test_backend_paths(name), bootstrap_overrides, port_env_var = 'PORT', } = opts;
|
|
52
|
+
const { name, port, start_command, database_url = 'memory://', extra_env, capabilities = ts_default_capabilities, paths = build_test_backend_paths(name), bootstrap_overrides, port_env_var = 'PORT', cookie_name = 'fuz_session', } = opts;
|
|
55
53
|
return {
|
|
56
54
|
name,
|
|
57
55
|
start_command,
|
|
@@ -60,7 +58,7 @@ export const make_default_ts_backend_config = (opts) => {
|
|
|
60
58
|
ws_path: '/api/ws',
|
|
61
59
|
health_path: '/health',
|
|
62
60
|
bootstrap_path: '/api/account/bootstrap',
|
|
63
|
-
cookie_name
|
|
61
|
+
cookie_name,
|
|
64
62
|
startup_timeout_ms: 30_000,
|
|
65
63
|
env: {
|
|
66
64
|
NODE_ENV: 'development',
|
|
@@ -83,7 +81,7 @@ export const make_default_ts_backend_config = (opts) => {
|
|
|
83
81
|
* the 120s startup window for cargo's first-run build cost.
|
|
84
82
|
*/
|
|
85
83
|
export const make_default_rust_backend_config = (opts) => {
|
|
86
|
-
const { name, port, start_command, database_url, extra_env, capabilities = rust_default_capabilities, paths = build_test_backend_paths(name), bootstrap_overrides, port_env_var = 'PORT', rust_log = 'info', } = opts;
|
|
84
|
+
const { name, port, start_command, database_url, extra_env, capabilities = rust_default_capabilities, paths = build_test_backend_paths(name), bootstrap_overrides, port_env_var = 'PORT', rust_log = 'info', cookie_name = 'fuz_session', } = opts;
|
|
87
85
|
return {
|
|
88
86
|
name,
|
|
89
87
|
start_command,
|
|
@@ -92,7 +90,7 @@ export const make_default_rust_backend_config = (opts) => {
|
|
|
92
90
|
ws_path: '/api/ws',
|
|
93
91
|
health_path: '/health',
|
|
94
92
|
bootstrap_path: '/api/account/bootstrap',
|
|
95
|
-
cookie_name
|
|
93
|
+
cookie_name,
|
|
96
94
|
startup_timeout_ms: 120_000,
|
|
97
95
|
env: {
|
|
98
96
|
RUST_LOG: rust_log,
|
|
@@ -10,10 +10,19 @@ import type { AppServerContext } from '../../server/app_server.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare const spine_session_options: SessionOptions<string>;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* App role the role-shaped-`cell_grant` cross suite exercises. Registered
|
|
14
|
+
* with no grant path (`grant_paths: []`) so it stays a valid registry member
|
|
15
|
+
* without entering the admin / self-service grant flows — holders are seeded
|
|
16
|
+
* directly via `extra_accounts`. Must match the `cell_editor` entry in the
|
|
17
|
+
* Rust `testing_spine_stub`'s `known_roles` (cross-language test contract).
|
|
18
|
+
*/
|
|
19
|
+
export declare const SPINE_CELL_EDITOR_ROLE = "cell_editor";
|
|
20
|
+
/**
|
|
21
|
+
* The spine's closed role registry: built-ins plus `SPINE_CELL_EDITOR_ROLE`.
|
|
22
|
+
* Threaded into the cell spec set's role-validity gate; the Rust stub mirrors
|
|
23
|
+
* the same membership. When the spine grows additional grantable roles,
|
|
24
|
+
* thread their registry through `create_role_schema` here so the admin suite
|
|
25
|
+
* picks up grant-path coverage.
|
|
17
26
|
*/
|
|
18
27
|
export declare const spine_roles: RoleSchemaResult;
|
|
19
28
|
/** RPC endpoint mount path — matches the binary's `/api/rpc`. */
|
|
@@ -27,11 +36,10 @@ export declare const SPINE_RPC_PATH = "/api/rpc";
|
|
|
27
36
|
*/
|
|
28
37
|
export declare const SPINE_SSE_PATH = "/api/admin/audit/stream";
|
|
29
38
|
/**
|
|
30
|
-
* Factory-form RPC endpoints
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* never called across the process boundary.
|
|
39
|
+
* Factory-form RPC endpoints over the per-test `ctx.deps`. `create_app_server`
|
|
40
|
+
* (in the binary) owns live dispatch; the surface builder invokes the factory
|
|
41
|
+
* once with a stub ctx for setup-time path/method lookup, so the handler
|
|
42
|
+
* closures are never called across the process boundary.
|
|
35
43
|
*
|
|
36
44
|
* Test binaries append their own `_testing_reset` action to this endpoint's
|
|
37
45
|
* `actions` (see `testing_reset_actions.ts`); it is intentionally excluded
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6B9B,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAGxF,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AAGjE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG
|
|
1
|
+
{"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6B9B,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAGxF,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AAGjE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,gBAExB,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,eAAe,CAOhF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,CAiB/E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,QAAO,cAM1C,CAAC"}
|
|
@@ -37,12 +37,23 @@ import { create_test_app_surface_spec } from '../stubs.js';
|
|
|
37
37
|
*/
|
|
38
38
|
export const spine_session_options = create_session_config('fuz_session');
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* App role the role-shaped-`cell_grant` cross suite exercises. Registered
|
|
41
|
+
* with no grant path (`grant_paths: []`) so it stays a valid registry member
|
|
42
|
+
* without entering the admin / self-service grant flows — holders are seeded
|
|
43
|
+
* directly via `extra_accounts`. Must match the `cell_editor` entry in the
|
|
44
|
+
* Rust `testing_spine_stub`'s `known_roles` (cross-language test contract).
|
|
44
45
|
*/
|
|
45
|
-
export const
|
|
46
|
+
export const SPINE_CELL_EDITOR_ROLE = 'cell_editor';
|
|
47
|
+
/**
|
|
48
|
+
* The spine's closed role registry: built-ins plus `SPINE_CELL_EDITOR_ROLE`.
|
|
49
|
+
* Threaded into the cell spec set's role-validity gate; the Rust stub mirrors
|
|
50
|
+
* the same membership. When the spine grows additional grantable roles,
|
|
51
|
+
* thread their registry through `create_role_schema` here so the admin suite
|
|
52
|
+
* picks up grant-path coverage.
|
|
53
|
+
*/
|
|
54
|
+
export const spine_roles = create_role_schema([
|
|
55
|
+
{ name: SPINE_CELL_EDITOR_ROLE, grant_paths: [] },
|
|
56
|
+
]);
|
|
46
57
|
/** RPC endpoint mount path — matches the binary's `/api/rpc`. */
|
|
47
58
|
export const SPINE_RPC_PATH = '/api/rpc';
|
|
48
59
|
/**
|
|
@@ -54,11 +65,10 @@ export const SPINE_RPC_PATH = '/api/rpc';
|
|
|
54
65
|
*/
|
|
55
66
|
export const SPINE_SSE_PATH = '/api/admin/audit/stream';
|
|
56
67
|
/**
|
|
57
|
-
* Factory-form RPC endpoints
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* never called across the process boundary.
|
|
68
|
+
* Factory-form RPC endpoints over the per-test `ctx.deps`. `create_app_server`
|
|
69
|
+
* (in the binary) owns live dispatch; the surface builder invokes the factory
|
|
70
|
+
* once with a stub ctx for setup-time path/method lookup, so the handler
|
|
71
|
+
* closures are never called across the process boundary.
|
|
62
72
|
*
|
|
63
73
|
* Test binaries append their own `_testing_reset` action to this endpoint's
|
|
64
74
|
* `actions` (see `testing_reset_actions.ts`); it is intentionally excluded
|
|
@@ -69,7 +79,6 @@ export const spine_rpc_endpoints = (ctx) => [
|
|
|
69
79
|
{
|
|
70
80
|
path: SPINE_RPC_PATH,
|
|
71
81
|
actions: create_standard_rpc_actions(ctx.deps, {
|
|
72
|
-
app_settings: ctx.app_settings,
|
|
73
82
|
roles: spine_roles,
|
|
74
83
|
}),
|
|
75
84
|
},
|
|
@@ -97,7 +106,6 @@ export const create_spine_route_specs = (ctx) => [
|
|
|
97
106
|
session_options: spine_session_options,
|
|
98
107
|
ip_rate_limiter: null,
|
|
99
108
|
signup_account_rate_limiter: null,
|
|
100
|
-
app_settings: ctx.app_settings,
|
|
101
109
|
}),
|
|
102
110
|
]),
|
|
103
111
|
...(ctx.audit_sse
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic vitest project factory for cross-backend integration suites.
|
|
3
|
+
*
|
|
4
|
+
* One vitest project per spawned backend; each runs the consumer's shared
|
|
5
|
+
* `*.cross.test.ts` files against its own bootstrapped binary. The paired
|
|
6
|
+
* `create_cross_backend_global_setup` (in `global_setup.ts`) reads the
|
|
7
|
+
* project's `name` to pick which `BackendConfig` to spawn, so the project
|
|
8
|
+
* name is the single source of truth for backend selection.
|
|
9
|
+
*
|
|
10
|
+
* Consumers compose these into their `vite.config.ts`:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const cross_backend_projects = process.env.FUZ_TEST_CROSS_BACKEND
|
|
14
|
+
* ? [
|
|
15
|
+
* make_cross_backend_project({name: 'cross_backend_ts_deno', global_setup: GLOBAL_SETUP}),
|
|
16
|
+
* make_cross_backend_project({name: 'cross_backend_rust', global_setup: GLOBAL_SETUP}),
|
|
17
|
+
* ]
|
|
18
|
+
* : [];
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* where `GLOBAL_SETUP = './src/test/cross_backend/global_setup.ts'`.
|
|
22
|
+
*
|
|
23
|
+
* This module is intentionally dependency-free and `assert_dev_env`-free:
|
|
24
|
+
* it runs at vite **config** time (including production builds, where the
|
|
25
|
+
* consumer gates the projects behind an env flag), so it must not pull in
|
|
26
|
+
* the DEV-only test runtime.
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
export interface CrossBackendProjectOptions {
|
|
31
|
+
/**
|
|
32
|
+
* vitest project name. `create_cross_backend_global_setup` derives the
|
|
33
|
+
* backend name from it (by default stripping a `cross_backend_(ts_)?`
|
|
34
|
+
* prefix), so name projects `cross_backend_<backend>` (e.g.
|
|
35
|
+
* `cross_backend_rust`, `cross_backend_ts_deno`).
|
|
36
|
+
*/
|
|
37
|
+
readonly name: string;
|
|
38
|
+
/**
|
|
39
|
+
* Path to the consumer's vitest `globalSetup` module, relative to the
|
|
40
|
+
* consumer repo root (e.g. `'./src/test/cross_backend/global_setup.ts'`).
|
|
41
|
+
* That module is expected to export a `create_cross_backend_global_setup`
|
|
42
|
+
* result as its default.
|
|
43
|
+
*/
|
|
44
|
+
readonly global_setup: string;
|
|
45
|
+
/** Test-file globs. Default: `['src/test/cross_backend/*.cross.test.ts']`. */
|
|
46
|
+
readonly include?: ReadonlyArray<string>;
|
|
47
|
+
/** Globs to exclude from `include` (e.g. a backend-specific variant file). Default: `[]`. */
|
|
48
|
+
readonly exclude?: ReadonlyArray<string>;
|
|
49
|
+
/** vitest `sequence.groupOrder`. Default: `3` (runs after unit + db). */
|
|
50
|
+
readonly group_order?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a single cross-backend vitest project config. Spread the results
|
|
54
|
+
* into `test.projects` in the consumer's `vite.config.ts`. `isolate: false`
|
|
55
|
+
* + `fileParallelism: false` because a project shares one spawned backend
|
|
56
|
+
* across its files.
|
|
57
|
+
*/
|
|
58
|
+
export declare const make_cross_backend_project: ({ name, global_setup, include, exclude, group_order, }: CrossBackendProjectOptions) => {
|
|
59
|
+
extends: true;
|
|
60
|
+
test: {
|
|
61
|
+
name: string;
|
|
62
|
+
include: Array<string>;
|
|
63
|
+
exclude: Array<string>;
|
|
64
|
+
globalSetup: Array<string>;
|
|
65
|
+
isolate: false;
|
|
66
|
+
fileParallelism: false;
|
|
67
|
+
sequence: {
|
|
68
|
+
groupOrder: number;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=make_cross_backend_project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make_cross_backend_project.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/make_cross_backend_project.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,MAAM,WAAW,0BAA0B;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,yEAAyE;IACzE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,wDAMxC,0BAA0B,KAAG;IAC/B,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACvB,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACvB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,EAAE,KAAK,CAAC;QACf,eAAe,EAAE,KAAK,CAAC;QACvB,QAAQ,EAAE;YAAC,UAAU,EAAE,MAAM,CAAA;SAAC,CAAC;KAC/B,CAAC;CAYD,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic vitest project factory for cross-backend integration suites.
|
|
3
|
+
*
|
|
4
|
+
* One vitest project per spawned backend; each runs the consumer's shared
|
|
5
|
+
* `*.cross.test.ts` files against its own bootstrapped binary. The paired
|
|
6
|
+
* `create_cross_backend_global_setup` (in `global_setup.ts`) reads the
|
|
7
|
+
* project's `name` to pick which `BackendConfig` to spawn, so the project
|
|
8
|
+
* name is the single source of truth for backend selection.
|
|
9
|
+
*
|
|
10
|
+
* Consumers compose these into their `vite.config.ts`:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const cross_backend_projects = process.env.FUZ_TEST_CROSS_BACKEND
|
|
14
|
+
* ? [
|
|
15
|
+
* make_cross_backend_project({name: 'cross_backend_ts_deno', global_setup: GLOBAL_SETUP}),
|
|
16
|
+
* make_cross_backend_project({name: 'cross_backend_rust', global_setup: GLOBAL_SETUP}),
|
|
17
|
+
* ]
|
|
18
|
+
* : [];
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* where `GLOBAL_SETUP = './src/test/cross_backend/global_setup.ts'`.
|
|
22
|
+
*
|
|
23
|
+
* This module is intentionally dependency-free and `assert_dev_env`-free:
|
|
24
|
+
* it runs at vite **config** time (including production builds, where the
|
|
25
|
+
* consumer gates the projects behind an env flag), so it must not pull in
|
|
26
|
+
* the DEV-only test runtime.
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
/** Default test-file globs — the convention is `src/test/cross_backend/*.cross.test.ts`. */
|
|
31
|
+
const DEFAULT_INCLUDE = ['src/test/cross_backend/*.cross.test.ts'];
|
|
32
|
+
/** vitest `sequence.groupOrder` for cross-backend projects — after unit (1) + db (2). */
|
|
33
|
+
const DEFAULT_GROUP_ORDER = 3;
|
|
34
|
+
/**
|
|
35
|
+
* Build a single cross-backend vitest project config. Spread the results
|
|
36
|
+
* into `test.projects` in the consumer's `vite.config.ts`. `isolate: false`
|
|
37
|
+
* + `fileParallelism: false` because a project shares one spawned backend
|
|
38
|
+
* across its files.
|
|
39
|
+
*/
|
|
40
|
+
export const make_cross_backend_project = ({ name, global_setup, include = DEFAULT_INCLUDE, exclude = [], group_order = DEFAULT_GROUP_ORDER, }) => ({
|
|
41
|
+
extends: true,
|
|
42
|
+
test: {
|
|
43
|
+
name,
|
|
44
|
+
include: [...include],
|
|
45
|
+
exclude: [...exclude],
|
|
46
|
+
globalSetup: [global_setup],
|
|
47
|
+
isolate: false,
|
|
48
|
+
fileParallelism: false,
|
|
49
|
+
sequence: { groupOrder: group_order },
|
|
50
|
+
},
|
|
51
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { CellCrossTestOptions } from './cell_cross_helpers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Options for the origin parity suite. Shares the shape of the cell /
|
|
5
|
+
* account-lifecycle suites (`setup_test` / `capabilities` / `rpc_path`);
|
|
6
|
+
* reuses `CellCrossTestOptions` rather than minting a structural duplicate.
|
|
7
|
+
*/
|
|
8
|
+
export type OriginCrossTestOptions = CellCrossTestOptions;
|
|
9
|
+
export declare const describe_origin_cross_tests: (options: OriginCrossTestOptions) => void;
|
|
10
|
+
//# sourceMappingURL=origin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/origin.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkC9B,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAGlE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAM1D,eAAO,MAAM,2BAA2B,GAAI,SAAS,sBAAsB,KAAG,IAwC7E,CAAC"}
|