6b0c633a75
Add visitor roles, row-level scope, owner ops APIs, MySQL policy index, Turnstile captcha, browser client SDK, publish-panel dataset binding, acceptance tests, and usage documentation. Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
2.3 KiB
JavaScript
75 lines
2.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
extractPageDataCaptchaToken,
|
|
stripPageDataMetaFields,
|
|
verifyPageDataCaptcha,
|
|
} from './page-data-captcha.mjs';
|
|
import {
|
|
buildPageDataPolicyFromPublishInput,
|
|
mapPublicationAccessModeToPageData,
|
|
} from './page-access-policy.mjs';
|
|
|
|
test('stripPageDataMetaFields removes captcha and meta keys', () => {
|
|
assert.deepEqual(
|
|
stripPageDataMetaFields({ name: '张三', turnstile_token: 'abc', updated_by_label: 'x' }),
|
|
{ name: '张三' },
|
|
);
|
|
});
|
|
|
|
test('verifyPageDataCaptcha skips when secret is not configured', async () => {
|
|
const prev = process.env.PAGE_DATA_TURNSTILE_SECRET;
|
|
delete process.env.PAGE_DATA_TURNSTILE_SECRET;
|
|
try {
|
|
const result = await verifyPageDataCaptcha(null, '127.0.0.1');
|
|
assert.equal(result.skipped, true);
|
|
} finally {
|
|
if (prev) process.env.PAGE_DATA_TURNSTILE_SECRET = prev;
|
|
}
|
|
});
|
|
|
|
test('verifyPageDataCaptcha rejects missing token when enabled', async () => {
|
|
const prev = process.env.PAGE_DATA_TURNSTILE_SECRET;
|
|
process.env.PAGE_DATA_TURNSTILE_SECRET = 'test-secret';
|
|
try {
|
|
await assert.rejects(
|
|
() => verifyPageDataCaptcha('', '127.0.0.1'),
|
|
(error) => error.code === 'captcha_required',
|
|
);
|
|
} finally {
|
|
if (prev) process.env.PAGE_DATA_TURNSTILE_SECRET = prev;
|
|
else delete process.env.PAGE_DATA_TURNSTILE_SECRET;
|
|
}
|
|
});
|
|
|
|
test('extractPageDataCaptchaToken reads header and body fields', () => {
|
|
assert.equal(
|
|
extractPageDataCaptchaToken({ headers: { 'x-page-data-captcha': 'hdr' } }, {}),
|
|
'hdr',
|
|
);
|
|
assert.equal(extractPageDataCaptchaToken({}, { turnstile_token: 'body' }), 'body');
|
|
});
|
|
|
|
test('buildPageDataPolicyFromPublishInput applies defaults by access mode', () => {
|
|
const policy = buildPageDataPolicyFromPublishInput({
|
|
pageId: 'page-1',
|
|
ownerUserId: 'user-1',
|
|
accessMode: 'public',
|
|
datasetName: 'signups',
|
|
registryDataset: {
|
|
name: 'signups',
|
|
table: 'signups',
|
|
columns: {
|
|
read: ['id', 'name'],
|
|
insert: ['name', 'phone'],
|
|
update: ['status'],
|
|
soft_delete: ['id'],
|
|
},
|
|
},
|
|
});
|
|
assert.equal(policy.accessMode, 'public');
|
|
assert.equal(policy.datasets.signups.insert, true);
|
|
assert.equal(policy.datasets.signups.read, false);
|
|
assert.equal(mapPublicationAccessModeToPageData('private_link'), null);
|
|
});
|