feat(mindspace): add achievements showcase with paginated management
Memind CI / Test, build, and release guards (push) Successful in 3m28s
Memind CI / Test, build, and release guards (push) Successful in 3m28s
Ship the M成果 page with list/delete UX, fix deletePage for incomplete page records, route /space/achievements, and add M成果 under the WeChat M空间 menu. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -88,6 +88,45 @@ test('normalizeListPageFilters clamps limit and offset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('pageResponse includes publication metrics when publish row is present', () => {
|
||||
const response = pageInternals.pageResponse(
|
||||
{
|
||||
id: 'page-1',
|
||||
user_id: 'user-1',
|
||||
category_id: 'cat-1',
|
||||
category_code: 'public',
|
||||
source_session_id: null,
|
||||
source_message_id: null,
|
||||
source_asset_id: null,
|
||||
title: '成果页面',
|
||||
summary: '摘要',
|
||||
page_type: 'html',
|
||||
template_id: 'static-html',
|
||||
status: 'published',
|
||||
visibility: 'public',
|
||||
current_version_id: 'ver-1',
|
||||
version_no: 3,
|
||||
pub_access_mode: 'public',
|
||||
pub_public_url: 'https://m.tkmind.cn/u/john/pages/demo',
|
||||
pub_id: 'pub-1',
|
||||
pub_status: 'online',
|
||||
pub_view_count: 128,
|
||||
pub_published_at: 1_700_000_000_000,
|
||||
created_at: 1_699_000_000_000,
|
||||
updated_at: 1_700_100_000_000,
|
||||
},
|
||||
{ includeContent: false },
|
||||
);
|
||||
|
||||
assert.equal(response.publicationId, 'pub-1');
|
||||
assert.equal(response.publicationStatus, 'online');
|
||||
assert.equal(response.viewCount, 128);
|
||||
assert.equal(response.clickCount, null);
|
||||
assert.equal(response.statsSource, 'publication');
|
||||
assert.equal(response.publishedAt, 1_700_000_000_000);
|
||||
assert.equal(response.publicationUrl, 'https://m.tkmind.cn/u/john/pages/demo');
|
||||
});
|
||||
|
||||
test('preview rendering escapes active HTML and emits a restrictive CSP', () => {
|
||||
const html = pageInternals.renderPreviewHtml({
|
||||
title: '<script>alert(1)</script>',
|
||||
@@ -295,3 +334,70 @@ test('getPage falls back to workspace html when storage asset is missing', async
|
||||
assert.equal(page.title, 'Page Demo');
|
||||
assert.match(page.content, /<h1>Body<\/h1>/);
|
||||
});
|
||||
|
||||
test('deletePage succeeds when page content asset chain is incomplete', async () => {
|
||||
const userId = 'user-1';
|
||||
const pageId = 'page-broken';
|
||||
let deletedPage = false;
|
||||
const conn = {
|
||||
async beginTransaction() {},
|
||||
async commit() {},
|
||||
async rollback() {},
|
||||
async release() {},
|
||||
query: async (sql) => {
|
||||
const normalized = String(sql);
|
||||
if (normalized.includes('FOR UPDATE') && normalized.includes('h5_page_records')) {
|
||||
return [[{ id: pageId, space_id: 'space-1', title: 'test', source_asset_id: null }]];
|
||||
}
|
||||
if (normalized.includes('FROM h5_page_versions pv')) {
|
||||
return [[]];
|
||||
}
|
||||
if (normalized.includes('FROM h5_publish_records') && normalized.includes("status = 'online'")) {
|
||||
return [[]];
|
||||
}
|
||||
if (normalized.includes('FROM h5_agent_jobs')) {
|
||||
return [[{ job_count: 0 }]];
|
||||
}
|
||||
if (normalized.includes("SET status = 'deleted'") && normalized.includes('h5_page_records')) {
|
||||
deletedPage = true;
|
||||
return [{ affectedRows: 1 }];
|
||||
}
|
||||
if (normalized.includes('UPDATE h5_agent_jobs')) {
|
||||
return [{ affectedRows: 0 }];
|
||||
}
|
||||
return [[]];
|
||||
},
|
||||
};
|
||||
const pool = {
|
||||
getConnection: async () => conn,
|
||||
query: async (sql) => {
|
||||
const normalized = String(sql);
|
||||
if (
|
||||
normalized.includes('FROM h5_page_records p') &&
|
||||
normalized.includes('page_type')
|
||||
) {
|
||||
return [[{
|
||||
id: pageId,
|
||||
title: 'test',
|
||||
page_type: 'markdown',
|
||||
space_id: 'space-1',
|
||||
source_asset_id: null,
|
||||
}]];
|
||||
}
|
||||
if (normalized.includes('JOIN h5_asset_versions')) {
|
||||
return [[]];
|
||||
}
|
||||
if (normalized.includes('source_snapshot_json')) {
|
||||
return [[{ source_snapshot_json: null }]];
|
||||
}
|
||||
return [[]];
|
||||
},
|
||||
};
|
||||
|
||||
const pageService = createPageService(pool, { storageRoot: '/tmp/unused', h5Root: null });
|
||||
const result = await pageService.deletePage(userId, pageId);
|
||||
assert.equal(result.deleted, true);
|
||||
assert.equal(result.pageId, pageId);
|
||||
assert.equal(result.title, 'test');
|
||||
assert.equal(deletedPage, true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user