Files
memind/mindspace-local-server-adapter.test.mjs

202 lines
6.5 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { createMindSpaceLocalServerAdapter } from './mindspace-local-server-adapter.mjs';
test('createMindSpaceLocalServerAdapter wires backfill and public html hydrator through local adapter seams', async () => {
const h5Root = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-local-server-adapter-'));
await fs.mkdir(path.join(h5Root, 'MindSpace', 'user-1', 'public', 'landing'), { recursive: true });
await fs.writeFile(
path.join(h5Root, 'MindSpace', 'user-1', 'public', 'landing', 'index.html'),
'<html><body>ok</body></html>',
'utf8',
);
const snapshots = new Map([
[
'session-1',
{
session: { name: 'Published from chat' },
messages: [
{
role: 'assistant',
content: [
{
type: 'text',
text: 'Preview ready at public/landing/index.html',
},
],
},
],
},
],
]);
const pool = {
query: async () => {
throw new Error('not implemented in unit test');
},
};
const registered = [];
const adapter = createMindSpaceLocalServerAdapter({
pool,
h5Root,
env: {
MINDSPACE_STORAGE_ROOT: '/srv/mindspace-data',
H5_PUBLIC_BASE_URL: 'https://example.com',
},
maxFileBytes: 8192,
publicPageLimit: 8,
resolveUserIdForAgentSession: async () => 'user-1',
resolveSessionSnapshot: async (sessionId) => snapshots.get(sessionId) ?? null,
registerPublicHtmlArtifactsForConversation: async (payload) => {
registered.push(payload);
return payload.artifactRefs;
},
logger: { log() {}, warn() {}, error() {} },
});
const hydrated = await adapter.serviceFacade.prepareConversationPackageRead({
user: { id: 'user-1' },
sessionId: 'session-1',
});
assert.ok(adapter.conversationPackageRegistry);
assert.equal(adapter.storageRoot, '/srv/mindspace-data');
assert.ok(Array.isArray(hydrated));
assert.equal(hydrated.length, 1);
assert.equal(registered.length, 1);
assert.equal(registered[0].publishDir, path.join(h5Root, 'MindSpace', 'user-1'));
assert.equal(registered[0].title, 'Published from chat');
assert.equal(registered[0].artifactRefs[0].relativePath, 'public/landing/index.html');
});
test('createMindSpaceLocalServerAdapter starts publication cleanup and agent worker timers through adapter helpers', async () => {
const intervals = [];
const loggerCalls = [];
const adapter = createMindSpaceLocalServerAdapter({
pool: {
query: async () => {
throw new Error('not implemented in unit test');
},
},
h5Root: '/tmp/h5',
env: {
MINDSPACE_STORAGE_ROOT: '/srv/mindspace-data',
H5_PUBLIC_BASE_URL: 'https://example.com',
},
maxFileBytes: 8192,
publicPageLimit: 8,
resolveUserIdForAgentSession: async () => 'user-1',
logger: {
log: (...args) => loggerCalls.push(['log', ...args]),
warn: (...args) => loggerCalls.push(['warn', ...args]),
error: (...args) => loggerCalls.push(['error', ...args]),
},
setIntervalFn: (fn, ms) => {
const timer = { fn, ms, unref() {} };
intervals.push(timer);
return timer;
},
});
adapter.publicationService.cleanupExpiredUnconfirmedPublications = async () => ({ cleaned: 2 });
adapter.agentJobService.claimNextJob = async () => null;
adapter.agentJobService.reapStaleJobs = async () => 1;
const publicationTimer = adapter.startPublicationCleanup();
const workerTimers = adapter.startAgentWorker({
enabled: true,
concurrency: 2,
pollMs: 1500,
staleMs: 90_000,
runner: {
runJob: async () => {},
},
});
assert.equal(publicationTimer.ms, 60_000);
assert.equal(workerTimers.workerTimer.ms, 1500);
assert.equal(workerTimers.reaperTimer.ms, 60_000);
await publicationTimer.fn();
await workerTimers.workerTimer.fn();
await workerTimers.reaperTimer.fn();
assert.equal(intervals.length, 3);
assert.ok(
loggerCalls.some(
([level, message]) =>
level === 'log' && String(message).includes('Auto-privatized 2 expired unconfirmed publications'),
),
);
assert.ok(
loggerCalls.some(
([level, message]) =>
level === 'log' && String(message).includes('Agent job worker enabled (concurrency=2, poll=1500ms)'),
),
);
assert.ok(
loggerCalls.some(
([level, message]) =>
level === 'warn' && String(message).includes('Agent worker reaped 1 stale running job(s)'),
),
);
});
test('createMindSpaceLocalServerAdapter startBackgroundJobs aggregates startup side effects', async () => {
const intervals = [];
const workspaceCalls = [];
const adapter = createMindSpaceLocalServerAdapter({
pool: {
query: async () => {
throw new Error('not implemented in unit test');
},
},
h5Root: '/tmp/h5',
env: {
MINDSPACE_STORAGE_ROOT: '/srv/mindspace-data',
H5_PUBLIC_BASE_URL: 'https://example.com',
},
maxFileBytes: 8192,
publicPageLimit: 8,
resolveUserIdForAgentSession: async () => 'user-1',
logger: { log() {}, warn() {}, error() {} },
setIntervalFn: (fn, ms) => {
const timer = { fn, ms, unref() {} };
intervals.push(timer);
return timer;
},
});
adapter.publicationService.cleanupExpiredUnconfirmedPublications = async () => ({ cleaned: 0 });
adapter.agentJobService.claimNextJob = async () => null;
adapter.agentJobService.reapStaleJobs = async () => 0;
adapter.assetService.expireStaleUploads = async () => {};
const jobs = adapter.startBackgroundJobs({
publicationCleanupIntervalMs: 61_000,
agentWorker: {
enabled: true,
concurrency: 2,
pollMs: 1500,
staleMs: 90_000,
},
agentRunner: { runJob: async () => {} },
workspaceMaintenanceEnabled: true,
publishRoot: '/tmp/h5/MindSpace',
startWorkspaceThumbnailWatcher: (root) => workspaceCalls.push(['thumb', root]),
startWorkspaceAssetSyncWatcher: (payload) => workspaceCalls.push(['asset', payload.publishRoot]),
syncUserWorkspaceByDirKey: async () => {},
expireStaleUploadsIntervalMs: 30_000,
});
assert.equal(jobs.publicationCleanup.ms, 61_000);
assert.equal(jobs.agentWorker.workerTimer.ms, 1500);
assert.equal(jobs.workspaceMaintenance.expireStaleUploadsTimer.ms, 30_000);
assert.deepEqual(workspaceCalls, [
['thumb', '/tmp/h5/MindSpace'],
['asset', '/tmp/h5/MindSpace'],
]);
});