Files
memind/server/portal-domain-services-bootstrap.test.mjs
T
john fde6503bdf
Memind CI / Test, build, and release guards (push) Has been cancelled
feat(mindspace): add SEO/GEO delivery, page template catalog, and admin hooks
Enable optional SEO/GEO injection and discovery routes for confirmed public pages while keeping private pages noindex. Add premium page template skills, portal catalog API, template shop UI, and Baidu push gated by memind_adm config.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 08:06:12 +08:00

460 lines
12 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { bootstrapPortalDomainServices } from './portal-domain-services-bootstrap.mjs';
function createBootstrapSetup(overrides = {}) {
const calls = [];
const pool = {
async query(sql, params) {
calls.push({ kind: 'query', sql, params });
if (sql.includes('agent_session_id')) {
return [[{ user_id: 'agent-user' }]];
}
if (sql.includes('username')) {
return [[{ id: 'named-user' }]];
}
return [[]];
},
};
const pageService = {
findPageByRelativePath() {},
};
const adapter = {
serviceFacade: { id: 'facade' },
chatSaveService: { id: 'chat-save' },
conversationArtifactService: {
id: 'conversation-artifacts',
},
publicFinishService: { id: 'public-finish' },
workspacePublicationDeliveryService: {
id: 'workspace-publication-delivery',
},
workspacePageDeliveryService: {
id: 'workspace-deliver',
},
conversationPackageRegistry: { id: 'registry' },
assetService: { id: 'assets' },
pageService,
pageSyncService: { id: 'page-sync' },
pageLiveEditService: { id: 'live-edit' },
assetAgentService: { id: 'asset-agent' },
publicationService: { id: 'publications' },
cleanupService: { id: 'cleanup' },
assertReady() {
calls.push({ kind: 'adapter-ready' });
},
};
const plazaRedis = {
enabled: true,
invalidateFeedCaches() {
calls.push({ kind: 'invalidate-feed' });
},
};
let adapterOptions = null;
let pageDataOptions = null;
let pageDataPublicOptions = null;
let plazaPostOptions = null;
let plazaOpsOptions = null;
const dependencies = {
pool,
h5Root: '/workspace',
env: {
H5_DEFAULT_TIMEZONE: 'Asia/Singapore',
PLAZA_REDIS_URL: 'redis://plaza',
MEMIND_RYBBIT_ENABLED: 'true',
MEMIND_RYBBIT_SITE_ID: '2',
},
runtime: {
maxFileBytes: 100,
aiDailyLimit: 2,
publicPageLimit: 3,
monthlyViewLimit: 4,
remote: { enabled: false },
},
analyticsConfig: {
enabled: false,
hostPath: '/old',
},
getUserAuth: () => ({
resolveWorkingDir: (userId) => `/users/${userId}`,
}),
getSessionSnapshotService: () => ({
get: (sessionId) => ({ sessionId }),
}),
onMindSearchSchemaReady({ pool: receivedPool }) {
assert.equal(receivedPool, pool);
calls.push({ kind: 'auth-pool-ready' });
},
workspaceMaintenanceEnabled: true,
logger: {
log(message) {
calls.push({ kind: 'log', message });
},
},
async initSchemaFn(receivedPool) {
assert.equal(receivedPool, pool);
calls.push({ kind: 'init-schema' });
},
createMindSearchConfigServiceFn(receivedPool) {
assert.equal(receivedPool, pool);
return {
async ensureSchema() {
calls.push({ kind: 'mind-search-schema' });
},
};
},
async ensureMindSpaceConfigFn(receivedPool, options) {
assert.equal(receivedPool, pool);
calls.push({ kind: 'mindspace-config', options });
},
async loadMindSpaceConfigFn() {
calls.push({ kind: 'load-mindspace-config' });
return {
analytics: {
enabled: true,
websiteId: 'website-1',
idSecret: 'secret-1',
},
seoGeo: {
enabled: true,
seo: { enabled: true, baiduPush: true },
geo: { enabled: false },
},
};
},
async loadMindSpaceConfigCachedFn() {
calls.push({ kind: 'load-mindspace-config-cached' });
return {
seoGeo: {
enabled: true,
seo: { enabled: true, baiduPush: true },
},
};
},
createScheduleServiceFn(receivedPool, options) {
calls.push({ kind: 'schedule', receivedPool, options });
return { id: 'schedule' };
},
createPageDataServiceFn(options) {
pageDataOptions = options;
return { id: 'page-data' };
},
createPageDataPublicServiceFn(options) {
pageDataPublicOptions = options;
return { id: 'page-data-public' };
},
createFeedbackServiceFn() {
return { id: 'feedback' };
},
createMindSpaceServiceFn(receivedPool, options) {
calls.push({ kind: 'mindspace', receivedPool, options });
return { id: 'mindspace' };
},
createMindSpaceServerAdapterFn(options) {
adapterOptions = options;
return adapter;
},
assertMindSpaceServerAdapterContractFn(received) {
calls.push({ kind: 'adapter-contract' });
return received;
},
async ensureAlgorithmConfigFn() {
calls.push({ kind: 'algorithm-schema' });
},
async loadAlgorithmConfigFn() {
calls.push({ kind: 'algorithm-load' });
return { version: 1 };
},
async createPlazaRedisFn(url, receivedPool) {
calls.push({ kind: 'redis', url, receivedPool });
return plazaRedis;
},
createPlazaSeoServiceFn() {
return {
notifyPostPublished(postId) {
calls.push({ kind: 'seo-published', postId });
},
};
},
createPlazaInteractionServiceFn(_pool, options) {
calls.push({ kind: 'interactions', options });
return {
async loadViewerReactions(viewerId, postIds) {
calls.push({
kind: 'viewer-reactions',
viewerId,
postIds,
});
return {};
},
};
},
createPlazaEventServiceFn() {
return { id: 'events' };
},
createPlazaRecommendServiceFn(_pool, options) {
calls.push({ kind: 'recommend', options });
return { id: 'recommend' };
},
createPlazaPostServiceFn(_pool, options) {
plazaPostOptions = options;
return {
reviewPost(...args) {
calls.push({ kind: 'review-post', args });
},
};
},
createPlazaOpsServiceFn(_pool, options) {
plazaOpsOptions = options;
return {
async loadActiveFeaturedPosts(viewerId) {
return { viewerId };
},
};
},
startPlazaTasksFn(options) {
calls.push({ kind: 'plaza-tasks', options });
},
recalculateHotScoresFn() {},
writebackPublicationsFn() {},
formatPostRowFn() {},
publishKeyUuid: /^uuid-/,
...overrides,
};
return {
calls,
pool,
adapter,
pageService,
plazaRedis,
dependencies,
captured: {
get adapterOptions() {
return adapterOptions;
},
get pageDataOptions() {
return pageDataOptions;
},
get pageDataPublicOptions() {
return pageDataPublicOptions;
},
get plazaPostOptions() {
return plazaPostOptions;
},
get plazaOpsOptions() {
return plazaOpsOptions;
},
},
};
}
test('domain bootstrap requires pool and runtime', async () => {
await assert.rejects(
() => bootstrapPortalDomainServices(),
/requires pool and runtime/,
);
});
test('domain bootstrap preserves schema and service assembly order', async () => {
const setup = createBootstrapSetup();
const result = await bootstrapPortalDomainServices(
setup.dependencies,
);
assert.deepEqual(
setup.calls
.filter((call) =>
[
'mind-search-schema',
'auth-pool-ready',
'init-schema',
'mindspace-config',
'load-mindspace-config',
'adapter-contract',
'adapter-ready',
'algorithm-schema',
'algorithm-load',
'redis',
'plaza-tasks',
].includes(call.kind),
)
.map((call) => call.kind),
[
'mind-search-schema',
'auth-pool-ready',
'init-schema',
'mindspace-config',
'load-mindspace-config',
'adapter-contract',
'adapter-ready',
'algorithm-schema',
'algorithm-load',
'redis',
'plaza-tasks',
],
);
assert.equal(result.mindSpace.id, 'mindspace');
assert.equal(result.mindSpaceAssets.id, 'assets');
assert.equal(result.mindSpaceChatSave.id, 'chat-save');
assert.equal(
result.mindSpaceConversationArtifacts.id,
'conversation-artifacts',
);
assert.equal(
result.mindSpacePublicFinish.id,
'public-finish',
);
assert.equal(
result
.mindSpaceWorkspacePublicationDelivery
.id,
'workspace-publication-delivery',
);
assert.equal(result.mindSpacePages, setup.pageService);
assert.equal(result.workspacePageDeliver.id, 'workspace-deliver');
assert.equal(result.mindSpaceCleanup.id, 'cleanup');
assert.equal(result.plazaRedis, setup.plazaRedis);
assert.equal(
result.mindSpaceAnalyticsConfig.enabled,
true,
);
assert.equal(
result.mindSpaceAnalyticsConfig.hostPath,
'/analytics',
);
assert.equal(
result.mindSpaceAnalyticsConfig.scriptPath,
'/analytics/script.js',
);
assert.equal(result.mindSpaceRybbitConfig.enabled, true);
assert.equal(result.mindSpaceRybbitConfig.siteId, '2');
assert.equal(result.mindSpaceRybbitConfig.idSecret, 'secret-1');
});
test('domain bootstrap preserves delayed user, snapshot, and Page Data dependencies', async () => {
let activeUserAuth = {
resolveWorkingDir: (userId) => `/first/${userId}`,
};
const snapshot = { id: 'snapshot-1' };
const setup = createBootstrapSetup({
getUserAuth: () => activeUserAuth,
getSessionSnapshotService: () => ({
get: () => snapshot,
}),
});
await bootstrapPortalDomainServices(setup.dependencies);
assert.equal(
await setup.captured.pageDataOptions.resolveWorkspaceRoot({
workspaceRoot: '/explicit',
}),
'/explicit',
);
activeUserAuth = {
resolveWorkingDir: (userId) => `/second/${userId}`,
};
assert.equal(
await setup.captured.pageDataOptions.resolveWorkspaceRoot({
id: 'user-1',
}),
'/second/user-1',
);
assert.equal(
setup.captured.pageDataPublicOptions.getPool(),
setup.pool,
);
assert.equal(
setup.captured.pageDataPublicOptions.resolveH5Root(),
'/workspace',
);
assert.equal(
setup.captured.adapterOptions.resolveWorkspaceRoot(
'user-2',
),
'/second/user-2',
);
assert.equal(
setup.captured.adapterOptions.resolveSessionSnapshot(
'session-1',
),
snapshot,
);
assert.equal(
await setup.captured.adapterOptions.resolveUserIdForAgentSession(
'session-1',
),
'agent-user',
);
assert.equal(
setup.captured.adapterOptions.syncWorkspaceAssetsEnabled,
true,
);
});
test('domain bootstrap preserves directory and Plaza cross-service callbacks', async () => {
const setup = createBootstrapSetup();
const result = await bootstrapPortalDomainServices(
setup.dependencies,
);
assert.equal(
await result.resolveUserIdByDirKey('uuid-user'),
'uuid-user',
);
assert.equal(
await result.resolveUserIdByDirKey('alice'),
'named-user',
);
assert.deepEqual(
await setup.captured.plazaPostOptions.loadFeaturedPosts(
'viewer-1',
),
{ viewerId: 'viewer-1' },
);
await setup.captured.plazaPostOptions.onPostPublished('post-1');
await setup.captured.plazaPostOptions.loadViewerReactions(
'viewer-1',
['post-1'],
);
setup.captured.plazaOpsOptions.reviewPost('post-1');
setup.captured.plazaOpsOptions.invalidateFeedCaches();
assert.deepEqual(
setup.calls
.filter((call) =>
[
'seo-published',
'viewer-reactions',
'review-post',
'invalidate-feed',
].includes(call.kind),
)
.map((call) => call.kind),
[
'seo-published',
'viewer-reactions',
'review-post',
'invalidate-feed',
],
);
});
test('passive candidate runtime does not start Plaza background tasks', async () => {
const setup = createBootstrapSetup({
env: {
H5_DEFAULT_TIMEZONE: 'Asia/Singapore',
MEMIND_RUNTIME_ROLE: 'candidate',
MEMIND_CANARY_PASSIVE_RUNTIME: '1',
},
});
await bootstrapPortalDomainServices(setup.dependencies);
assert.equal(setup.calls.some((call) => call.kind === 'plaza-tasks'), false);
assert.ok(
setup.calls.some(
(call) =>
call.kind === 'log'
&& call.message === 'Passive candidate runtime: Plaza background tasks disabled',
),
);
});