@fuzdev/fuz_app 0.110.1 → 0.112.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/CLAUDE.md +15 -11
- package/dist/actions/action_rpc.d.ts +3 -3
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +4 -6
- package/dist/actions/peer_ping.d.ts +8 -0
- package/dist/actions/peer_ping.d.ts.map +1 -1
- package/dist/actions/peer_ping.js +8 -0
- package/dist/actions/peer_request.d.ts +1 -1
- package/dist/actions/peer_request.js +1 -1
- package/dist/actions/perform_action.d.ts +16 -9
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +30 -21
- package/dist/actions/register_action_ws.d.ts +1 -1
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +88 -21
- package/dist/auth/CLAUDE.md +59 -10
- package/dist/auth/account_action_specs.d.ts +0 -2
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +10 -1
- package/dist/auth/account_queries.d.ts +22 -0
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +70 -16
- package/dist/auth/account_schema.d.ts +27 -9
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +23 -3
- package/dist/auth/actor_search_actions.d.ts +3 -3
- package/dist/auth/actor_search_actions.d.ts.map +1 -1
- package/dist/auth/actor_search_actions.js +10 -6
- package/dist/auth/admin_action_specs.d.ts +0 -2
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/api_token_queries.d.ts +9 -4
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +36 -8
- package/dist/auth/app_settings_queries.d.ts +8 -0
- package/dist/auth/app_settings_queries.d.ts.map +1 -1
- package/dist/auth/app_settings_queries.js +16 -3
- package/dist/auth/audit_log_queries.d.ts +21 -3
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +38 -7
- package/dist/auth/audit_log_schema.d.ts +0 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/auth_ddl.d.ts +6 -0
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +9 -0
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +3 -8
- package/dist/auth/cell_audit_actions.d.ts.map +1 -1
- package/dist/auth/cell_audit_actions.js +1 -1
- package/dist/auth/cell_field_actions.js +1 -1
- package/dist/auth/cell_grant_actions.js +1 -1
- package/dist/auth/cell_item_actions.js +1 -1
- package/dist/auth/invite_queries.d.ts +10 -0
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +28 -7
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +10 -0
- package/dist/auth/role_grant_offer_ddl.d.ts +34 -2
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_ddl.js +61 -2
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +48 -53
- package/dist/auth/role_grant_queries.d.ts +18 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +58 -18
- package/dist/auth/session_queries.d.ts +12 -3
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +26 -6
- package/dist/db/CLAUDE.md +34 -0
- package/dist/db/cell_audit_queries.d.ts.map +1 -1
- package/dist/db/cell_audit_queries.js +3 -1
- package/dist/db/cell_field_queries.d.ts +12 -3
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +25 -7
- package/dist/db/cell_grant_queries.d.ts +10 -2
- package/dist/db/cell_grant_queries.d.ts.map +1 -1
- package/dist/db/cell_grant_queries.js +29 -11
- package/dist/db/cell_item_queries.d.ts +13 -2
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +27 -7
- package/dist/db/cell_queries.d.ts +32 -3
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +57 -22
- package/dist/db/fact_queries.d.ts +7 -0
- package/dist/db/fact_queries.d.ts.map +1 -1
- package/dist/db/fact_queries.js +27 -4
- package/dist/db/schema_ready.d.ts +6 -1
- package/dist/db/schema_ready.d.ts.map +1 -1
- package/dist/db/schema_ready.js +6 -2
- package/dist/db/sql_columns.d.ts +144 -0
- package/dist/db/sql_columns.d.ts.map +1 -0
- package/dist/db/sql_columns.js +164 -0
- package/dist/db/sql_identifier.d.ts +6 -5
- package/dist/db/sql_identifier.d.ts.map +1 -1
- package/dist/db/sql_identifier.js +6 -5
- package/dist/http/CLAUDE.md +4 -2
- package/dist/http/jsonrpc_helpers.d.ts +13 -0
- package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
- package/dist/http/jsonrpc_helpers.js +13 -0
- package/dist/http/route_spec.d.ts +2 -2
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +1 -2
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +7 -2
- package/dist/testing/CLAUDE.md +55 -28
- package/dist/testing/admin_integration.d.ts +0 -3
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +0 -1
- package/dist/testing/adversarial_404.d.ts.map +1 -1
- package/dist/testing/adversarial_404.js +4 -1
- package/dist/testing/adversarial_input.d.ts.map +1 -1
- package/dist/testing/adversarial_input.js +5 -4
- package/dist/testing/app_server.d.ts +4 -3
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +11 -5
- package/dist/testing/attack_surface.d.ts +16 -0
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +36 -13
- package/dist/testing/audit_completeness.d.ts +0 -3
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +0 -1
- package/dist/testing/auth_apps.d.ts +9 -2
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +9 -2
- package/dist/testing/cross_backend/account_lifecycle.d.ts +5 -4
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +7 -0
- package/dist/testing/cross_backend/action_manifest.d.ts +19 -3
- package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
- package/dist/testing/cross_backend/action_manifest.js +10 -4
- package/dist/testing/cross_backend/action_manifest_parity.d.ts +9 -3
- package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -1
- package/dist/testing/cross_backend/action_manifest_parity.js +6 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts +3 -2
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.d.ts +4 -2
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +1 -1
- package/dist/testing/cross_backend/cell_crud.d.ts +2 -2
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +10 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts +3 -3
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_grant_role.d.ts +2 -2
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.d.ts +2 -2
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/expected_schema.json +1 -1
- package/dist/testing/cross_backend/fact_serving.d.ts +2 -2
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts +17 -2
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -1
- package/dist/testing/cross_backend/make_cross_backend_project.js +23 -3
- package/dist/testing/cross_backend/origin.d.ts +4 -2
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +11 -7
- package/dist/testing/cross_backend/query_shape.d.ts +6 -5
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/ready.d.ts +2 -2
- package/dist/testing/cross_backend/ready.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_enumeration.js +7 -1
- package/dist/testing/cross_backend/setup.d.ts +26 -6
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.d.ts +5 -1
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +1 -5
- package/dist/testing/cross_backend/testing_backdoor.js +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_lifetime.js +6 -0
- package/dist/testing/cross_backend/token_scope_surface.d.ts +2 -2
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/wire_shapes.d.ts +22 -0
- package/dist/testing/cross_backend/wire_shapes.d.ts.map +1 -0
- package/dist/testing/cross_backend/wire_shapes.js +41 -0
- package/dist/testing/data_exposure.d.ts +0 -3
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +0 -1
- package/dist/testing/db.d.ts +62 -7
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +86 -28
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +15 -2
- package/dist/testing/round_trip.d.ts +0 -3
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +0 -4
- package/dist/testing/rpc_attack_surface.js +2 -2
- package/dist/testing/rpc_round_trip.d.ts +0 -3
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +0 -1
- package/dist/testing/schema_generators.d.ts +15 -0
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +45 -11
- package/dist/testing/standard.d.ts +5 -1
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +2 -7
- package/dist/timestamp.d.ts +58 -0
- package/dist/timestamp.d.ts.map +1 -0
- package/dist/timestamp.js +57 -0
- package/dist/ui/AdminOverview.svelte +5 -10
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/RoleGrantOfferHistory.svelte +3 -1
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.js +6 -2
- package/package.json +1 -1
package/dist/testing/db.d.ts
CHANGED
|
@@ -15,27 +15,64 @@ export interface DbFactory {
|
|
|
15
15
|
skip_reason?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Reset a
|
|
18
|
+
* Reset a test database to a clean state by dropping and recreating the public schema.
|
|
19
19
|
*
|
|
20
20
|
* Removes all tables, sequences, indexes, types, and functions.
|
|
21
|
-
* The database instance remains usable after reset.
|
|
21
|
+
* The database instance remains usable after reset. Despite the name it issues
|
|
22
|
+
* plain SQL, so it also resets the other drivers the test factories hand out.
|
|
22
23
|
*
|
|
23
24
|
* @mutates db - drops the `public` schema and recreates it; all rows in all
|
|
24
25
|
* tables are gone after this returns.
|
|
25
26
|
*/
|
|
26
27
|
export declare const reset_pglite: (db: Db) => Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Builds a `DbFactory` from a schema initializer — `create_pglite_factory`'s own shape.
|
|
30
|
+
*/
|
|
31
|
+
export type DbFactoryBuilder = (init_schema: (db: Db) => Promise<void>) => DbFactory;
|
|
32
|
+
/**
|
|
33
|
+
* Options for `create_pglite_factory`.
|
|
34
|
+
*/
|
|
35
|
+
export interface CreatePgliteFactoryOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Whether an installed substitute (`set_substitute_db_factory`) may stand in
|
|
38
|
+
* for the PGlite factory at this call site. Defaults to `true`; pass `false`
|
|
39
|
+
* where PGlite itself is the subject under test.
|
|
40
|
+
*/
|
|
41
|
+
substitutable?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Install a builder that stands in for `create_pglite_factory`, so suites
|
|
45
|
+
* written against PGlite can run against another driver without being edited.
|
|
46
|
+
*
|
|
47
|
+
* Injected rather than selected here: the drivers a substitute wraps live in
|
|
48
|
+
* test-side code this shipped module cannot import. A call site that passes
|
|
49
|
+
* `{substitutable: false}` keeps PGlite regardless. Pass `null` to uninstall.
|
|
50
|
+
*
|
|
51
|
+
* The slot is read when a factory is built, not when it creates a database,
|
|
52
|
+
* so install before the first `create_pglite_factory` call — a vitest
|
|
53
|
+
* `setupFiles` entry, which runs ahead of every test file's imports, is the
|
|
54
|
+
* shape fuz_app's own `db` project uses. A factory built earlier (including
|
|
55
|
+
* the module-level fallback in `testing/app_server.ts`) keeps the driver it
|
|
56
|
+
* was built with, and uninstalling does not revert it.
|
|
57
|
+
*
|
|
58
|
+
* @param create_factory - the builder to install, or `null` to remove the installed one
|
|
59
|
+
*/
|
|
60
|
+
export declare const set_substitute_db_factory: (create_factory: DbFactoryBuilder | null) => void;
|
|
27
61
|
/**
|
|
28
62
|
* Create a pglite (in-memory) database factory for tests.
|
|
29
63
|
*
|
|
30
64
|
* Always enabled — no external dependencies required.
|
|
31
|
-
* Shares a single PGlite WASM instance across
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
65
|
+
* Shares a single PGlite WASM instance across the PGlite factories this module builds.
|
|
66
|
+
* Subsequent `create()` calls reset the schema via `DROP SCHEMA public CASCADE`
|
|
67
|
+
* instead of paying the WASM cold-start cost again.
|
|
68
|
+
*
|
|
69
|
+
* Returns the installed substitute's factory instead when one is installed and
|
|
70
|
+
* `options.substitutable` is not `false` — see `set_substitute_db_factory`.
|
|
35
71
|
*
|
|
36
72
|
* @param init_schema - callback to initialize the database schema
|
|
73
|
+
* @param options - `substitutable: false` pins this call site to PGlite
|
|
37
74
|
*/
|
|
38
|
-
export declare const create_pglite_factory: (init_schema: (db: Db) => Promise<void
|
|
75
|
+
export declare const create_pglite_factory: (init_schema: (db: Db) => Promise<void>, options?: CreatePgliteFactoryOptions) => DbFactory;
|
|
39
76
|
/**
|
|
40
77
|
* Create a pg (PostgreSQL) database factory for tests.
|
|
41
78
|
*
|
|
@@ -103,4 +140,22 @@ export declare const create_describe_db: (factories: DbFactory | Array<DbFactory
|
|
|
103
140
|
* Log factory status to console.
|
|
104
141
|
*/
|
|
105
142
|
export declare const log_db_factory_status: (factories: Array<DbFactory>) => void;
|
|
143
|
+
/**
|
|
144
|
+
* Assert a `*_COLUMNS` projection const names exactly the live columns of
|
|
145
|
+
* `table` — the drift guard for an exported const (`ACCOUNT_COLUMNS`,
|
|
146
|
+
* `CELL_COLUMNS`, a consumer's own).
|
|
147
|
+
*
|
|
148
|
+
* This is the reverse of the fail-loud read: a named projection makes a
|
|
149
|
+
* *dropped* column fail loud at query time, but a column *added* to the table
|
|
150
|
+
* and not to the projection would silently vanish from every row read. Both
|
|
151
|
+
* directions are covered by the equality here. It guards one const; it can't
|
|
152
|
+
* see a table that has *no* const — for that, keep a table → const registry
|
|
153
|
+
* and assert its key set against `query_public_columns` (fuz_app's own is
|
|
154
|
+
* `src/test/db/column_projections.db.test.ts`).
|
|
155
|
+
*
|
|
156
|
+
* @param db - a bootstrapped test database
|
|
157
|
+
* @param table - the `public`-schema table the const projects
|
|
158
|
+
* @param columns - the projection const's column names
|
|
159
|
+
*/
|
|
160
|
+
export declare const assert_columns_match_live: (db: Db, table: string, columns: ReadonlyArray<string>) => Promise<void>;
|
|
106
161
|
//# sourceMappingURL=db.d.ts.map
|
package/dist/testing/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA8B7B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA8B7B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAMtC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAQF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,yBAAyB,GAAI,gBAAgB,gBAAgB,GAAG,IAAI,KAAG,IAEnF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GACjC,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,UAAU,0BAA0B,KAClC,SAyBF,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SAgEF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAG3D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAC9B,WAAW,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,EACvC,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,IAAI,CAwBzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,CAAC,SAAS,CAAC,KAAG,IAQnE,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,yBAAyB,GACrC,IAAI,EAAE,EACN,OAAO,MAAM,EACb,SAAS,aAAa,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,IAAI,CAId,CAAC"}
|
package/dist/testing/db.js
CHANGED
|
@@ -22,20 +22,22 @@ import './assert_dev_env.js';
|
|
|
22
22
|
*
|
|
23
23
|
* @module
|
|
24
24
|
*/
|
|
25
|
-
import { describe, beforeAll, beforeEach, afterAll } from 'vitest';
|
|
25
|
+
import { describe, beforeAll, beforeEach, afterAll, assert } from 'vitest';
|
|
26
26
|
import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
27
27
|
import { create_pglite_db } from '../db/db_pglite.js';
|
|
28
28
|
import { assert_valid_sql_identifier } from '../db/sql_identifier.js';
|
|
29
29
|
import { create_pg_db, register_pg_type_parsers } from '../db/db_pg.js';
|
|
30
|
+
import { query_public_columns } from '../db/schema_ready.js';
|
|
30
31
|
/**
|
|
31
32
|
* CI detection — `CI=true` is set automatically by GitHub Actions, GitLab CI, etc.
|
|
32
33
|
*/
|
|
33
34
|
export const IS_CI = process.env.CI === 'true';
|
|
34
35
|
/**
|
|
35
|
-
* Reset a
|
|
36
|
+
* Reset a test database to a clean state by dropping and recreating the public schema.
|
|
36
37
|
*
|
|
37
38
|
* Removes all tables, sequences, indexes, types, and functions.
|
|
38
|
-
* The database instance remains usable after reset.
|
|
39
|
+
* The database instance remains usable after reset. Despite the name it issues
|
|
40
|
+
* plain SQL, so it also resets the other drivers the test factories hand out.
|
|
39
41
|
*
|
|
40
42
|
* @mutates db - drops the `public` schema and recreates it; all rows in all
|
|
41
43
|
* tables are gone after this returns.
|
|
@@ -44,40 +46,74 @@ export const reset_pglite = async (db) => {
|
|
|
44
46
|
await db.query('DROP SCHEMA public CASCADE');
|
|
45
47
|
await db.query('CREATE SCHEMA public');
|
|
46
48
|
};
|
|
47
|
-
// Module-level PGlite cache —
|
|
48
|
-
//
|
|
49
|
+
// Module-level PGlite cache — one instance shared by the PGlite factories this
|
|
50
|
+
// module builds, for as long as the module is loaded. Under the `db` project's
|
|
51
|
+
// `isolate: false` + `fileParallelism: false` that is the whole run; a project
|
|
52
|
+
// that isolates its files gets one per worker instead.
|
|
49
53
|
let module_db = null;
|
|
54
|
+
// The installed stand-in for `create_pglite_factory`, `null` when none is installed.
|
|
55
|
+
let substitute_db_factory = null;
|
|
56
|
+
/**
|
|
57
|
+
* Install a builder that stands in for `create_pglite_factory`, so suites
|
|
58
|
+
* written against PGlite can run against another driver without being edited.
|
|
59
|
+
*
|
|
60
|
+
* Injected rather than selected here: the drivers a substitute wraps live in
|
|
61
|
+
* test-side code this shipped module cannot import. A call site that passes
|
|
62
|
+
* `{substitutable: false}` keeps PGlite regardless. Pass `null` to uninstall.
|
|
63
|
+
*
|
|
64
|
+
* The slot is read when a factory is built, not when it creates a database,
|
|
65
|
+
* so install before the first `create_pglite_factory` call — a vitest
|
|
66
|
+
* `setupFiles` entry, which runs ahead of every test file's imports, is the
|
|
67
|
+
* shape fuz_app's own `db` project uses. A factory built earlier (including
|
|
68
|
+
* the module-level fallback in `testing/app_server.ts`) keeps the driver it
|
|
69
|
+
* was built with, and uninstalling does not revert it.
|
|
70
|
+
*
|
|
71
|
+
* @param create_factory - the builder to install, or `null` to remove the installed one
|
|
72
|
+
*/
|
|
73
|
+
export const set_substitute_db_factory = (create_factory) => {
|
|
74
|
+
substitute_db_factory = create_factory;
|
|
75
|
+
};
|
|
50
76
|
/**
|
|
51
77
|
* Create a pglite (in-memory) database factory for tests.
|
|
52
78
|
*
|
|
53
79
|
* Always enabled — no external dependencies required.
|
|
54
|
-
* Shares a single PGlite WASM instance across
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
80
|
+
* Shares a single PGlite WASM instance across the PGlite factories this module builds.
|
|
81
|
+
* Subsequent `create()` calls reset the schema via `DROP SCHEMA public CASCADE`
|
|
82
|
+
* instead of paying the WASM cold-start cost again.
|
|
83
|
+
*
|
|
84
|
+
* Returns the installed substitute's factory instead when one is installed and
|
|
85
|
+
* `options.substitutable` is not `false` — see `set_substitute_db_factory`.
|
|
58
86
|
*
|
|
59
87
|
* @param init_schema - callback to initialize the database schema
|
|
88
|
+
* @param options - `substitutable: false` pins this call site to PGlite
|
|
60
89
|
*/
|
|
61
|
-
export const create_pglite_factory = (init_schema) =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
async create() {
|
|
65
|
-
if (module_db) {
|
|
66
|
-
await reset_pglite(module_db);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
const { PGlite } = await import('@electric-sql/pglite');
|
|
70
|
-
const pglite = new PGlite();
|
|
71
|
-
module_db = create_pglite_db(pglite).db;
|
|
72
|
-
}
|
|
73
|
-
await init_schema(module_db);
|
|
74
|
-
return module_db;
|
|
75
|
-
},
|
|
76
|
-
async close() {
|
|
77
|
-
// No-op: shared instance lives for the worker thread's lifetime.
|
|
78
|
-
// PGlite is cleaned up when the vitest worker exits.
|
|
90
|
+
export const create_pglite_factory = (init_schema, options) => {
|
|
91
|
+
if (substitute_db_factory && options?.substitutable !== false) {
|
|
92
|
+
return substitute_db_factory(init_schema);
|
|
79
93
|
}
|
|
80
|
-
|
|
94
|
+
return {
|
|
95
|
+
name: 'pglite',
|
|
96
|
+
skip: false,
|
|
97
|
+
async create() {
|
|
98
|
+
if (module_db) {
|
|
99
|
+
await reset_pglite(module_db);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const { PGlite } = await import('@electric-sql/pglite');
|
|
103
|
+
const pglite = new PGlite();
|
|
104
|
+
module_db = create_pglite_db(pglite).db;
|
|
105
|
+
}
|
|
106
|
+
await init_schema(module_db);
|
|
107
|
+
return module_db;
|
|
108
|
+
},
|
|
109
|
+
async close() {
|
|
110
|
+
// No-op: the instance is the module-level cache above, so it lives as long
|
|
111
|
+
// as this module stays loaded — under the `db` project's `isolate: false` +
|
|
112
|
+
// `fileParallelism: false` that is the whole run. Nothing frees the PGlite
|
|
113
|
+
// instance earlier.
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
};
|
|
81
117
|
/**
|
|
82
118
|
* Create a pg (PostgreSQL) database factory for tests.
|
|
83
119
|
*
|
|
@@ -246,3 +282,25 @@ export const log_db_factory_status = (factories) => {
|
|
|
246
282
|
const skipped = factories.filter((f) => f.skip).map((f) => `${f.name} (${f.skip_reason})`);
|
|
247
283
|
console.log(`[db tests] drivers: ${enabled.join(', ')}${skipped.length ? ` | skipped: ${skipped.join(', ')}` : ''}`);
|
|
248
284
|
};
|
|
285
|
+
/**
|
|
286
|
+
* Assert a `*_COLUMNS` projection const names exactly the live columns of
|
|
287
|
+
* `table` — the drift guard for an exported const (`ACCOUNT_COLUMNS`,
|
|
288
|
+
* `CELL_COLUMNS`, a consumer's own).
|
|
289
|
+
*
|
|
290
|
+
* This is the reverse of the fail-loud read: a named projection makes a
|
|
291
|
+
* *dropped* column fail loud at query time, but a column *added* to the table
|
|
292
|
+
* and not to the projection would silently vanish from every row read. Both
|
|
293
|
+
* directions are covered by the equality here. It guards one const; it can't
|
|
294
|
+
* see a table that has *no* const — for that, keep a table → const registry
|
|
295
|
+
* and assert its key set against `query_public_columns` (fuz_app's own is
|
|
296
|
+
* `src/test/db/column_projections.db.test.ts`).
|
|
297
|
+
*
|
|
298
|
+
* @param db - a bootstrapped test database
|
|
299
|
+
* @param table - the `public`-schema table the const projects
|
|
300
|
+
* @param columns - the projection const's column names
|
|
301
|
+
*/
|
|
302
|
+
export const assert_columns_match_live = async (db, table, columns) => {
|
|
303
|
+
const live = await query_public_columns(db, { table });
|
|
304
|
+
assert.ok(live[table], `table "${table}" is not in the live public schema`);
|
|
305
|
+
assert.deepEqual([...columns].sort(), live[table]);
|
|
306
|
+
};
|
|
@@ -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,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,iCAAiC,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,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAKpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D;;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,IAs8CF,CAAC"}
|
|
@@ -25,6 +25,7 @@ import { account_verify_action_spec, account_session_list_action_spec, account_s
|
|
|
25
25
|
import { invite_create_action_spec } from '../auth/admin_action_specs.js';
|
|
26
26
|
import { LoginOutput, AccountStatusOutput } from '../auth/account_route_schema.js';
|
|
27
27
|
import { test_if } from './cross_backend/capabilities.js';
|
|
28
|
+
import { assert_iso8601_seconds, assert_iso8601_seconds_nullable } from './cross_backend/wire_shapes.js';
|
|
28
29
|
import { DEFAULT_TEST_PASSWORD } from './test_credentials.js';
|
|
29
30
|
/**
|
|
30
31
|
* Standard integration test suite for fuz_app auth routes.
|
|
@@ -343,6 +344,13 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
343
344
|
// Single-actor keeper: actor resolved, role_grants populated.
|
|
344
345
|
assert.ok(parsed.actor, 'single-actor keeper must resolve a non-null actor');
|
|
345
346
|
assert.ok(parsed.role_grants.length > 0, 'single-actor keeper must have populated role_grants');
|
|
347
|
+
// Byte-shape gate: `z.string()` accepts a millisecond stamp too,
|
|
348
|
+
// so the schema parse above can't tell the two spines apart.
|
|
349
|
+
assert_iso8601_seconds(parsed.account.created_at, 'account.created_at');
|
|
350
|
+
for (const grant of parsed.role_grants) {
|
|
351
|
+
assert_iso8601_seconds(grant.created_at, 'role_grant.created_at');
|
|
352
|
+
assert_iso8601_seconds_nullable(grant.expires_at, 'role_grant.expires_at');
|
|
353
|
+
}
|
|
346
354
|
});
|
|
347
355
|
});
|
|
348
356
|
// --- 2. Cookie attributes ---
|
|
@@ -419,7 +427,12 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
419
427
|
});
|
|
420
428
|
assert.ok(list_res.ok, 'account_session_list should succeed');
|
|
421
429
|
assert.ok(list_res.result.sessions.length >= 1);
|
|
422
|
-
const
|
|
430
|
+
const session = list_res.result.sessions[0];
|
|
431
|
+
// Byte-shape gate on the session timestamps — see the account-status
|
|
432
|
+
// case for why the output schema alone can't catch the divergence.
|
|
433
|
+
assert_iso8601_seconds(session.created_at, 'session.created_at');
|
|
434
|
+
assert_iso8601_seconds(session.expires_at, 'session.expires_at');
|
|
435
|
+
const session_id = session.id;
|
|
423
436
|
// Revoke that session by ID
|
|
424
437
|
const revoke_res = await rpc_call_for_spec({
|
|
425
438
|
app: { request: fixture.transport },
|
|
@@ -691,7 +704,7 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
691
704
|
// phase doesn't first demand an acting actor → `actor_required`)
|
|
692
705
|
// and input/param/query-free (params and query still validate ahead
|
|
693
706
|
// of the auth phase — only body validation moved behind the gates,
|
|
694
|
-
// per the 401 → authz → 403 → 400 order). Admin surfaces are
|
|
707
|
+
// per the 401 → authz → 403 → 429 → 400 order). Admin surfaces are
|
|
695
708
|
// RPC-first now; a consumer may expose no such REST route (fuz_app
|
|
696
709
|
// itself, zzz) — then this REST-era check is a no-op and RPC denial
|
|
697
710
|
// is covered by `describe_rpc_attack_surface_tests`.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
|
-
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
3
2
|
import type { SetupTest, TestFixture } from './cross_backend/setup.js';
|
|
4
3
|
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
5
4
|
/** Options for `describe_round_trip_validation`. */
|
|
@@ -16,8 +15,6 @@ export interface RoundTripTestOptions {
|
|
|
16
15
|
* TS by the consumer; same shape for in-process and cross-process tests.
|
|
17
16
|
*/
|
|
18
17
|
surface_source: AppSurfaceSpec;
|
|
19
|
-
/** Backend capability declarations — see `cross_backend/capabilities.ts`. */
|
|
20
|
-
capabilities: BackendCapabilities;
|
|
21
18
|
/** Routes to skip, in `'METHOD /path'` format. */
|
|
22
19
|
skip_routes?: Array<string>;
|
|
23
20
|
/** Override generated bodies for specific routes (`'METHOD /path'` → body). */
|
|
@@ -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;AA2B7B,OAAO,KAAK,EAAE,
|
|
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,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,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;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CACnF,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,oBAAoB,KAAG,IAyI9E,CAAC"}
|
|
@@ -36,10 +36,6 @@ import { resolve_valid_path, generate_valid_body } from './schema_generators.js'
|
|
|
36
36
|
export const describe_round_trip_validation = (options) => {
|
|
37
37
|
const describe_time_specs = options.surface_source.route_specs;
|
|
38
38
|
const skip_set = new Set(options.skip_routes);
|
|
39
|
-
// `capabilities` is currently unused by this suite (no in-process-only
|
|
40
|
-
// reads, no transport-gated cases) but stays on the options for
|
|
41
|
-
// uniformity with the other Tier 1 suites.
|
|
42
|
-
void options.capabilities;
|
|
43
39
|
describe('round-trip validation', () => {
|
|
44
40
|
let fixture;
|
|
45
41
|
let authed_account;
|
|
@@ -84,8 +84,8 @@ const describe_rpc_auth = (options) => {
|
|
|
84
84
|
if (!app)
|
|
85
85
|
throw new Error(`No test app for role '${wrong_role}'`);
|
|
86
86
|
// Send valid params so the probe varies only the role. Dispatcher
|
|
87
|
-
// ordering is 401 → authz → 403 → 400, so the role gate
|
|
88
|
-
// ahead of validation regardless; valid params also keep the
|
|
87
|
+
// ordering is 401 → authz → 403 → 429 → 400, so the role gate
|
|
88
|
+
// trips ahead of validation regardless; valid params also keep the
|
|
89
89
|
// authorization phase from answering `actor_required` first.
|
|
90
90
|
const action = find_rpc_action(rpc_endpoint_specs, endpoint.path, method.name);
|
|
91
91
|
const params = action ? generate_valid_body(action.spec.input) : undefined;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
2
|
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
3
3
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
4
|
-
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
5
4
|
import type { SetupTest, TestFixture } from './cross_backend/setup.js';
|
|
6
5
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
7
6
|
/** Options for `describe_rpc_round_trip_tests`. */
|
|
@@ -14,8 +13,6 @@ export interface RpcRoundTripTestOptions {
|
|
|
14
13
|
* in-process and cross-process tests.
|
|
15
14
|
*/
|
|
16
15
|
surface_source: AppSurfaceSpec;
|
|
17
|
-
/** Backend capability declarations. */
|
|
18
|
-
capabilities: BackendCapabilities;
|
|
19
16
|
/**
|
|
20
17
|
* Session config — only needed to resolve factory-form `rpc_endpoints`
|
|
21
18
|
* against a stub `AppServerContext` at setup time (the actions' input
|
|
@@ -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,EAAE,cAAc,EAAuB,MAAM,oBAAoB,CAAC;AAE9E,OAAO,EAQN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,
|
|
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,EAAE,cAAc,EAAuB,MAAM,oBAAoB,CAAC;AAE9E,OAAO,EAQN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B;;;;;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,IA+JhF,CAAC"}
|
|
@@ -88,7 +88,6 @@ export const describe_rpc_round_trip_tests = (options) => {
|
|
|
88
88
|
// match what the running backend serves.
|
|
89
89
|
const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
90
90
|
const surface_rpc_endpoints = options.surface_source.surface.rpc_endpoints;
|
|
91
|
-
void options.capabilities;
|
|
92
91
|
describe('RPC round-trip validation', () => {
|
|
93
92
|
let fixture;
|
|
94
93
|
let authed_account;
|
|
@@ -20,6 +20,21 @@ export declare const generate_valid_value: (field: ZodFieldInfo, field_schema: z
|
|
|
20
20
|
/**
|
|
21
21
|
* Resolve a route path with valid-ish param values so params validation passes.
|
|
22
22
|
* Used when testing input on routes that also have params.
|
|
23
|
+
*
|
|
24
|
+
* Without a params schema (or for a param the schema doesn't name) each
|
|
25
|
+
* `:name` becomes `test_<name>` — fine for an unconstrained segment. With
|
|
26
|
+
* one, `test_<name>` is still preferred wherever the field schema accepts
|
|
27
|
+
* it, because the param name in the URL is what makes a failure message
|
|
28
|
+
* readable. Only a segment that refuses it falls through: a `uuid` param
|
|
29
|
+
* takes the nil UUID (the fast path — by far the most common shape), and
|
|
30
|
+
* anything else is synthesized by `generate_valid_string`, so a
|
|
31
|
+
* format-constrained segment (a `blake3:…` fact hash, a `tok_…` id)
|
|
32
|
+
* resolves to a value its schema accepts instead of a `test_<name>` that
|
|
33
|
+
* 400s before the route's real gates run.
|
|
34
|
+
*
|
|
35
|
+
* The synthesized value is percent-encoded, and a value containing `/` is
|
|
36
|
+
* refused (it would silently add a path segment and route elsewhere) —
|
|
37
|
+
* both fall back to `test_<name>`.
|
|
23
38
|
*/
|
|
24
39
|
export declare const resolve_valid_path: (path: string, params_schema?: z.ZodObject) => string;
|
|
25
40
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAkBhE,CAAC;
|
|
1
|
+
{"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAkBhE,CAAC;AAmHF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OAgGnF,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAiB9E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SA+B5B,CAAC"}
|
|
@@ -41,19 +41,32 @@ export const detect_format = (field_schema) => {
|
|
|
41
41
|
return null;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
* Extract a candidate value from a JSON Schema `pattern` when the shape is
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
44
|
+
* Extract a candidate value from a JSON Schema `pattern` when the shape is a
|
|
45
|
+
* fixed-length hex character class, optionally behind a literal prefix —
|
|
46
|
+
* covers bare digests (blake3 64-char lowercase hex, sha256, md5) and the
|
|
47
|
+
* algorithm-tagged form the fact store addresses bytes by
|
|
48
|
+
* (`^blake3:[0-9a-f]{64}$` → `blake3:` + 64 zeros).
|
|
49
|
+
*
|
|
50
|
+
* Coverage today:
|
|
51
|
+
* - Optional prefix: alphanumeric, starting with a letter, ending in `:`
|
|
52
|
+
* (`blake3:`, `sha256:`). A `_`-joined prefix over a base64url class is
|
|
53
|
+
* the sibling shape — see `generate_prefix_slug_pattern_value`.
|
|
54
|
+
* - Character class must be exactly `[0-9a-f]` or `[0-9a-fA-F]` (Zod passes
|
|
55
|
+
* the regex source through verbatim — no character-class reordering).
|
|
56
|
+
* - Fixed-length quantifier `{N}`.
|
|
57
|
+
*
|
|
58
|
+
* Returns `null` when the pattern doesn't match the expected shape; the
|
|
59
|
+
* caller then falls through to its other candidates.
|
|
48
60
|
*/
|
|
49
61
|
const generate_hex_pattern_value = (pattern) => {
|
|
50
|
-
const match =
|
|
62
|
+
const match = /^\^(?:([A-Za-z][A-Za-z0-9]*):)?\[0-9a-f(?:A-F)?\]\{(\d+)\}\$$/.exec(pattern);
|
|
51
63
|
if (!match)
|
|
52
64
|
return null;
|
|
53
|
-
const n = Number(match[
|
|
65
|
+
const n = Number(match[2]);
|
|
54
66
|
if (!Number.isInteger(n) || n <= 0)
|
|
55
67
|
return null;
|
|
56
|
-
|
|
68
|
+
const prefix = match[1] === undefined ? '' : `${match[1]}:`;
|
|
69
|
+
return prefix + '0'.repeat(n);
|
|
57
70
|
};
|
|
58
71
|
/**
|
|
59
72
|
* Extract a candidate value from a JSON Schema `pattern` when the shape is a
|
|
@@ -78,8 +91,8 @@ const generate_hex_pattern_value = (pattern) => {
|
|
|
78
91
|
* doesn't match the detection regex. `x`-fill would still satisfy the
|
|
79
92
|
* refinement if the detection were widened.
|
|
80
93
|
* - No-prefix fixed-length slugs (e.g. `^[A-Za-z0-9_-]{43}$` — daemon
|
|
81
|
-
* token shape) are not matched here;
|
|
82
|
-
*
|
|
94
|
+
* token shape) are not matched here; `generate_hex_pattern_value` is the
|
|
95
|
+
* hex sibling, and it covers both the bare and the `prefix:`-tagged form.
|
|
83
96
|
* Widen the detection regex when a new branded shape surfaces.
|
|
84
97
|
*/
|
|
85
98
|
const generate_prefix_slug_pattern_value = (pattern) => {
|
|
@@ -254,22 +267,43 @@ export const generate_valid_value = (field, field_schema) => {
|
|
|
254
267
|
/**
|
|
255
268
|
* Resolve a route path with valid-ish param values so params validation passes.
|
|
256
269
|
* Used when testing input on routes that also have params.
|
|
270
|
+
*
|
|
271
|
+
* Without a params schema (or for a param the schema doesn't name) each
|
|
272
|
+
* `:name` becomes `test_<name>` — fine for an unconstrained segment. With
|
|
273
|
+
* one, `test_<name>` is still preferred wherever the field schema accepts
|
|
274
|
+
* it, because the param name in the URL is what makes a failure message
|
|
275
|
+
* readable. Only a segment that refuses it falls through: a `uuid` param
|
|
276
|
+
* takes the nil UUID (the fast path — by far the most common shape), and
|
|
277
|
+
* anything else is synthesized by `generate_valid_string`, so a
|
|
278
|
+
* format-constrained segment (a `blake3:…` fact hash, a `tok_…` id)
|
|
279
|
+
* resolves to a value its schema accepts instead of a `test_<name>` that
|
|
280
|
+
* 400s before the route's real gates run.
|
|
281
|
+
*
|
|
282
|
+
* The synthesized value is percent-encoded, and a value containing `/` is
|
|
283
|
+
* refused (it would silently add a path segment and route elsewhere) —
|
|
284
|
+
* both fall back to `test_<name>`.
|
|
257
285
|
*/
|
|
258
286
|
export const resolve_valid_path = (path, params_schema) => {
|
|
259
287
|
if (!params_schema) {
|
|
260
288
|
return path.replace(/:(\w+)/g, 'test_$1');
|
|
261
289
|
}
|
|
262
290
|
return path.replace(/:(\w+)/g, (_match, name) => {
|
|
291
|
+
const fallback = `test_${name}`;
|
|
263
292
|
const field_schema = params_schema.shape[name];
|
|
264
293
|
if (!field_schema)
|
|
265
|
-
return
|
|
294
|
+
return fallback;
|
|
295
|
+
if (field_schema.safeParse(fallback).success)
|
|
296
|
+
return fallback;
|
|
266
297
|
const base_type = zod_get_base_type(field_schema);
|
|
267
298
|
if (base_type === 'uuid')
|
|
268
299
|
return '00000000-0000-0000-0000-000000000000';
|
|
269
300
|
const format = detect_format(field_schema);
|
|
270
301
|
if (format === 'uuid')
|
|
271
302
|
return '00000000-0000-0000-0000-000000000000';
|
|
272
|
-
|
|
303
|
+
const generated = generate_valid_string(field_schema);
|
|
304
|
+
if (generated.includes('/'))
|
|
305
|
+
return fallback;
|
|
306
|
+
return encodeURIComponent(generated);
|
|
273
307
|
});
|
|
274
308
|
};
|
|
275
309
|
/**
|
|
@@ -37,7 +37,11 @@ export interface StandardTestOptions {
|
|
|
37
37
|
* in-process and cross-process tests.
|
|
38
38
|
*/
|
|
39
39
|
surface_source: AppSurfaceSpec;
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Backend capability declarations. Forwarded to the one bundled suite
|
|
42
|
+
* that gates on them (`describe_standard_integration_tests`); the other
|
|
43
|
+
* suites in the bundle read no flags and take no capability field.
|
|
44
|
+
*/
|
|
41
45
|
capabilities: BackendCapabilities;
|
|
42
46
|
/** Session config — needed for cookie_name + factory-form rpc_endpoints resolution. */
|
|
43
47
|
session_options: SessionOptions<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/standard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AASvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B
|
|
1
|
+
{"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/standard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AASvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B;;;;OAIG;IACH,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,sEAAsE;IACtE,yBAAyB,CAAC,EAAE,eAAe,CAAC;IAC5C;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,mBAAmB,KAAG,IAsDtE,CAAC"}
|
package/dist/testing/standard.js
CHANGED
|
@@ -24,20 +24,17 @@ export const describe_standard_tests = (options) => {
|
|
|
24
24
|
});
|
|
25
25
|
describe_round_trip_validation({
|
|
26
26
|
setup_test: options.setup_test,
|
|
27
|
-
surface_source: options.surface_source
|
|
28
|
-
capabilities: options.capabilities
|
|
27
|
+
surface_source: options.surface_source
|
|
29
28
|
});
|
|
30
29
|
describe_rpc_round_trip_tests({
|
|
31
30
|
setup_test: options.setup_test,
|
|
32
31
|
surface_source: options.surface_source,
|
|
33
|
-
capabilities: options.capabilities,
|
|
34
32
|
session_options: options.session_options,
|
|
35
33
|
rpc_endpoints: options.rpc_endpoints
|
|
36
34
|
});
|
|
37
35
|
describe_data_exposure_tests({
|
|
38
36
|
setup_test: options.setup_test,
|
|
39
|
-
surface_source: options.surface_source
|
|
40
|
-
capabilities: options.capabilities
|
|
37
|
+
surface_source: options.surface_source
|
|
41
38
|
});
|
|
42
39
|
describe_rate_limiting_tests({
|
|
43
40
|
session_options: options.session_options,
|
|
@@ -49,7 +46,6 @@ export const describe_standard_tests = (options) => {
|
|
|
49
46
|
describe_standard_admin_integration_tests({
|
|
50
47
|
setup_test: options.setup_test,
|
|
51
48
|
surface_source: options.surface_source,
|
|
52
|
-
capabilities: options.capabilities,
|
|
53
49
|
session_options: options.session_options,
|
|
54
50
|
roles: options.roles,
|
|
55
51
|
rpc_endpoints: options.rpc_endpoints,
|
|
@@ -58,7 +54,6 @@ export const describe_standard_tests = (options) => {
|
|
|
58
54
|
describe_audit_completeness_tests({
|
|
59
55
|
setup_test: options.setup_test,
|
|
60
56
|
surface_source: options.surface_source,
|
|
61
|
-
capabilities: options.capabilities,
|
|
62
57
|
session_options: options.session_options,
|
|
63
58
|
rpc_endpoints: options.rpc_endpoints
|
|
64
59
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical wire timestamp shape shared by the two spine implementations.
|
|
3
|
+
*
|
|
4
|
+
* Every timestamp that reaches the wire is second-precision UTC —
|
|
5
|
+
* `YYYY-MM-DDTHH:MM:SSZ`, exactly 20 characters. Timestamps read from
|
|
6
|
+
* Postgres get that shape from the SQL projection
|
|
7
|
+
* (`iso8601_timestamp_column` in `db/sql_columns.ts`, twin of
|
|
8
|
+
* `fuz_db::iso8601_timestamp_column`); this module is the same shape for the
|
|
9
|
+
* stamps minted in TS rather than read back from a row, so the two sources
|
|
10
|
+
* agree and the two backends serialize the same instant to the same bytes.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The canonical wire timestamp shape as a regex — second-precision UTC,
|
|
16
|
+
* exactly 20 characters.
|
|
17
|
+
*
|
|
18
|
+
* Lives in library code rather than a test helper so the unit suites, the db
|
|
19
|
+
* suites, and the cross-backend suites all pin the same pattern the server
|
|
20
|
+
* renders.
|
|
21
|
+
*/
|
|
22
|
+
export declare const ISO8601_SECONDS: RegExp;
|
|
23
|
+
/**
|
|
24
|
+
* The width of one wire timestamp, in milliseconds.
|
|
25
|
+
*
|
|
26
|
+
* A wire stamp is truncated, not rounded, so the instant it names lies
|
|
27
|
+
* anywhere in `[stamp, stamp + ISO8601_SECOND_MS)`. A predicate that must
|
|
28
|
+
* not fire early against a stamp — "has this expiry passed?" — adds this to
|
|
29
|
+
* reach the end of the second the stamp stands for.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ISO8601_SECOND_MS = 1000;
|
|
32
|
+
/**
|
|
33
|
+
* Whether the second a wire timestamp names has not yet ended.
|
|
34
|
+
*
|
|
35
|
+
* The one in-memory expiry rule over a truncated stamp: `stamp` stands for
|
|
36
|
+
* `[stamp, stamp + ISO8601_SECOND_MS)`, so this admits until that whole
|
|
37
|
+
* second is over and can never report an expiry the server's raw-column
|
|
38
|
+
* `expires_at > NOW()` gate would still accept. A stamp that does not parse
|
|
39
|
+
* is never live.
|
|
40
|
+
*
|
|
41
|
+
* @param stamp - a second-precision wire timestamp
|
|
42
|
+
* @param now_ms - the current time in epoch milliseconds
|
|
43
|
+
* @returns `true` while `now_ms` lies before the end of `stamp`'s second
|
|
44
|
+
*/
|
|
45
|
+
export declare const is_iso8601_seconds_live: (stamp: string, now_ms: number) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Render a `Date` as the canonical second-precision UTC wire timestamp
|
|
48
|
+
* (`YYYY-MM-DDTHH:MM:SSZ`).
|
|
49
|
+
*
|
|
50
|
+
* Truncates rather than rounds, mirroring Postgres'
|
|
51
|
+
* `to_char(…, 'YYYY-MM-DD"T"HH24:MI:SS"Z"')` and the Rust spine's
|
|
52
|
+
* `fuz_sys::rfc3339_now`.
|
|
53
|
+
*
|
|
54
|
+
* @param date - the instant to render
|
|
55
|
+
* @returns the 20-character UTC timestamp
|
|
56
|
+
*/
|
|
57
|
+
export declare const to_iso8601_seconds: (date: Date) => string;
|
|
58
|
+
//# sourceMappingURL=timestamp.d.ts.map
|