@fuzdev/fuz_app 0.148.0 → 0.150.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 +11 -9
- package/dist/actions/action_bridge.d.ts +7 -0
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +10 -0
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +14 -1
- package/dist/actions/action_event_types.d.ts +1 -1
- package/dist/actions/action_event_types.d.ts.map +1 -1
- package/dist/actions/perform_action.d.ts +17 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +30 -7
- package/dist/actions/register_action_ws.d.ts +8 -2
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +24 -4
- package/dist/actions/transports_ws_auth_guard.d.ts +6 -4
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +9 -6
- package/dist/actions/ws_endpoint_spec.d.ts +1 -1
- package/dist/auth/CLAUDE.md +72 -33
- package/dist/auth/account_action_specs.d.ts +12 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +16 -2
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +7 -4
- package/dist/auth/actor_lookup_queries.d.ts +3 -2
- package/dist/auth/actor_lookup_queries.d.ts.map +1 -1
- package/dist/auth/actor_lookup_queries.js +6 -3
- package/dist/auth/actor_search_queries.d.ts +5 -2
- package/dist/auth/actor_search_queries.d.ts.map +1 -1
- package/dist/auth/actor_search_queries.js +11 -4
- package/dist/auth/admin_action_specs.d.ts +4 -4
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +33 -12
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +35 -6
- package/dist/auth/all_cell_actions.d.ts +11 -2
- package/dist/auth/all_cell_actions.d.ts.map +1 -1
- package/dist/auth/all_cell_actions.js +2 -1
- package/dist/auth/audit_emitter.d.ts +23 -3
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +14 -4
- package/dist/auth/audit_log_schema.d.ts +1 -0
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +9 -5
- package/dist/auth/bootstrap_account.d.ts +25 -1
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +37 -4
- package/dist/auth/bootstrap_routes.d.ts +3 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +5 -4
- package/dist/auth/cell_action_specs.d.ts +215 -15
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +103 -16
- package/dist/auth/cell_actions.d.ts +40 -18
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +126 -19
- package/dist/auth/cell_audit_events.d.ts +1 -1
- package/dist/auth/cell_audit_events.d.ts.map +1 -1
- package/dist/auth/cell_audit_events.js +3 -2
- package/dist/auth/cell_audit_metadata.d.ts +11 -0
- package/dist/auth/cell_audit_metadata.d.ts.map +1 -1
- package/dist/auth/cell_audit_metadata.js +10 -0
- package/dist/auth/cell_grant_action_specs.d.ts +7 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_grant_action_specs.js +7 -0
- package/dist/auth/cell_grant_actions.d.ts +36 -4
- package/dist/auth/cell_grant_actions.d.ts.map +1 -1
- package/dist/auth/cell_grant_actions.js +20 -3
- package/dist/auth/credential_type_schema.d.ts +3 -2
- package/dist/auth/credential_type_schema.d.ts.map +1 -1
- package/dist/auth/credential_type_schema.js +3 -2
- package/dist/auth/role_grant_offer_action_specs.d.ts +18 -2
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +30 -6
- package/dist/auth/role_grant_offer_actions.d.ts +9 -4
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +58 -7
- package/dist/auth/role_grant_offer_notifications.d.ts +3 -2
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_notifications.js +3 -2
- package/dist/auth/role_grant_offer_queries.d.ts +20 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +35 -0
- package/dist/auth/role_grant_queries.d.ts +27 -9
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +40 -13
- package/dist/auth/role_schema.d.ts +14 -8
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +6 -3
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +20 -0
- package/dist/db/CLAUDE.md +9 -2
- package/dist/db/cell_ddl.d.ts +9 -1
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +11 -1
- package/dist/db/cell_queries.d.ts +53 -7
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +61 -7
- package/dist/http/CLAUDE.md +14 -2
- package/dist/http/body_limit.d.ts +3 -1
- package/dist/http/body_limit.d.ts.map +1 -1
- package/dist/http/body_limit.js +6 -1
- package/dist/http/error_schemas.d.ts +8 -0
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +12 -0
- package/dist/http/jsonrpc.d.ts +9 -0
- package/dist/http/jsonrpc.d.ts.map +1 -1
- package/dist/http/jsonrpc.js +17 -0
- package/dist/http/jsonrpc_errors.d.ts +40 -15
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +99 -35
- package/dist/http/proxy.d.ts +4 -2
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +7 -2
- package/dist/http/surface.d.ts +3 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +7 -4
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +3 -2
- package/dist/schema_meta.d.ts +92 -1
- package/dist/schema_meta.d.ts.map +1 -1
- package/dist/schema_meta.js +117 -0
- package/dist/server/app_server.d.ts +13 -5
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +32 -8
- package/dist/testing/CLAUDE.md +196 -82
- package/dist/testing/adversarial_input_cases.d.ts +67 -7
- package/dist/testing/adversarial_input_cases.d.ts.map +1 -1
- package/dist/testing/adversarial_input_cases.js +210 -165
- package/dist/testing/app_server.d.ts +3 -1
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.d.ts +3 -2
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +5 -3
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/actor_lookup.js +27 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts +6 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -1
- package/dist/testing/cross_backend/audit_cross_helpers.js +11 -0
- package/dist/testing/cross_backend/capabilities.d.ts +29 -5
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +10 -6
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +117 -2
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +109 -13
- package/dist/testing/cross_backend/cell_moderate.d.ts +7 -1
- package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_moderate.js +68 -4
- package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_ref_admission.js +18 -9
- package/dist/testing/cross_backend/conformance_table.d.ts +5 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +12 -10
- package/dist/testing/cross_backend/cross_rpc.d.ts +3 -1
- package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -1
- package/dist/testing/cross_backend/cross_rpc.js +2 -2
- package/dist/testing/cross_backend/default_backend_configs.d.ts +9 -7
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +3 -3
- package/dist/testing/cross_backend/differential.d.ts +7 -0
- package/dist/testing/cross_backend/differential.d.ts.map +1 -1
- package/dist/testing/cross_backend/differential.js +17 -0
- package/dist/testing/cross_backend/expected_schema.json +1 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +5 -4
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +74 -2
- package/dist/testing/cross_backend/rpc_adversarial.d.ts +9 -0
- package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -1
- package/dist/testing/cross_backend/rpc_adversarial.js +16 -6
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -3
- package/dist/testing/cross_backend/setup.d.ts +2 -2
- package/dist/testing/cross_backend/spine_suites.d.ts +85 -5
- package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_suites.js +291 -20
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +15 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +24 -0
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +18 -19
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +66 -42
- package/dist/testing/cross_backend/testing_backdoor.d.ts +27 -4
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +136 -41
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -15
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +52 -35
- package/dist/testing/cross_backend/transport_composition.js +3 -3
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +6 -3
- package/dist/testing/rpc_attack_surface.d.ts +10 -0
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +15 -3
- package/dist/testing/rpc_envelope_cases.d.ts +5 -2
- package/dist/testing/rpc_envelope_cases.d.ts.map +1 -1
- package/dist/testing/rpc_envelope_cases.js +4 -1
- package/dist/testing/rpc_round_trip.js +3 -3
- package/dist/testing/schema_generators.d.ts +99 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +244 -76
- package/dist/testing/stubs.d.ts +4 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +6 -4
- package/dist/zod.d.ts +9 -0
- package/dist/zod.d.ts.map +1 -1
- package/dist/zod.js +20 -0
- package/package.json +1 -1
|
@@ -23,7 +23,8 @@ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
|
23
23
|
import { PARTICIPATION_ROLE, SPACE_CELL_KIND, TEST_SPACE_POLICY } from './test_cell_gated_create_authorize.js';
|
|
24
24
|
/**
|
|
25
25
|
* Cross-backend parity for the **`cell_moderate` verb** (the `pending →
|
|
26
|
-
* approved | rejected`
|
|
26
|
+
* approved | rejected` decision and the `approved → rejected` takedown) —
|
|
27
|
+
* root-authority-gated.
|
|
27
28
|
*
|
|
28
29
|
* Builds a moderated public space (admin) + a `participant`'s `post` (born
|
|
29
30
|
* `pending` + private under the `moderation_required: true` policy), then
|
|
@@ -41,6 +42,11 @@ import { PARTICIPATION_ROLE, SPACE_CELL_KIND, TEST_SPACE_POLICY } from './test_c
|
|
|
41
42
|
* - **a cell with no governing root** → `400 cell_not_a_contribution` for any
|
|
42
43
|
* viewer (owner, admin), the cell left unmoderated; a non-viewer gets the
|
|
43
44
|
* `cell_not_found` mask first.
|
|
45
|
+
* - **approval needs a pending contribution, rejection a pending or approved
|
|
46
|
+
* one** → else `400 cell_not_pending`, the cell left as it was: a root owner
|
|
47
|
+
* a private, born-approved contribution was shared with can't publish it by
|
|
48
|
+
* "approving" it; an approved post is not approved again but can be taken
|
|
49
|
+
* down (rejected, visibility untouched); a rejected post is final.
|
|
44
50
|
*
|
|
45
51
|
* And that moderation gates reads — a cell's effective audience is its
|
|
46
52
|
* visibility or grants **and** its moderation admission (unmoderated or
|
|
@@ -102,7 +108,7 @@ export const describe_cell_moderate_cross_tests = (options) => {
|
|
|
102
108
|
// participant's pending post under it, optionally carrying a fact the
|
|
103
109
|
// author put.
|
|
104
110
|
const setup_pending_post_on = async (fixture, post_fact_content) => {
|
|
105
|
-
const space = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: SPACE_CELL_KIND, data: TEST_SPACE_POLICY, visibility: 'public' }, fixture.create_session_headers()), CellCreateOutput);
|
|
111
|
+
const space = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: SPACE_CELL_KIND, data: {}, policy: TEST_SPACE_POLICY, visibility: 'public' }, fixture.create_session_headers()), CellCreateOutput);
|
|
106
112
|
const author = await fixture.create_account({
|
|
107
113
|
username: 'moderate_author',
|
|
108
114
|
roles: [PARTICIPATION_ROLE]
|
|
@@ -118,7 +124,7 @@ export const describe_cell_moderate_cross_tests = (options) => {
|
|
|
118
124
|
// put.
|
|
119
125
|
const setup_stewarded_post_on = async (fixture, post_fact_content) => {
|
|
120
126
|
const root_owner = await fixture.create_account({ username: 'steward_root_owner' });
|
|
121
|
-
const root = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: 'forum', data: TEST_SPACE_POLICY, visibility: 'public' }, root_owner.create_session_headers()), CellCreateOutput);
|
|
127
|
+
const root = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: 'forum', data: {}, policy: TEST_SPACE_POLICY, visibility: 'public' }, root_owner.create_session_headers()), CellCreateOutput);
|
|
122
128
|
const author = await fixture.create_account({
|
|
123
129
|
username: 'steward_author',
|
|
124
130
|
roles: [PARTICIPATION_ROLE]
|
|
@@ -208,6 +214,64 @@ export const describe_cell_moderate_cross_tests = (options) => {
|
|
|
208
214
|
assert.strictEqual(got.cell.moderation, null);
|
|
209
215
|
assert.strictEqual(got.cell.visibility, 'private');
|
|
210
216
|
});
|
|
217
|
+
test_if(capabilities.cell_gated_create, 'a root owner shown a private approved contribution cannot publish it → 400 cell_not_pending', async () => {
|
|
218
|
+
// the contribution is approved at birth (`comment` is unmoderated
|
|
219
|
+
// under the policy), so it was never pending; its author shares it
|
|
220
|
+
// with the root owner, who "approves" it
|
|
221
|
+
const fixture = await setup_test();
|
|
222
|
+
const root_owner = await fixture.create_account({ username: 'not_pending_root_owner' });
|
|
223
|
+
const root = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: 'forum', data: {}, policy: TEST_SPACE_POLICY, visibility: 'public' }, root_owner.create_session_headers()), CellCreateOutput);
|
|
224
|
+
const author = await fixture.create_account({
|
|
225
|
+
username: 'not_pending_author',
|
|
226
|
+
roles: [PARTICIPATION_ROLE]
|
|
227
|
+
});
|
|
228
|
+
const author_h = author.create_session_headers();
|
|
229
|
+
const comment = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: 'comment', data: {}, parent_id: root.cell.id, visibility: 'private' }, author_h), CellCreateOutput);
|
|
230
|
+
assert.strictEqual(comment.cell.moderation, 'approved', 'born approved');
|
|
231
|
+
expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_grant_create', {
|
|
232
|
+
cell_id: comment.cell.id,
|
|
233
|
+
level: 'viewer',
|
|
234
|
+
principal: { kind: 'actor', actor_id: root_owner.actor.id }
|
|
235
|
+
}, author_h), CellGrantCreateOutput);
|
|
236
|
+
const refused = await cross_rpc_call(fixture.transport, rpc_path, 'cell_moderate', { cell_id: comment.cell.id, moderation: 'approved' }, root_owner.create_session_headers());
|
|
237
|
+
assert.ok(!refused.ok, 'an approved contribution is not approved again');
|
|
238
|
+
assert.strictEqual(refused.error?.code, JSONRPC_ERROR_CODES.invalid_params);
|
|
239
|
+
assert.strictEqual(error_reason(refused), 'cell_not_pending');
|
|
240
|
+
const after = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_get', { id: comment.cell.id }, author_h), CellGetOutput).cell;
|
|
241
|
+
assert.strictEqual(after.visibility, 'private', 'the refusal publishes nothing');
|
|
242
|
+
assert.strictEqual(after.moderation, 'approved');
|
|
243
|
+
assert.strictEqual(await outcome(fixture, 'cell_get', { id: comment.cell.id }, null), 'cell_not_found', 'still hidden from anon');
|
|
244
|
+
});
|
|
245
|
+
test_if(capabilities.cell_gated_create, 'approval needs a pending post, rejection a pending or approved one → else 400 cell_not_pending', async () => {
|
|
246
|
+
const try_moderate = async (fixture, cell_id, moderation) => outcome(fixture, 'cell_moderate', { cell_id, moderation }, fixture.create_session_headers());
|
|
247
|
+
const state = async (fixture, id) => {
|
|
248
|
+
const { cell } = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_get', { id }, fixture.create_session_headers()), CellGetOutput);
|
|
249
|
+
return [cell.moderation, cell.visibility];
|
|
250
|
+
};
|
|
251
|
+
// approved: approving again is refused; the takedown is not
|
|
252
|
+
{
|
|
253
|
+
const { fixture, post } = await setup_pending_post();
|
|
254
|
+
await moderate(fixture, post.cell.id, 'approved');
|
|
255
|
+
assert.strictEqual(await try_moderate(fixture, post.cell.id, 'approved'), 'cell_not_pending', 'approved, approve');
|
|
256
|
+
assert.deepStrictEqual(await state(fixture, post.cell.id), ['approved', 'public']);
|
|
257
|
+
assert.strictEqual(await try_moderate(fixture, post.cell.id, 'rejected'), 'ok', 'approved, reject');
|
|
258
|
+
// rejected is terminal
|
|
259
|
+
for (const again of ['approved', 'rejected']) {
|
|
260
|
+
assert.strictEqual(await try_moderate(fixture, post.cell.id, again), 'cell_not_pending', `taken down, ${again}`);
|
|
261
|
+
}
|
|
262
|
+
assert.deepStrictEqual(await state(fixture, post.cell.id), ['rejected', 'public']);
|
|
263
|
+
}
|
|
264
|
+
// rejected from pending: neither decision moves it
|
|
265
|
+
{
|
|
266
|
+
const { fixture, post } = await setup_pending_post();
|
|
267
|
+
await moderate(fixture, post.cell.id, 'rejected');
|
|
268
|
+
for (const again of ['approved', 'rejected']) {
|
|
269
|
+
assert.strictEqual(await try_moderate(fixture, post.cell.id, again), 'cell_not_pending', `rejected, ${again}`);
|
|
270
|
+
}
|
|
271
|
+
assert.deepStrictEqual(await state(fixture, post.cell.id), ['rejected', 'private']);
|
|
272
|
+
assert.strictEqual(await outcome(fixture, 'cell_get', { id: post.cell.id }, null), 'cell_not_found');
|
|
273
|
+
}
|
|
274
|
+
});
|
|
211
275
|
});
|
|
212
276
|
describe('moderation gates reads (parity)', () => {
|
|
213
277
|
test_if(capabilities.cell_gated_create, 'a pending post its author flips public stays hidden past the author and admins', async () => {
|
|
@@ -493,7 +557,7 @@ export const describe_cell_moderate_cross_tests = (options) => {
|
|
|
493
557
|
describe('cell_clone runs the creation authorizer (parity)', () => {
|
|
494
558
|
test_if(capabilities.cell_gated_create, 'a non-admin clone of a viewable `space` root → 403, admin clone succeeds', async () => {
|
|
495
559
|
const fixture = await setup_test();
|
|
496
|
-
const space = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: SPACE_CELL_KIND, data: TEST_SPACE_POLICY, visibility: 'public' }, fixture.create_session_headers()), CellCreateOutput);
|
|
560
|
+
const space = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: SPACE_CELL_KIND, data: {}, policy: TEST_SPACE_POLICY, visibility: 'public' }, fixture.create_session_headers()), CellCreateOutput);
|
|
497
561
|
// Every cell a clone writes is a parentless create of the source's
|
|
498
562
|
// kind, gated like `cell_create` — so a caller denied a `space` root
|
|
499
563
|
// can't clone one they can see into one they own.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_ref_admission.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_ref_admission.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_ref_admission.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_ref_admission.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAwD9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AA6B1E,eAAO,MAAM,uCAAuC,GACnD,SAAS,uCAAuC,KAC9C,IA2fF,CAAC"}
|
|
@@ -31,7 +31,9 @@ import '../assert_dev_env.js';
|
|
|
31
31
|
*
|
|
32
32
|
* Facts are put through `_testing_put_fact`, with `holder` naming the actor a
|
|
33
33
|
* real upload would record. Gated on `capabilities.fact_serving` (the
|
|
34
|
-
* seeder + the serve routes)
|
|
34
|
+
* seeder + the serve routes), and the cases driving a grant, item, or clone
|
|
35
|
+
* verb on `capabilities.cell_relations` as well; runs under every
|
|
36
|
+
* `cross_backend_*` project.
|
|
35
37
|
*
|
|
36
38
|
* @module
|
|
37
39
|
*/
|
|
@@ -101,7 +103,7 @@ export const describe_cell_ref_admission_cross_tests = (options) => {
|
|
|
101
103
|
}), uploaded);
|
|
102
104
|
expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { cover: uploaded } }, bob_headers), CellCreateOutput);
|
|
103
105
|
});
|
|
104
|
-
test_if(capabilities.fact_serving, 'update keeps existing refs and refuses new unheld ones', async () => {
|
|
106
|
+
test_if(capabilities.fact_serving && capabilities.cell_relations, 'update keeps existing refs and refuses new unheld ones', async () => {
|
|
105
107
|
const fixture = await setup_test();
|
|
106
108
|
const alice = await fixture.create_account({ username: 'ref_upd_alice' });
|
|
107
109
|
const bob = await fixture.create_account({ username: 'ref_upd_bob' });
|
|
@@ -135,7 +137,7 @@ export const describe_cell_ref_admission_cross_tests = (options) => {
|
|
|
135
137
|
expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id, data: { label: 'dropped' } }, bob_headers), CellUpdateOutput);
|
|
136
138
|
assert_not_held(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id, data: { cover: alices } }, bob_headers), 'update restoring a dropped ref');
|
|
137
139
|
});
|
|
138
|
-
test_if(capabilities.fact_serving, 'clone carries source refs per row without holding them', async () => {
|
|
140
|
+
test_if(capabilities.fact_serving && capabilities.cell_relations, 'clone carries source refs per row without holding them', async () => {
|
|
139
141
|
const fixture = await setup_test();
|
|
140
142
|
const alice = await fixture.create_account({ username: 'ref_clone_alice' });
|
|
141
143
|
const bob = await fixture.create_account({ username: 'ref_clone_bob' });
|
|
@@ -184,7 +186,7 @@ export const describe_cell_ref_admission_cross_tests = (options) => {
|
|
|
184
186
|
assert.deepStrictEqual(created.cell.refs, [named]);
|
|
185
187
|
}
|
|
186
188
|
});
|
|
187
|
-
test_if(capabilities.fact_serving, 'a revoked grantee cannot re-reference a fact it read', async () => {
|
|
189
|
+
test_if(capabilities.fact_serving && capabilities.cell_relations, 'a revoked grantee cannot re-reference a fact it read', async () => {
|
|
188
190
|
const fixture = await setup_test();
|
|
189
191
|
const alice = await fixture.create_account({ username: 'ref_rev_alice' });
|
|
190
192
|
const bob = await fixture.create_account({ username: 'ref_rev_bob' });
|
|
@@ -242,17 +244,21 @@ export const describe_cell_ref_admission_cross_tests = (options) => {
|
|
|
242
244
|
const prose = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: not_refs }, bob.create_session_headers()), CellCreateOutput);
|
|
243
245
|
assert.strictEqual(prose.cell.refs, null);
|
|
244
246
|
});
|
|
245
|
-
|
|
247
|
+
/** Alice's private cell over her own fact, which Bob cannot see. */
|
|
248
|
+
const setup_hidden_cell = async (prefix) => {
|
|
246
249
|
const fixture = await setup_test();
|
|
247
|
-
const alice = await fixture.create_account({ username:
|
|
248
|
-
const bob = await fixture.create_account({ username:
|
|
250
|
+
const alice = await fixture.create_account({ username: `${prefix}_alice` });
|
|
251
|
+
const bob = await fixture.create_account({ username: `${prefix}_bob` });
|
|
249
252
|
const t = fixture.fresh_transport();
|
|
250
|
-
const bob_headers = bob.create_session_headers();
|
|
251
253
|
const alices = await cross_put_fact(fixture, rpc_path, {
|
|
252
|
-
content:
|
|
254
|
+
content: `${prefix} hidden bytes`,
|
|
253
255
|
holder: alice.actor.id
|
|
254
256
|
});
|
|
255
257
|
const hidden = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { file: alices }, visibility: 'private' }, alice.create_session_headers()), CellCreateOutput).cell.id;
|
|
258
|
+
return { t, bob_headers: bob.create_session_headers(), alices, hidden };
|
|
259
|
+
};
|
|
260
|
+
test_if(capabilities.fact_serving, 'ref admission answers after the 404 masks', async () => {
|
|
261
|
+
const { t, bob_headers, alices, hidden } = await setup_hidden_cell('ref_mask');
|
|
256
262
|
// An update of a hidden cell answers its mask whatever `data` names —
|
|
257
263
|
// an unheld new ref or the hidden cell's own ref alike.
|
|
258
264
|
for (const named of [UNSTORED_HASH, alices]) {
|
|
@@ -261,6 +267,9 @@ export const describe_cell_ref_admission_cross_tests = (options) => {
|
|
|
261
267
|
}
|
|
262
268
|
const under = await cross_rpc_call(t, rpc_path, 'cell_create', { data: { file: UNSTORED_HASH }, parent_id: hidden }, bob_headers);
|
|
263
269
|
assert.strictEqual(error_reason(under), 'cell_not_found', 'create under a hidden parent');
|
|
270
|
+
});
|
|
271
|
+
test_if(capabilities.fact_serving && capabilities.cell_relations, 'clone ref admission answers after the 404 mask', async () => {
|
|
272
|
+
const { t, bob_headers, hidden } = await setup_hidden_cell('ref_clone_mask');
|
|
264
273
|
const clone = await cross_rpc_call(t, rpc_path, 'cell_clone', { source_id: hidden, with_data_patch: { extra: UNSTORED_HASH } }, bob_headers);
|
|
265
274
|
assert.strictEqual(error_reason(clone), 'cell_not_found', 'clone of a hidden source');
|
|
266
275
|
});
|
|
@@ -154,6 +154,11 @@ export interface SendConformanceCaseOptions {
|
|
|
154
154
|
readonly resolved_rpc_endpoints: ReturnType<typeof resolve_rpc_endpoints_for_setup>;
|
|
155
155
|
/** Send the request as a frame on a socket at this endpoint instead of over HTTP. */
|
|
156
156
|
readonly ws?: WsEndpointTarget;
|
|
157
|
+
/**
|
|
158
|
+
* Headers added to the HTTP request beneath the principal's, which win on
|
|
159
|
+
* a name clash. Not sent on the WS leg (a frame carries no headers).
|
|
160
|
+
*/
|
|
161
|
+
readonly request_headers?: Readonly<Record<string, string>>;
|
|
157
162
|
}
|
|
158
163
|
/** The spec a sent case resolved to — the RPC action, or the REST route. */
|
|
159
164
|
export type ConformanceCaseTarget = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8C9B,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAKN,+BAA+B,EAC/B,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAI3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAGvE,OAAO,EAGN,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IACtD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,8BAA8B;IAC9C,kCAAkC;IAClC,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IACtD,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CACjD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,8BAA8B,KACrC,KAAK,CAAC,eAAe,CAKvB,CAAC;AAkBF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAAI,GAAG,eAAe,KAAG,OAIf,CAAC;AAE9C,0EAA0E;AAC1E,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AA+CD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,WAAW,EACpB,IAAI,oBAAoB,EACxB,aAAa,0BAA0B,EACvC,yBAAwB,UAAU,CAAC,OAAO,+BAA+B,CAAM,KAC7E,OAAO,CAAC,4BAA4B,CAmJtC,CAAC;AA4CF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AA0BF,2DAA2D;AAC3D,MAAM,WAAW,0BAA0B;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,EAAE,UAAU,CAAC,OAAO,+BAA+B,CAAC,CAAC;IACpF,qFAAqF;IACrF,QAAQ,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"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"}
|
|
@@ -391,13 +391,16 @@ const is_success_status = (status) => status >= 200 && status < 300;
|
|
|
391
391
|
* auth routes
|
|
392
392
|
*/
|
|
393
393
|
export const send_conformance_case = async (c, options) => {
|
|
394
|
-
const { fixture, surface_source, principals, resolved_rpc_endpoints, ws } = options;
|
|
394
|
+
const { fixture, surface_source, principals, resolved_rpc_endpoints, ws, request_headers } = options;
|
|
395
395
|
if (ws && !is_ws_conformance_case(c)) {
|
|
396
396
|
throw new Error(`conformance: case '${c.name}' cannot run over a WebSocket (is_ws_conformance_case) — ` +
|
|
397
397
|
`filter the cases before sending them on the WS leg.`);
|
|
398
398
|
}
|
|
399
399
|
const principal = await resolve_conformance_principal(fixture, c.request.as, principals, resolved_rpc_endpoints);
|
|
400
|
-
const { transport,
|
|
400
|
+
const { transport, suppress_default_origin } = principal;
|
|
401
|
+
const headers = request_headers
|
|
402
|
+
? { ...request_headers, ...principal.headers }
|
|
403
|
+
: principal.headers;
|
|
401
404
|
const params = c.request.acting
|
|
402
405
|
? bind_acting(c, resolve_acting_binding(c, c.request.acting, fixture, principal))
|
|
403
406
|
: c.request.params;
|
|
@@ -461,15 +464,14 @@ const CONFORMANCE_REQUEST_ID = 'test';
|
|
|
461
464
|
* The upgrade carries the principal's headers (its cookie, bearer or daemon
|
|
462
465
|
* token) and the `Origin` the HTTP leg would send: none for a principal that
|
|
463
466
|
* suppresses it, the target's otherwise. It also names the principal's own
|
|
464
|
-
* actor as `?acting
|
|
465
|
-
*
|
|
466
|
-
* `actor_required`)
|
|
467
|
-
* ignores `?acting=`. Per-frame dispatch resolves the actor from each frame's
|
|
467
|
+
* actor as `?acting=`, which both spines resolve at the upgrade (a
|
|
468
|
+
* multi-actor account's upgrade without one is refused 400
|
|
469
|
+
* `actor_required`). Per-frame dispatch resolves the actor from each frame's
|
|
468
470
|
* own `acting` on both. Because the leg always sends `?acting=`, it does not
|
|
469
|
-
* cover
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
471
|
+
* cover the upgrade's own refusal — the front-door table's multi-actor row
|
|
472
|
+
* does. The socket closes afterwards; a close error is not the case's subject
|
|
473
|
+
* (a request that revokes its own credential closes the socket after its
|
|
474
|
+
* reply).
|
|
473
475
|
*/
|
|
474
476
|
const send_ws_frame = async (method, params, principal, ws) => {
|
|
475
477
|
const client = await create_admitted_ws_transport({
|
|
@@ -3,6 +3,8 @@ import type { z } from 'zod';
|
|
|
3
3
|
import type { FetchTransport } from '../transports/fetch_transport.js';
|
|
4
4
|
/** Minimal JSON-RPC envelope shape the suites read off responses. */
|
|
5
5
|
export interface RpcResult {
|
|
6
|
+
/** The HTTP status the reply carried. */
|
|
7
|
+
readonly status: number;
|
|
6
8
|
readonly ok: boolean;
|
|
7
9
|
readonly result?: unknown;
|
|
8
10
|
readonly error?: {
|
|
@@ -22,7 +24,7 @@ export interface RpcResult {
|
|
|
22
24
|
* @param method - the JSON-RPC method
|
|
23
25
|
* @param params - the request params
|
|
24
26
|
* @param headers - auth and other headers, merged over the JSON defaults
|
|
25
|
-
* @returns the response's `ok`, `result` and `error`
|
|
27
|
+
* @returns the response's HTTP `status`, `ok`, `result` and `error`
|
|
26
28
|
* @throws Error when the reply is not a JSON object — naming the method, the
|
|
27
29
|
* HTTP status and the head of the body, so a proxy page or a crash reads
|
|
28
30
|
* as what it is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cross_rpc.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAgB9B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEvE,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC9F;AAKD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GAC1B,WAAW,cAAc,EACzB,MAAM,MAAM,EACZ,QAAQ,MAAM,EACd,QAAQ,OAAO,EACf,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,OAAO,CAAC,SAAS,CAmBnB,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,YAAY,GAAI,GAAG,SAAS,KAAG,OAG/B,CAAC;AAEd;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,GAAG,SAAS,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAG,CAUrE,CAAC"}
|
|
1
|
+
{"version":3,"file":"cross_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cross_rpc.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAgB9B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEvE,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACzB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC9F;AAKD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GAC1B,WAAW,cAAc,EACzB,MAAM,MAAM,EACZ,QAAQ,MAAM,EACd,QAAQ,OAAO,EACf,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,OAAO,CAAC,SAAS,CAmBnB,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,YAAY,GAAI,GAAG,SAAS,KAAG,OAG/B,CAAC;AAEd;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,GAAG,SAAS,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAG,CAUrE,CAAC"}
|
|
@@ -26,7 +26,7 @@ const BODY_HEAD_LENGTH = 200;
|
|
|
26
26
|
* @param method - the JSON-RPC method
|
|
27
27
|
* @param params - the request params
|
|
28
28
|
* @param headers - auth and other headers, merged over the JSON defaults
|
|
29
|
-
* @returns the response's `ok`, `result` and `error`
|
|
29
|
+
* @returns the response's HTTP `status`, `ok`, `result` and `error`
|
|
30
30
|
* @throws Error when the reply is not a JSON object — naming the method, the
|
|
31
31
|
* HTTP status and the head of the body, so a proxy page or a crash reads
|
|
32
32
|
* as what it is
|
|
@@ -48,7 +48,7 @@ export const cross_rpc_call = async (transport, path, method, params, headers) =
|
|
|
48
48
|
JSON.stringify(text.slice(0, BODY_HEAD_LENGTH)));
|
|
49
49
|
}
|
|
50
50
|
const envelope = body;
|
|
51
|
-
return { ok: res.ok, result: envelope.result, error: envelope.error };
|
|
51
|
+
return { status: res.status, ok: res.ok, result: envelope.result, error: envelope.error };
|
|
52
52
|
};
|
|
53
53
|
/** Pull `error.data.reason` off an RPC error envelope (undefined if absent). */
|
|
54
54
|
export const error_reason = (r) => r.error && typeof r.error.data === 'object' && r.error.data !== null
|
|
@@ -34,14 +34,14 @@ export declare const action_rate_limit_env: (options: ActionRateLimitEnvOptions)
|
|
|
34
34
|
/**
|
|
35
35
|
* Capabilities shared by TS-family backends — only what the family
|
|
36
36
|
* guarantees: the declared spine surface's mounts and the behavior of its
|
|
37
|
-
* reference runtime, Node. Carries no `MountFlag
|
|
38
|
-
* those itself (`FamilyDefaultCapabilities`).
|
|
37
|
+
* reference runtime, Node. Carries no `MountFlag` and no `testing_backdoors`;
|
|
38
|
+
* each backend config states those itself (`FamilyDefaultCapabilities`).
|
|
39
39
|
*/
|
|
40
40
|
export declare const ts_default_capabilities: FamilyDefaultCapabilities;
|
|
41
41
|
/**
|
|
42
42
|
* Capabilities for the Rust family — only what the family guarantees, as with
|
|
43
43
|
* `ts_default_capabilities`: the declared spine surface's mounts and the
|
|
44
|
-
* behavior of hyper. Carries no `MountFlag`.
|
|
44
|
+
* behavior of hyper. Carries no `MountFlag` and no `testing_backdoors`.
|
|
45
45
|
*/
|
|
46
46
|
export declare const rust_default_capabilities: FamilyDefaultCapabilities;
|
|
47
47
|
export interface MakeDefaultTsBackendConfigOptions {
|
|
@@ -57,8 +57,9 @@ export interface MakeDefaultTsBackendConfigOptions {
|
|
|
57
57
|
readonly extra_env?: Readonly<Record<string, string>>;
|
|
58
58
|
/**
|
|
59
59
|
* Every capability, stated: spread `ts_default_capabilities` and add each
|
|
60
|
-
* `MountFlag
|
|
61
|
-
* than a suite that
|
|
60
|
+
* `MountFlag` and the `testing_backdoors` list. Required, so a mount flag or
|
|
61
|
+
* the backdoor list left out is a type error rather than a suite that
|
|
62
|
+
* silently skips.
|
|
62
63
|
*/
|
|
63
64
|
readonly capabilities: BackendCapabilities;
|
|
64
65
|
/** Pre-computed paths; defaults to `build_test_backend_paths(name)`. */
|
|
@@ -113,8 +114,9 @@ export interface MakeDefaultRustBackendConfigOptions {
|
|
|
113
114
|
readonly extra_env?: Readonly<Record<string, string>>;
|
|
114
115
|
/**
|
|
115
116
|
* Every capability, stated: spread `rust_default_capabilities` and add each
|
|
116
|
-
* `MountFlag
|
|
117
|
-
* than a suite that
|
|
117
|
+
* `MountFlag` and the `testing_backdoors` list. Required, so a mount flag or
|
|
118
|
+
* the backdoor list left out is a type error rather than a suite that
|
|
119
|
+
* silently skips.
|
|
118
120
|
*/
|
|
119
121
|
readonly capabilities: BackendCapabilities;
|
|
120
122
|
/** Pre-computed paths; defaults to `build_test_backend_paths(name)`. */
|
|
@@ -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;AAqC9B,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAA4B,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAehG;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,mBAAmB,MAAM,KAAG,MAS/D,CAAC;AAcF,2CAA2C;AAC3C,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,yBAAyB,KAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAKtB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,yBAgBpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,yBAatC,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,mDAAmD;IACnD,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
|
|
1
|
+
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"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"}
|
|
@@ -81,8 +81,8 @@ export const action_rate_limit_env = (options) => ({
|
|
|
81
81
|
/**
|
|
82
82
|
* Capabilities shared by TS-family backends — only what the family
|
|
83
83
|
* guarantees: the declared spine surface's mounts and the behavior of its
|
|
84
|
-
* reference runtime, Node. Carries no `MountFlag
|
|
85
|
-
* those itself (`FamilyDefaultCapabilities`).
|
|
84
|
+
* reference runtime, Node. Carries no `MountFlag` and no `testing_backdoors`;
|
|
85
|
+
* each backend config states those itself (`FamilyDefaultCapabilities`).
|
|
86
86
|
*/
|
|
87
87
|
export const ts_default_capabilities = Object.freeze({
|
|
88
88
|
// The account-lifecycle verbs are on the declared surface
|
|
@@ -104,7 +104,7 @@ export const ts_default_capabilities = Object.freeze({
|
|
|
104
104
|
/**
|
|
105
105
|
* Capabilities for the Rust family — only what the family guarantees, as with
|
|
106
106
|
* `ts_default_capabilities`: the declared spine surface's mounts and the
|
|
107
|
-
* behavior of hyper. Carries no `MountFlag`.
|
|
107
|
+
* behavior of hyper. Carries no `MountFlag` and no `testing_backdoors`.
|
|
108
108
|
*/
|
|
109
109
|
export const rust_default_capabilities = Object.freeze({
|
|
110
110
|
// The account-lifecycle verbs are in the spine's admin action set, on the
|
|
@@ -6,6 +6,13 @@ import type { ConformanceCase } from './conformance_case.js';
|
|
|
6
6
|
import { type ConformancePrincipalConfig } from './conformance_table.js';
|
|
7
7
|
import type { SetupTest } from './setup.js';
|
|
8
8
|
import type { WsEndpointTarget } from './ws_endpoint.js';
|
|
9
|
+
/**
|
|
10
|
+
* Headers every HTTP request the differential sends carries, beneath the
|
|
11
|
+
* principal's: `accept-encoding: identity`, so each backend answers
|
|
12
|
+
* uncompressed and a runtime's own compression (`Deno.serve` compresses for a
|
|
13
|
+
* request that accepts gzip) never reads as a header difference.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DIFFERENTIAL_REQUEST_HEADERS: Readonly<Record<string, string>>;
|
|
9
16
|
/** One side of the differential. */
|
|
10
17
|
export interface DifferentialBackend {
|
|
11
18
|
/** Short label used in failure output (`ts`, `rust`). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"differential.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/differential.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"differential.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/differential.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,13 @@ import '../assert_dev_env.js';
|
|
|
19
19
|
* each backend's conformance leg already holds them; the differential holds
|
|
20
20
|
* the two answers to each other.
|
|
21
21
|
*
|
|
22
|
+
* Every HTTP request asks for an uncompressed response
|
|
23
|
+
* (`DIFFERENTIAL_REQUEST_HEADERS`): content negotiation is the transport's,
|
|
24
|
+
* not the app's — Deno's `Deno.serve` compresses a response whose request
|
|
25
|
+
* accepts gzip, as Node's `fetch` sends by default, while other servers send
|
|
26
|
+
* it as is — so letting a server choose would read as a `content-encoding` and
|
|
27
|
+
* `vary` difference on every case.
|
|
28
|
+
*
|
|
22
29
|
* A difference that is tracked and accepted goes in the **known-divergence
|
|
23
30
|
* ledger** (`KnownDivergence`), matched by case names or by methods, naming
|
|
24
31
|
* the paths that differ (`what_differs`), each optionally with the value each
|
|
@@ -42,6 +49,15 @@ import '../assert_dev_env.js';
|
|
|
42
49
|
import { assert, describe, test } from 'vitest';
|
|
43
50
|
import { is_ws_conformance_case, resolve_conformance_rpc_endpoints, send_conformance_case } from './conformance_table.js';
|
|
44
51
|
import { diff_differential_responses, differential_path_covers, format_differential_diffs, normalize_differential_response } from './differential_normalize.js';
|
|
52
|
+
/**
|
|
53
|
+
* Headers every HTTP request the differential sends carries, beneath the
|
|
54
|
+
* principal's: `accept-encoding: identity`, so each backend answers
|
|
55
|
+
* uncompressed and a runtime's own compression (`Deno.serve` compresses for a
|
|
56
|
+
* request that accepts gzip) never reads as a header difference.
|
|
57
|
+
*/
|
|
58
|
+
export const DIFFERENTIAL_REQUEST_HEADERS = {
|
|
59
|
+
'accept-encoding': 'identity'
|
|
60
|
+
};
|
|
45
61
|
const as_list = (value) => typeof value === 'string' ? [value] : value;
|
|
46
62
|
/** The ledger entries matching a case. */
|
|
47
63
|
const find_known_divergences = (c, ledger) => ledger.filter((d) => d.case_name !== undefined
|
|
@@ -126,6 +142,7 @@ export const describe_differential_conformance_tests = (options) => {
|
|
|
126
142
|
surface_source,
|
|
127
143
|
...(principals && { principals }),
|
|
128
144
|
resolved_rpc_endpoints,
|
|
145
|
+
request_headers: DIFFERENTIAL_REQUEST_HEADERS,
|
|
129
146
|
...(backend.ws && { ws: backend.ws })
|
|
130
147
|
}));
|
|
131
148
|
describe(options.suite_name ?? 'differential conformance', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAMvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAkC7D,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAMvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAkC7D,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CA4CjB,CAAC;AAEF,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,cAUF,CAAC;AAaF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,QAAO,KAAK,CAAC,eAAe,CAOvE,CAAC"}
|
|
@@ -56,10 +56,11 @@ export const build_full_spine_rpc_actions = (deps, options) => {
|
|
|
56
56
|
// Mount the directory-model `cell_gated_create` test policy (twin of the
|
|
57
57
|
// Rust stub's `TestCellGatedCreateAuthorize`) so the cross-backend
|
|
58
58
|
// authorizer-parity suite has a known gate: `kind: 'space'` roots are
|
|
59
|
-
// admin-only, contributions are gated by the root's `
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
59
|
+
// admin-only, contributions are gated by the root's `policy`, and plain
|
|
60
|
+
// parentless creates stay open (the other cell suites are unaffected).
|
|
61
|
+
// The handler hands the governing root's `policy` to the (pure)
|
|
62
|
+
// authorizer, whose `validate_policy` accepts or refuses each policy
|
|
63
|
+
// write.
|
|
63
64
|
...create_all_cell_actions({ ...deps, authorize_create: test_cell_gated_create_authorize }, { roles: spine_roles }),
|
|
64
65
|
...create_actor_lookup_actions(deps),
|
|
65
66
|
...create_actor_search_actions(deps),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_lifecycle.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_offer_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offer_lifecycle.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_offer_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiE9B,OAAO,KAAK,EAAE,wBAAwB,EAAe,MAAM,YAAY,CAAC;AAMxE,qEAAqE;AACrE,MAAM,WAAW,uCAAwC,SAAQ,wBAAwB;IACxF;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,+CAA+C,GAC3D,SAAS,uCAAuC,KAC9C,IAyYF,CAAC"}
|
|
@@ -17,6 +17,13 @@ import '../assert_dev_env.js';
|
|
|
17
17
|
* - **already decided → 400** `invalid_request` with reason
|
|
18
18
|
* `role_grant_offer_terminal` — a decline or retract after an accept, a
|
|
19
19
|
* second decline, a second retract, and either one after the other.
|
|
20
|
+
* - **a grantor's lost authority ends its pending offers.** Revoking a
|
|
21
|
+
* grantor's grant, or deleting its account, supersedes every pending offer
|
|
22
|
+
* it made — gone from the recipient's inbox, an accept answers
|
|
23
|
+
* `role_grant_offer_terminal`, and a `role_grant_offer_supersede` row records
|
|
24
|
+
* it (`role_grant_revoked` with the grant as cause, `account_deleted` with
|
|
25
|
+
* the account). The keeper's own `admin` grant refuses a revoke, 403
|
|
26
|
+
* `cannot_revoke_keeper_admin`.
|
|
20
27
|
* - **one audit row per success, none per refusal.** A decline writes
|
|
21
28
|
* `role_grant_offer_decline` attributed to the recipient and targeting the
|
|
22
29
|
* grantor, a retract `role_grant_offer_retract` attributed to the grantor
|
|
@@ -37,11 +44,14 @@ import '../assert_dev_env.js';
|
|
|
37
44
|
*/
|
|
38
45
|
import { assert, describe, test } from 'vitest';
|
|
39
46
|
import { rpc_call } from '../rpc_helpers.js';
|
|
40
|
-
import { ERROR_ROLE_GRANT_OFFER_NOT_FOUND, ERROR_ROLE_GRANT_OFFER_TERMINAL } from '../../auth/role_grant_offer_action_specs.js';
|
|
47
|
+
import { ERROR_CANNOT_REVOKE_KEEPER_ADMIN, ERROR_ROLE_GRANT_OFFER_NOT_FOUND, ERROR_ROLE_GRANT_OFFER_TERMINAL } from '../../auth/role_grant_offer_action_specs.js';
|
|
48
|
+
import { AdminAccountListOutput } from '../../auth/admin_action_specs.js';
|
|
49
|
+
import { ROLE_ADMIN } from '../../auth/role_schema.js';
|
|
50
|
+
import { expect_output } from './cross_rpc.js';
|
|
41
51
|
import { JSONRPC_INVALID_REQUEST } from '../../http/jsonrpc.js';
|
|
42
52
|
import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
|
|
43
53
|
import { assert_audit_metadata_fields, list_audit_events } from './audit_cross_helpers.js';
|
|
44
|
-
import { create_pending_role_grant_offer } from './role_grant_cross_helpers.js';
|
|
54
|
+
import { create_active_role_grant, create_pending_role_grant_offer } from './role_grant_cross_helpers.js';
|
|
45
55
|
import { SPINE_PARTICIPANT_ROLE, SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
46
56
|
/** A well-formed UUID that names no offer. */
|
|
47
57
|
const NIL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
@@ -200,4 +210,66 @@ export const describe_role_grant_offer_lifecycle_cross_tests = (options) => {
|
|
|
200
210
|
assert.strictEqual(after.retracts.length, before.retracts.length, 'no retract row added');
|
|
201
211
|
});
|
|
202
212
|
});
|
|
213
|
+
describe("a grantor's lost authority ends its pending offers", () => {
|
|
214
|
+
/** A grantor holding a global `admin` grant (offer authority) and its id. */
|
|
215
|
+
const setup_grantor = async (fixture, username) => {
|
|
216
|
+
const grantor = await fixture.create_account({ username });
|
|
217
|
+
const grant_id = await create_active_role_grant(fixture, grantor, {
|
|
218
|
+
role: ROLE_ADMIN,
|
|
219
|
+
rpc_path
|
|
220
|
+
});
|
|
221
|
+
return { grantor, grant_id };
|
|
222
|
+
};
|
|
223
|
+
/** The pending `role` offer `grantor` makes to `recipient`. */
|
|
224
|
+
const offer_from = (fixture, grantor, recipient) => create_pending_role_grant_offer(fixture, recipient.account.id, grantor.create_session_headers(), { role, rpc_path });
|
|
225
|
+
const assert_superseded = async (fixture, recipient, offer_id, cause) => {
|
|
226
|
+
assert.ok(!(await is_pending(fixture, recipient, offer_id)), 'gone from the recipient inbox');
|
|
227
|
+
assert_terminal(await accept(fixture, offer_id, recipient.create_session_headers()), 'an accept of the superseded offer');
|
|
228
|
+
const rows = (await list_audit_events(fixture, { event_type: 'role_grant_offer_supersede' }, rpc_path)).filter((e) => e.metadata?.offer_id === offer_id);
|
|
229
|
+
assert.strictEqual(rows.length, 1, 'one role_grant_offer_supersede row');
|
|
230
|
+
assert_audit_metadata_fields(fixture, rows[0].metadata, {
|
|
231
|
+
offer_id,
|
|
232
|
+
role,
|
|
233
|
+
scope_id: null,
|
|
234
|
+
...cause
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
test("revoking the grantor's grant supersedes its pending offers", async () => {
|
|
238
|
+
const fixture = await setup_test();
|
|
239
|
+
const { grantor, grant_id } = await setup_grantor(fixture, 'lost_authority_revoked');
|
|
240
|
+
const recipient = await fixture.create_account({ username: 'lost_authority_to' });
|
|
241
|
+
const offer = await offer_from(fixture, grantor, recipient);
|
|
242
|
+
// another grantor's offer to the same recipient is the control
|
|
243
|
+
const control = await create_offer(fixture, recipient);
|
|
244
|
+
assert_ok(await rpc(fixture, 'role_grant_revoke', { actor_id: grantor.actor.id, role_grant_id: grant_id }, fixture.create_session_headers()), 'revoke');
|
|
245
|
+
await assert_superseded(fixture, recipient, offer.id, {
|
|
246
|
+
reason: 'role_grant_revoked',
|
|
247
|
+
cause_id: grant_id
|
|
248
|
+
});
|
|
249
|
+
assert.ok(await is_pending(fixture, recipient, control.id), "the keeper's offer stays");
|
|
250
|
+
});
|
|
251
|
+
test("deleting the grantor's account supersedes its pending offers", async () => {
|
|
252
|
+
const fixture = await setup_test();
|
|
253
|
+
const { grantor } = await setup_grantor(fixture, 'lost_authority_deleted');
|
|
254
|
+
const recipient = await fixture.create_account({ username: 'lost_authority_del_to' });
|
|
255
|
+
const offer = await offer_from(fixture, grantor, recipient);
|
|
256
|
+
assert_ok(await rpc(fixture, 'account_delete', { account_id: grantor.account.id }, fixture.create_session_headers()), 'account_delete');
|
|
257
|
+
await assert_superseded(fixture, recipient, offer.id, {
|
|
258
|
+
reason: 'account_deleted',
|
|
259
|
+
cause_id: grantor.account.id
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
test("the keeper's admin grant refuses a revoke → 403 cannot_revoke_keeper_admin", async () => {
|
|
263
|
+
const fixture = await setup_test();
|
|
264
|
+
const listed = expect_output(await rpc(fixture, 'admin_account_list', {}, fixture.create_session_headers()), AdminAccountListOutput);
|
|
265
|
+
const keeper = listed.accounts.find((a) => a.account.id === fixture.account.id);
|
|
266
|
+
const keeper_admin = keeper?.role_grants.find((g) => g.role === ROLE_ADMIN && g.scope_id === null);
|
|
267
|
+
assert.ok(keeper_admin, "the keeper's global admin grant");
|
|
268
|
+
const refused = await rpc(fixture, 'role_grant_revoke', { actor_id: fixture.actor.id, role_grant_id: keeper_admin.id }, fixture.create_session_headers());
|
|
269
|
+
assert.ok(!refused.ok, 'the keeper keeps its admin grant');
|
|
270
|
+
assert.strictEqual(refused.status, 403);
|
|
271
|
+
assert.strictEqual(refused.error.code, JSONRPC_ERROR_CODES.forbidden);
|
|
272
|
+
assert.deepStrictEqual(refused.error.data, { reason: ERROR_CANNOT_REVOKE_KEEPER_ADMIN });
|
|
273
|
+
});
|
|
274
|
+
});
|
|
203
275
|
};
|