Files
iios/packages/iios-messaging-ui/src/adapters/kernel-client.test.ts
T
maaz519 ade1d68015 feat: channels on the direct-IIOS path — kernel-client + KernelClientAdapter
- iios-kernel-client: RestClient.discoverThreads (GET /v1/threads/discover) +
  leaveThread (DELETE /v1/threads/:id/me); DiscoveredThread type
- KernelClientAdapter implements the four channel methods: browseChannels maps
  discovered public channels → ChannelSummary; createChannel → createThread
  (membership=channel, ADMIN, visibility/topic metadata); joinChannel →
  socket.openThread (governed public self-join); leaveChannel → rest.leaveThread
- adapter channel test over the fake transport (13 kernel-adapter tests); 57 green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 00:15:21 +05:30

154 lines
4.9 KiB
TypeScript

// Runs the shared adapter conformance suite against KernelClientAdapter — proving it satisfies
// the same contract as the mock, over the REAL MessageSocket facade. Only the lowest socket.io
// layer is faked (via kernel-client's own SocketLike seam), so the facade's wire mapping is
// exercised for real. No live IIOS required.
import { describe, it, expect } from 'vitest';
import { MessageSocket } from '@insignia/iios-kernel-client';
import type { Message as KernelMessage, SocketLike } from '@insignia/iios-kernel-client';
import { runAdapterConformance } from '../conformance';
import { KernelClientAdapter, type RestPort } from './kernel-client';
const ME = 'me';
const SEEDED = 'th_seed';
/** An in-memory stand-in for the /message socket.io namespace: stores messages, echoes 'message'. */
function makeFakeSocket(): SocketLike {
const threads = new Map<string, KernelMessage[]>([
[
SEEDED,
[
{
id: 'seed_1',
threadId: SEEDED,
senderActorId: 'actor_other',
senderId: 'pp_other',
senderName: 'Other',
content: 'seeded message',
createdAt: new Date(0).toISOString(),
},
],
],
]);
const handlers = new Map<string, Set<(...a: unknown[]) => void>>();
let seq = 1;
const fire = (event: string, payload: unknown): void => handlers.get(event)?.forEach((h) => h(payload));
return {
on(event, handler) {
if (!handlers.has(event)) handlers.set(event, new Set());
handlers.get(event)!.add(handler);
return undefined;
},
off(event, handler) {
handlers.get(event)?.delete(handler);
return undefined;
},
emit() {
return undefined; // typing/focus — no echo needed for conformance
},
async emitWithAck(event, payload) {
const p = (payload ?? {}) as {
threadId: string;
content?: string;
parentInteractionId?: string;
interactionId?: string;
type?: string;
value?: string;
};
if (event === 'open_thread') {
if (!threads.has(p.threadId)) threads.set(p.threadId, []);
return { threadId: p.threadId, status: 'OPEN', history: [...threads.get(p.threadId)!] };
}
if (event === 'send_message') {
const msg: KernelMessage = {
id: `m_${seq++}`,
threadId: p.threadId,
senderActorId: `actor_${ME}`,
senderId: ME,
senderName: ME,
content: p.content ?? '',
createdAt: new Date().toISOString(),
...(p.parentInteractionId ? { parentInteractionId: p.parentInteractionId } : {}),
};
if (!threads.has(p.threadId)) threads.set(p.threadId, []);
threads.get(p.threadId)!.push(msg);
fire('message', msg);
return msg;
}
if (event === 'read') return { ok: true };
if (event === 'annotate') {
fire('annotation', {
threadId: p.threadId,
interactionId: p.interactionId,
type: p.type,
value: p.value,
op: 'add',
users: [ME],
userId: ME,
});
return {};
}
return {};
},
connect() {
return undefined;
},
disconnect() {
return undefined;
},
connected: true,
};
}
function makeFakeRest(): RestPort {
let seq = 1;
return {
async listThreads() {
return [];
},
async createThread() {
return { threadId: `th_new_${seq++}` };
},
async addParticipant() {
/* governed server-side; a fake always allows */
},
async discoverThreads() {
return [
{
threadId: 'th_pub',
subject: 'general',
metadata: { membership: 'channel', visibility: 'public', topic: 'Company-wide' },
participantCount: 3,
joined: false,
},
];
},
async leaveThread(threadId) {
return { threadId, participantCount: 0 };
},
};
}
function makeAdapter(): KernelClientAdapter {
const socket = new MessageSocket({ serviceUrl: 'http://iios.test', token: 'tok', autoConnect: false }, makeFakeSocket());
return new KernelClientAdapter({ currentUserId: ME, socket, rest: makeFakeRest() });
}
runAdapterConformance({ makeAdapter, seededThreadId: SEEDED, openWith: ['pp_a'] });
describe('KernelClientAdapter channels', () => {
it('browse maps discovered public channels; create/join/leave delegate to the transport', async () => {
const adapter = makeAdapter();
const list = await adapter.browseChannels!();
expect(list[0]).toMatchObject({ threadId: 'th_pub', name: 'general', visibility: 'public', joined: false, memberCount: 3, topic: 'Company-wide' });
const { threadId } = await adapter.createChannel!({ name: 'design', topic: 'UI', visibility: 'public' });
expect(typeof threadId).toBe('string');
await expect(adapter.joinChannel!('th_pub')).resolves.toBeUndefined();
await expect(adapter.leaveChannel!('th_pub')).resolves.toBeUndefined();
});
});