import assert from 'node:assert/strict'; import test from 'node:test'; import { bootstrapPortalIntegrationServices } from './portal-integration-services-bootstrap.mjs'; function createSetup(overrides = {}) { const calls = []; const pool = { id: 'pool' }; const userAuth = { async getUserById(userId) { calls.push(['get-user', userId]); return { id: userId, username: 'John' }; }, setRechargeNotifier(notifier) { calls.push(['set-recharge-notifier']); this.rechargeNotifier = notifier; }, }; const sessionAccess = { id: 'session-access' }; const tkmindProxy = { apiFetch: { id: 'api-fetch' }, startSessionForUser(userId, options) { calls.push(['start-session', userId, options]); return { sessionId: 'session-1' }; }, async resolveTarget(sessionId) { calls.push(['resolve-target', sessionId]); return 'http://target'; }, apiFetchTo(target, pathname, init) { calls.push([ 'api-fetch-to', target, pathname, init, ]); return { ok: true }; }, submitSessionReplyForUser(...args) { calls.push(['submit-reply', ...args]); return { accepted: true }; }, applySessionLlmProvider(sessionId) { calls.push(['apply-provider', sessionId]); return { applied: true }; }, }; const wechatMpService = { enabled: true, sendTextToUser(userId, text) { calls.push(['send-wechat-text', userId, text]); return { sent: true }; }, }; const notificationDispatcher = { async sendRechargeSuccess(options) { calls.push(['recharge-success', options]); }, }; const scheduleService = { id: 'schedule' }; const mindSpacePublicFinish = { async prepareWechatHtmlDelivery() { return { confirmedArtifacts: [] }; }, async ensureWechatFreshPageThumbnails() { return { ok: true }; }, async prepareWechatPageDataDelivery() { return { outcome: { action: 'skip' } }; }, }; const sessionSnapshotService = { isEnabled() { return true; }, refresh(...args) { calls.push(['snapshot-refresh', ...args]); return { refreshed: true }; }, }; let wechatOptions; let notificationOptions; let reminderOptions; let pageEditOptions; let timerCallback; let timerDelay; let unrefCount = 0; const subscriptionService = { async processAutoRenewals() { calls.push(['auto-renew']); return { renewed: 2, failed: 1 }; }, async expireStaleSubscriptions() { calls.push(['expire-subscriptions']); return 3; }, }; const options = { pool, h5Root: '/app', codeRoot: '/runtime', env: { H5_SCHEDULE_ENABLED: '1', H5_REMINDER_WORKER_ENABLED: '1', }, usersRoot: '/users', wechatMpConfig: { enabled: true }, mindSpacePublicFinish, userAuth, sessionAccess, tkmindProxy, scheduleService, wechatScheduleLlmConfigService: { id: 'schedule-llm', }, llmProviderService: { id: 'llm' }, chatIntentRouter: { classifySessionAction(options) { calls.push(['classify-session', options]); return { action: 'chat' }; }, }, systemDisclosurePolicyService: { id: 'system-disclosure-policy', }, sessionSnapshotService, mindSpaceAnalyticsConfig: { enabled: true }, mindSpaceRybbitConfig: { enabled: true }, subscriptionService, apiTarget: 'http://api', apiSecret: 'secret', mindSpacePages: { id: 'pages' }, mindSpacePageLiveEdit: { id: 'live-edit' }, logger: { log(...args) { calls.push(['log', ...args]); }, warn(...args) { calls.push(['warn', ...args]); }, }, async loadWechatMpModuleFn(h5Root) { calls.push(['load-wechat', h5Root]); return { createWechatMpService(receivedOptions) { calls.push(['create-wechat']); wechatOptions = receivedOptions; return wechatMpService; }, }; }, resolveAnalyticsOwnerSegmentFn(user) { calls.push(['owner-segment', user]); return 'segment'; }, resolveAnalyticsOwnerLabelFn(user) { calls.push(['owner-label', user]); return 'label'; }, resolveAnalyticsPlanFn(user) { calls.push(['plan-type', user]); return 'pro'; }, sendMindSpaceAnalyticsEventFn(event) { calls.push(['analytics', event]); return Promise.resolve(); }, sendMindSpaceRybbitEventFn(event) { calls.push(['rybbit', event]); return Promise.resolve(); }, createNotificationDispatcherFn(receivedOptions) { calls.push(['notification-dispatcher']); notificationOptions = receivedOptions; return notificationDispatcher; }, startScheduleReminderWorkerFn(receivedOptions) { calls.push(['reminder-worker']); reminderOptions = receivedOptions; return { id: 'reminder-worker' }; }, createPageEditSessionServiceFn(receivedOptions) { calls.push(['page-edit']); pageEditOptions = receivedOptions; return { id: 'page-edit' }; }, setIntervalFn(callback, delay) { calls.push(['set-interval']); timerCallback = callback; timerDelay = delay; return { unref() { unrefCount += 1; calls.push(['timer-unref']); }, }; }, ...overrides, }; return { calls, options, pool, userAuth, sessionAccess, tkmindProxy, wechatMpService, notificationDispatcher, scheduleService, sessionSnapshotService, subscriptionService, getCaptured() { return { wechatOptions, notificationOptions, reminderOptions, pageEditOptions, timerCallback, timerDelay, unrefCount, }; }, }; } test('requires Portal integration dependencies', async () => { await assert.rejects( bootstrapPortalIntegrationServices(), /requires Portal integration dependencies/, ); }); test('preserves WeChat service configuration and proxy callbacks', async () => { const setup = createSetup(); await bootstrapPortalIntegrationServices( setup.options, ); const { wechatOptions } = setup.getCaptured(); assert.deepEqual( setup.calls.find(([kind]) => kind === 'load-wechat'), ['load-wechat', '/runtime'], ); assert.equal( wechatOptions.pageDataFinishGuard, setup.options.mindSpacePublicFinish, ); assert.equal( wechatOptions.pageDataDeliveryReviewer, null, ); assert.equal( wechatOptions.htmlDeliveryAuthority, setup.options.mindSpacePublicFinish, ); assert.equal( wechatOptions.apiFetch, setup.tkmindProxy.apiFetch, ); assert.equal( wechatOptions.scheduleService, setup.scheduleService, ); assert.equal( wechatOptions.systemDisclosurePolicyService, setup.options.systemDisclosurePolicyService, ); assert.deepEqual( wechatOptions.startAgentSession({ userId: 'user-1', workingDir: '/workspace', sessionPolicy: { sandboxed: true }, }), { sessionId: 'session-1' }, ); assert.deepEqual( setup.calls.find(([kind]) => kind === 'start-session'), [ 'start-session', 'user-1', { workingDir: '/workspace', sessionPolicy: { sandboxed: true }, origin: 'wechat', }, ], ); assert.deepEqual( await wechatOptions.sessionApiFetch( 'session-1', '/status', { method: 'GET' }, ), { ok: true }, ); assert.deepEqual( wechatOptions.submitSessionReply({ userId: 'user-1', sessionId: 'session-1', requestId: 'request-1', userMessage: 'hello', options: { deep: true }, }), { accepted: true }, ); assert.deepEqual( wechatOptions.sessionIntentClassifier({ text: 'hello', }), { action: 'chat' }, ); assert.deepEqual( wechatOptions.applySessionLlmProvider('session-1'), { applied: true }, ); }); test('wires the isolated WeChat Page Data reviewer only when its channel flag is enabled', async () => { const toolGateway = { id: 'wechat-review-tool-gateway' }; const reviewer = { reviewIfNeeded() {} }; let gatewayOptions; let reviewerOptions; const setup = createSetup({ wechatMpConfig: { enabled: true, pageDataAiderReviewEnabled: true, }, createToolGatewayFn(options) { gatewayOptions = options; return toolGateway; }, createPageDataDeliveryCodeReviewServiceFn(options) { reviewerOptions = options; return reviewer; }, }); await bootstrapPortalIntegrationServices(setup.options); const { wechatOptions } = setup.getCaptured(); assert.equal(gatewayOptions.llmProviderService, setup.options.llmProviderService); assert.equal(reviewerOptions.toolGateway, toolGateway); assert.equal(reviewerOptions.userAuth, setup.options.userAuth); assert.equal(wechatOptions.pageDataDeliveryReviewer, reviewer); }); test('preserves snapshot refresh through the resolved session target', async () => { const setup = createSetup(); await bootstrapPortalIntegrationServices( setup.options, ); const { wechatOptions } = setup.getCaptured(); assert.equal( typeof wechatOptions.refreshSessionSnapshot, 'function', ); assert.deepEqual( wechatOptions.refreshSessionSnapshot( 'session-1', 'user-1', ), { refreshed: true }, ); const refreshCall = setup.calls.find( ([name]) => name === 'snapshot-refresh', ); const fetchSnapshot = refreshCall[3]; assert.deepEqual( await fetchSnapshot('/snapshot', { method: 'GET', }), { ok: true }, ); assert.ok( setup.calls.some( ([name, sessionId]) => name === 'resolve-target' && sessionId === 'session-1', ), ); }); test('preserves generated-page analytics projection', async () => { const setup = createSetup(); await bootstrapPortalIntegrationServices( setup.options, ); const { wechatOptions } = setup.getCaptured(); await wechatOptions.onPageGenerated({ userId: 'user-1', sessionId: 'session-1', artifacts: [ { relativePath: 'public/page.html', url: 'https://example/page', }, ], }); assert.equal( setup.calls.filter(([name]) => name === 'get-user') .length, 1, ); const analyticsCall = setup.calls.find( ([name]) => name === 'analytics', ); assert.deepEqual(analyticsCall[1], { config: { enabled: true }, eventName: 'page_generated', ownerId: 'user-1', ownerSegment: 'segment', ownerLabel: 'label', planType: 'pro', generatedAt: analyticsCall[1].generatedAt, pageId: 'public/page.html', publicationId: 'session-1', agentRunId: 'session-1', channel: 'wechat_mp', url: 'https://example/page', }); assert.match( analyticsCall[1].generatedAt, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/, ); const rybbitCall = setup.calls.find( ([name]) => name === 'rybbit', ); assert.deepEqual(rybbitCall[1], { ...analyticsCall[1], config: { enabled: true }, }); }); test('preserves notification, recharge, reminder, and page-edit wiring', async () => { const setup = createSetup(); const result = await bootstrapPortalIntegrationServices( setup.options, ); const captured = setup.getCaptured(); assert.equal( captured.notificationOptions.sendWechatTextToUser( 'user-1', 'hello', ).sent, true, ); await setup.userAuth.rechargeNotifier({ userId: 'user-1', title: 'Recharge', body: 'Success', dedupeKey: 'dedupe-1', }); assert.deepEqual(captured.reminderOptions, { scheduleService: setup.scheduleService, notificationDispatcher: setup.notificationDispatcher, }); assert.equal(result.scheduleReminderWorker.id, 'reminder-worker'); assert.deepEqual(captured.pageEditOptions, { apiTarget: 'http://api', apiSecret: 'secret', userAuth: setup.userAuth, sessionAccess: setup.sessionAccess, pageService: setup.options.mindSpacePages, pageLiveEdit: setup.options.mindSpacePageLiveEdit, llmProviderService: setup.options.llmProviderService, }); }); test('preserves subscription timer work, logs, and unref', async () => { const setup = createSetup(); await bootstrapPortalIntegrationServices( setup.options, ); const captured = setup.getCaptured(); assert.equal(captured.timerDelay, 60 * 60 * 1000); assert.equal(captured.unrefCount, 1); await captured.timerCallback(); assert.ok( setup.calls.some( ([name, message]) => name === 'log' && message === 'Auto-renewed 2 subscription(s)', ), ); assert.ok( setup.calls.some( ([name, message]) => name === 'log' && message === 'Auto-renew failed for 1 subscription(s) (balance insufficient)', ), ); assert.ok( setup.calls.some( ([name, message]) => name === 'log' && message === 'Expired 3 stale subscription(s)', ), ); }); test('keeps optional integrations disabled and contains timer failures', async () => { const setup = createSetup({ env: {}, mindSpacePublicFinish: null, scheduleService: null, subscriptionService: { async processAutoRenewals() { throw new Error('renew failed'); }, async expireStaleSubscriptions() { return 0; }, }, sessionSnapshotService: { isEnabled: () => false, }, async loadWechatMpModuleFn() { return { createWechatMpService(options) { setup.disabledWechatOptions = options; return { enabled: false }; }, }; }, }); const result = await bootstrapPortalIntegrationServices( setup.options, ); const captured = setup.getCaptured(); await captured.timerCallback(); assert.equal( setup.disabledWechatOptions.pageDataFinishGuard, null, ); assert.equal( setup.disabledWechatOptions .htmlDeliveryAuthority, null, ); assert.equal( setup.disabledWechatOptions.scheduleService, null, ); assert.equal( setup.disabledWechatOptions.refreshSessionSnapshot, null, ); assert.equal(result.scheduleReminderWorker, null); assert.equal( captured.notificationOptions.sendWechatTextToUser, null, ); assert.ok( setup.calls.some( ([name, message, error]) => name === 'warn' && message === 'Subscription expiry check failed:' && error?.message === 'renew failed', ), ); }); test('passive candidate runtime disables singleton reminder and subscription timers', async () => { const setup = createSetup({ env: { H5_SCHEDULE_ENABLED: '1', H5_REMINDER_WORKER_ENABLED: '1', MEMIND_RUNTIME_ROLE: 'candidate', MEMIND_CANARY_PASSIVE_RUNTIME: '1', }, }); const result = await bootstrapPortalIntegrationServices(setup.options); const captured = setup.getCaptured(); assert.equal(result.scheduleReminderWorker, null); assert.equal(result.subscriptionExpiryTimer, null); assert.equal(captured.reminderOptions, undefined); assert.equal(captured.timerCallback, undefined); assert.equal(setup.calls.some(([name]) => name === 'reminder-worker'), false); assert.equal(setup.calls.some(([name]) => name === 'set-interval'), false); });