feat: persist settings and enrich conversation context

This commit is contained in:
jjbrunton committed 2026-08-08 18:18:28 +01:00
1 parent a36723a325
commit 2c100d72b6
84 files changed
+2248 -644

No files matched your search

+61 -2
View File
@@ -1,5 +1,9 @@
import { describe, it, expect } from 'vitest';
import { MatrixClient, MatrixRateLimitError } from '../src/services/chat/matrix/client.js';
import { describe, it, expect, vi } from 'vitest';
import {
MatrixAuthError,
MatrixClient,
MatrixRateLimitError,
} from '../src/services/chat/matrix/client.js';
import { createMockLogger } from './mockLogger.js';
const creds = {
@@ -10,6 +14,31 @@ const creds = {
};
describe('MatrixClient HTTP timeout', () => {
it('loads an encoded Matrix user profile with bearer authentication', async () => {
let requestedUrl = '';
let authorization = '';
const fetchImpl = ((url: string | URL | Request, init?: RequestInit) => {
requestedUrl = String(url);
authorization = new Headers(init?.headers).get('Authorization') ?? '';
return Promise.resolve(new Response(JSON.stringify({ displayname: 'MilkyBabe_10' }), {
status: 200,
headers: { 'content-type': 'application/json' },
}));
}) as typeof fetch;
const client = new MatrixClient(creds, createMockLogger(), {
minRequestIntervalMs: 0,
fetchImpl,
});
await expect(client.getUserProfile('@t2_26q364ua7n:reddit.com')).resolves.toEqual({
displayname: 'MilkyBabe_10',
});
expect(requestedUrl).toBe(
'https://matrix.example.test/_matrix/client/v3/profile/%40t2_26q364ua7n%3Areddit.com',
);
expect(authorization).toBe('Bearer tok');
});
it('aborts a hanging fetch and surfaces a timeout error rather than blocking forever', async () => {
// A fetch impl that never resolves on its own — only the AbortSignal can
// end it. If the client lacked an AbortSignal, this test would hang.
@@ -64,6 +93,36 @@ describe('MatrixClient HTTP timeout', () => {
}, 10_000);
});
describe('MatrixClient authentication classification', () => {
it('does not classify a room-level 403 as an expired account token', async () => {
const client = new MatrixClient(creds, createMockLogger(), {
minRequestIntervalMs: 0,
fetchImpl: vi.fn().mockResolvedValue(new Response('', { status: 403 })),
});
const error = await client.getRoomFullState('!left-room:example.test').catch((cause) => cause);
expect(error).toBeInstanceOf(Error);
expect(error).not.toBeInstanceOf(MatrixAuthError);
expect((error as Error).message).toMatch(/403/);
});
it('still classifies an explicit unknown-token 403 as authentication failure', async () => {
const client = new MatrixClient(creds, createMockLogger(), {
minRequestIntervalMs: 0,
fetchImpl: vi.fn().mockResolvedValue(new Response(JSON.stringify({
errcode: 'M_UNKNOWN_TOKEN',
error: 'Access token has expired',
}), {
status: 403,
headers: { 'content-type': 'application/json' },
})),
});
await expect(client.whoami()).rejects.toBeInstanceOf(MatrixAuthError);
});
});
describe('MatrixClient 429 handling', () => {
it('throws MatrixRateLimitError on a long retry_after rather than sleeping it out', async () => {
const fetchImpl = (() =>