Files
memind/server/portal-memory-session-services-bootstrap.test.mjs
T
2026-07-24 19:18:14 +08:00

369 lines
9.7 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { bootstrapPortalMemorySessionServices } from './portal-memory-session-services-bootstrap.mjs';
function createSetup(overrides = {}) {
const calls = [];
const pool = { id: 'pool' };
const llmProviderService = { id: 'llm' };
const userAuth = { id: 'user-auth' };
const sessionAccess = { id: 'session-access' };
const memoryV2ConfigService = {
async getRuntimeState() {
calls.push(['runtime-state']);
return {
overrides: {
MEMORY_BACKEND: 'pgvector',
SHARED: 'override',
},
};
},
};
const conversationMemoryService = {
id: 'conversation-memory',
};
const memoryV2 = { id: 'memory-v2' };
const episodicMemoryService = {
id: 'episodic',
async ensureSchema() {
calls.push(['episodic-schema']);
},
};
const sessionSnapshotService = { id: 'snapshot' };
const sessionStreamStore = { id: 'stream-store' };
const directChatService = { id: 'direct-chat' };
const chatIntentRouter = { id: 'intent-router' };
const systemDisclosurePolicyService = {
id: 'system-disclosure-policy',
async initialize() {
calls.push(['system-disclosure-initialize']);
},
};
let skillRuntimeOptions;
let agentCodeRunOptions;
let conversationOptions;
let memoryV2Options;
let episodicOptions;
let snapshotOptions;
let streamStoreOptions;
let directChatOptions;
let routerOptions;
const options = {
pool,
h5Root: '/app',
env: {
BASE_ONLY: 'base',
SHARED: 'base',
},
llmProviderService,
userAuth,
sessionAccess,
logger: {
warn(...args) {
calls.push(['warn', ...args]);
},
},
createMemoryV2AdminConfigServiceFn(receivedPool) {
assert.equal(receivedPool, pool);
calls.push(['memory-config']);
return memoryV2ConfigService;
},
createSkillRuntimeAdminConfigServiceFn(
receivedPool,
receivedOptions,
) {
assert.equal(receivedPool, pool);
calls.push(['skill-config']);
skillRuntimeOptions = receivedOptions;
return { id: 'skill-config' };
},
createSystemDisclosurePolicyServiceFn(
receivedPool,
) {
assert.equal(receivedPool, pool);
calls.push(['system-disclosure-config']);
return systemDisclosurePolicyService;
},
createAgentCodeRunAdminConfigServiceFn(
receivedPool,
receivedOptions,
) {
assert.equal(receivedPool, pool);
calls.push(['agent-code-run-config']);
agentCodeRunOptions = receivedOptions;
return { id: 'agent-code-run-config' };
},
createWechatScheduleLlmConfigServiceFn(
receivedPool,
) {
assert.equal(receivedPool, pool);
calls.push(['wechat-schedule-config']);
return { id: 'wechat-schedule-config' };
},
createConversationMemoryServiceFn(
receivedPool,
receivedOptions,
) {
assert.equal(receivedPool, pool);
calls.push(['conversation-memory']);
conversationOptions = receivedOptions;
return conversationMemoryService;
},
async createManagedMemoryV2RuntimeFn(
receivedOptions,
) {
calls.push(['memory-runtime']);
memoryV2Options = receivedOptions;
return memoryV2;
},
createEpisodicMemoryServiceFn(
receivedPool,
receivedOptions,
) {
assert.equal(receivedPool, pool);
calls.push(['episodic-memory']);
episodicOptions = receivedOptions;
return episodicMemoryService;
},
createSessionSnapshotServiceFn(
receivedPool,
receivedOptions,
) {
assert.equal(receivedPool, pool);
calls.push(['snapshot']);
snapshotOptions = receivedOptions;
return sessionSnapshotService;
},
isSessionStreamReplayEnabledFn() {
calls.push(['replay-enabled']);
return true;
},
createSessionStreamStoreFn(receivedOptions) {
calls.push(['stream-store']);
streamStoreOptions = receivedOptions;
return sessionStreamStore;
},
createDirectChatServiceFn(receivedOptions) {
calls.push(['direct-chat']);
directChatOptions = receivedOptions;
return directChatService;
},
createManagedChatIntentRouterFn(
receivedOptions,
) {
calls.push(['intent-router']);
routerOptions = receivedOptions;
return chatIntentRouter;
},
...overrides,
};
return {
calls,
options,
pool,
llmProviderService,
userAuth,
sessionAccess,
memoryV2ConfigService,
conversationMemoryService,
memoryV2,
episodicMemoryService,
systemDisclosurePolicyService,
sessionSnapshotService,
sessionStreamStore,
getCaptured() {
return {
skillRuntimeOptions,
agentCodeRunOptions,
conversationOptions,
memoryV2Options,
episodicOptions,
snapshotOptions,
streamStoreOptions,
directChatOptions,
routerOptions,
};
},
};
}
test('requires the Memory and Session dependencies', async () => {
await assert.rejects(
bootstrapPortalMemorySessionServices(),
/requires memory and session dependencies/,
);
});
test('preserves Memory, Session, and chat assembly order', async () => {
const setup = createSetup();
const result =
await bootstrapPortalMemorySessionServices(
setup.options,
);
assert.deepEqual(
setup.calls.map(([name]) => name),
[
'memory-config',
'skill-config',
'system-disclosure-config',
'system-disclosure-initialize',
'agent-code-run-config',
'wechat-schedule-config',
'conversation-memory',
'memory-runtime',
'episodic-memory',
'episodic-schema',
'snapshot',
'replay-enabled',
'stream-store',
'direct-chat',
'intent-router',
],
);
assert.equal(
result.memoryV2ConfigService,
setup.memoryV2ConfigService,
);
assert.equal(
result.systemDisclosurePolicyService,
setup.systemDisclosurePolicyService,
);
assert.equal(result.agentCodeRunPolicyService.id, 'agent-code-run-config');
assert.equal(
result.sessionStreamStore,
setup.sessionStreamStore,
);
assert.equal(result.directChatService.id, 'direct-chat');
assert.equal(result.chatIntentRouter.id, 'intent-router');
});
test('preserves configuration and runtime service wiring', async () => {
const setup = createSetup();
await bootstrapPortalMemorySessionServices(
setup.options,
);
const captured = setup.getCaptured();
assert.deepEqual(captured.skillRuntimeOptions, {
h5Root: '/app',
});
assert.deepEqual(captured.agentCodeRunOptions, {
env: {
BASE_ONLY: 'base',
SHARED: 'base',
},
});
assert.equal(
captured.conversationOptions.llmProviderService,
setup.llmProviderService,
);
assert.deepEqual(captured.memoryV2Options, {
legacyMemoryService: setup.conversationMemoryService,
configService: setup.memoryV2ConfigService,
mysqlPool: setup.pool,
});
assert.equal(
captured.episodicOptions.logger,
setup.options.logger,
);
assert.deepEqual(captured.snapshotOptions, {
conversationMemoryService:
setup.conversationMemoryService,
memoryV2: setup.memoryV2,
episodicMemoryService: setup.episodicMemoryService,
});
assert.deepEqual(captured.streamStoreOptions, {
pool: setup.pool,
});
});
test('merges runtime overrides and falls back to the base environment', async () => {
const setup = createSetup();
const result =
await bootstrapPortalMemorySessionServices(
setup.options,
);
const captured = setup.getCaptured();
assert.deepEqual(
await captured.conversationOptions.getEffectiveEnv(),
{
BASE_ONLY: 'base',
SHARED: 'override',
MEMORY_BACKEND: 'pgvector',
},
);
assert.equal(
captured.conversationOptions.getEffectiveEnv,
captured.episodicOptions.getEffectiveEnv,
);
setup.memoryV2ConfigService.getRuntimeState =
async () => {
throw new Error('config unavailable');
};
assert.deepEqual(await result.getEffectiveEnv(), {
BASE_ONLY: 'base',
SHARED: 'base',
});
});
test('preserves Direct Chat and Intent Router dependencies', async () => {
const setup = createSetup();
await bootstrapPortalMemorySessionServices(
setup.options,
);
const captured = setup.getCaptured();
assert.deepEqual(captured.directChatOptions, {
userAuth: setup.userAuth,
sessionAccess: setup.sessionAccess,
llmProviderService: setup.llmProviderService,
sessionSnapshotService: setup.sessionSnapshotService,
memoryV2: setup.memoryV2,
conversationMemoryService:
setup.conversationMemoryService,
episodicMemoryService: setup.episodicMemoryService,
});
assert.deepEqual(captured.routerOptions, {
llmProviderService: setup.llmProviderService,
memoryV2: setup.memoryV2,
conversationMemoryService:
setup.conversationMemoryService,
episodicMemoryService: setup.episodicMemoryService,
configService: setup.memoryV2ConfigService,
});
});
test('degrades episodic schema failures and can disable replay storage', async () => {
const setup = createSetup({
createEpisodicMemoryServiceFn() {
return {
async ensureSchema() {
throw new Error('schema unavailable');
},
};
},
isSessionStreamReplayEnabledFn: () => false,
});
const result =
await bootstrapPortalMemorySessionServices(
setup.options,
);
assert.equal(result.sessionStreamStore, null);
assert.ok(
setup.calls.some(
([name, message, detail]) =>
name === 'warn' &&
message ===
'[episodic-memory] schema setup degraded; snapshot fallback remains available:' &&
detail === 'schema unavailable',
),
);
});