Files
memind/page-access-policy.test.mjs
T

130 lines
3.9 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
assertPageAccessPolicyMatchesRegistry,
buildEffectiveDataset,
closePolicyDataset,
normalizePageAccessPolicy,
policyAllowsAction,
} from './page-access-policy.mjs';
test('normalizePageAccessPolicy validates access mode and datasets', () => {
const policy = normalizePageAccessPolicy(
{
pageId: 'page-1',
ownerUserId: 'user-1',
accessMode: 'public',
datasets: {
registrations: {
insert: true,
columns: { insert: ['name', 'phone'] },
},
},
},
{ fallbackPageId: 'page-1', fallbackOwnerUserId: 'user-1' },
);
assert.equal(policy.accessMode, 'public');
assert.equal(policy.datasets.registrations.insert, true);
assert.equal(policy.datasets.registrations.read, false);
});
test('buildEffectiveDataset intersects registry actions with policy permissions', () => {
const effective = buildEffectiveDataset(
{
name: 'registrations',
table: 'form_registrations',
description: '报名',
actions: ['read', 'insert', 'update', 'soft_delete'],
columns: {
read: ['id', 'name', 'phone', 'secret'],
insert: ['name', 'phone', 'secret'],
update: ['status'],
soft_delete: ['id'],
},
},
{
read: false,
insert: true,
update: false,
softDelete: false,
columns: { insert: ['name', 'phone'] },
},
);
assert.deepEqual(effective.actions, ['insert']);
assert.deepEqual(effective.columns.insert, ['name', 'phone']);
});
test('policy registry validation rejects declared columns missing from the real table', async () => {
const policy = normalizePageAccessPolicy({
pageId: 'page-1',
ownerUserId: 'user-1',
accessMode: 'password',
datasets: {
split_crud_uat_20260724: {
read: true,
update: true,
softDelete: true,
columns: {
read: ['id', 'title', 'updated_at', 'deleted_at', 'deleted_by'],
update: ['title', 'updated_at'],
soft_delete: ['deleted_at', 'deleted_by'],
},
},
},
});
const dataSpace = {
async getDataset() {
return {
name: 'split_crud_uat_20260724',
table: 'split_crud_uat_20260724',
actions: ['read', 'update', 'soft_delete'],
columns: {
read: ['id', 'title', 'updated_at', 'deleted_at', 'deleted_by'],
update: ['title', 'updated_at'],
soft_delete: ['deleted_at', 'deleted_by'],
},
};
},
async listTableColumns() {
return [{ name: 'id' }, { name: 'title' }];
},
};
await assert.rejects(
() => assertPageAccessPolicyMatchesRegistry(policy, dataSpace),
(error) =>
error.code === 'dataset_schema_mismatch' &&
error.status === 409 &&
error.columns.includes('updated_at') &&
error.columns.includes('deleted_at'),
);
});
test('policyAllowsAction respects per-dataset flags', () => {
const policy = normalizePageAccessPolicy({
pageId: 'page-1',
ownerUserId: 'user-1',
accessMode: 'password',
datasets: {
ledger: { read: true, insert: true, update: true, softDelete: true, columns: {} },
},
});
assert.equal(policyAllowsAction(policy, 'ledger', 'read'), true);
assert.equal(policyAllowsAction(policy, 'ledger', 'insert'), true);
assert.equal(policyAllowsAction(policy, 'missing', 'read'), false);
});
test('closePolicyDataset disables all dataset actions', () => {
const policy = normalizePageAccessPolicy({
pageId: 'page-1',
ownerUserId: 'user-1',
accessMode: 'password',
datasets: {
ledger: { read: true, insert: true, update: true, softDelete: true, columns: {} },
},
});
const closed = closePolicyDataset(policy, 'ledger');
assert.equal(closed.datasets.ledger.closed, true);
assert.equal(policyAllowsAction(closed, 'ledger', 'insert'), false);
});