fix: harden release gate and page delivery
Memind CI / Test, build, and release guards (push) Failing after 12m3s
Memind CI / Test, build, and release guards (push) Failing after 12m3s
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
|
||||
import { evaluatePageDataHtmlContent } from '../mindspace-page-data-finish-guard.mjs';
|
||||
import { resolveRegisteredPageDataPolicy } from '../page-data-workspace-bind.mjs';
|
||||
import { createUserDataSpaceService } from '../user-data-space-service.mjs';
|
||||
|
||||
test('DATA-05 child survey adds diet while preserving historical rows', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'page-data-children-'));
|
||||
try {
|
||||
const service = createUserDataSpaceService({ workspaceRoot: root });
|
||||
await service.executeSql(`CREATE TABLE children (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
hobby TEXT,
|
||||
created_at TEXT
|
||||
)`);
|
||||
await service.upsertDataset({
|
||||
name: 'children',
|
||||
table: 'children',
|
||||
actions: ['read', 'insert', 'update'],
|
||||
columns: {
|
||||
read: ['id', 'name', 'hobby', 'created_at'],
|
||||
insert: ['name', 'hobby'],
|
||||
update: ['hobby'],
|
||||
},
|
||||
});
|
||||
await service.insertDatasetRow('children', { name: '小明', hobby: '足球' });
|
||||
|
||||
await service.executeSql('ALTER TABLE children ADD COLUMN diet TEXT');
|
||||
await service.upsertDataset({
|
||||
name: 'children',
|
||||
table: 'children',
|
||||
actions: ['read', 'insert', 'update'],
|
||||
columns: {
|
||||
read: ['id', 'name', 'hobby', 'diet', 'created_at'],
|
||||
insert: ['name', 'hobby', 'diet'],
|
||||
update: ['hobby', 'diet'],
|
||||
},
|
||||
});
|
||||
await service.insertDatasetRow('children', {
|
||||
name: '小红',
|
||||
hobby: '绘画',
|
||||
diet: '不吃花生',
|
||||
});
|
||||
const rows = await service.readDatasetRows('children', { orderBy: 'id', orderDir: 'asc' });
|
||||
assert.equal(rows.rows.length, 2);
|
||||
assert.equal(rows.rows[0].name, '小明');
|
||||
assert.equal(rows.rows[0].hobby, '足球');
|
||||
assert.equal(rows.rows[0].diet, null);
|
||||
assert.equal(rows.rows[1].diet, '不吃花生');
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('DATA-13 incomplete schema fails closed, repairs additively, and rebinds modern client policy', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'page-data-repair-'));
|
||||
try {
|
||||
const service = createUserDataSpaceService({ workspaceRoot: root });
|
||||
await service.executeSql(`CREATE TABLE responses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
answer TEXT NOT NULL,
|
||||
created_at TEXT
|
||||
)`);
|
||||
await service.executeSql(
|
||||
"INSERT INTO responses (answer, created_at) VALUES ('历史答案', '2026-01-01T00:00:00.000Z')",
|
||||
);
|
||||
await service.upsertDataset({
|
||||
name: 'responses',
|
||||
table: 'responses',
|
||||
actions: ['read'],
|
||||
columns: {
|
||||
read: ['id', 'answer', 'created_at'],
|
||||
insert: ['answer', 'diet'],
|
||||
soft_delete: ['id'],
|
||||
},
|
||||
});
|
||||
const html = `<script src="/assets/mindspace-page-data-client.js"></script>
|
||||
<script>const c=MindSpacePageData.createClient();
|
||||
c.insertRow("responses",{answer:"新答案",diet:"清淡"});
|
||||
c.deleteRow("responses","1");</script>`;
|
||||
assert.throws(
|
||||
() => resolveRegisteredPageDataPolicy({
|
||||
html,
|
||||
userId: 'user-1',
|
||||
accessMode: 'public',
|
||||
userDataSpace: service,
|
||||
}),
|
||||
(error) => ['dataset_action_not_registered', 'dataset_schema_mismatch'].includes(error?.code),
|
||||
);
|
||||
|
||||
await service.executeSql('ALTER TABLE responses ADD COLUMN diet TEXT');
|
||||
await service.executeSql('ALTER TABLE responses ADD COLUMN deleted_at TEXT');
|
||||
await service.executeSql('ALTER TABLE responses ADD COLUMN deleted_by TEXT');
|
||||
await service.upsertDataset({
|
||||
name: 'responses',
|
||||
table: 'responses',
|
||||
actions: ['read', 'insert', 'soft_delete'],
|
||||
columns: {
|
||||
read: ['id', 'answer', 'diet', 'created_at'],
|
||||
insert: ['answer', 'diet'],
|
||||
soft_delete: ['id'],
|
||||
},
|
||||
});
|
||||
const policy = resolveRegisteredPageDataPolicy({
|
||||
html,
|
||||
userId: 'user-1',
|
||||
accessMode: 'public',
|
||||
userDataSpace: service,
|
||||
});
|
||||
assert.equal(policy.datasets.responses.insert, true);
|
||||
assert.equal(policy.datasets.responses.softDelete, true);
|
||||
const columns = service.listTableColumns('responses').map((column) => column.name);
|
||||
assert.equal(columns.filter((name) => name === 'created_at').length, 1);
|
||||
const rows = await service.readDatasetRows('responses');
|
||||
assert.equal(rows.rows[0].answer, '历史答案');
|
||||
|
||||
const legacy = evaluatePageDataHtmlContent(
|
||||
'<script>localStorage.setItem("rows","[]");fetch("/api/page-data/responses")</script>',
|
||||
{ relativePath: 'public/legacy.html' },
|
||||
);
|
||||
assert.equal(legacy.usesPageDataApi, true);
|
||||
assert.ok(legacy.issues.includes('forbidden_legacy_page_data_api'));
|
||||
assert.ok(legacy.issues.includes('forbidden_local_storage'));
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user