166 lines
4.9 KiB
TypeScript
166 lines
4.9 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
import Database from 'better-sqlite3';
|
|
import { runMigrations } from '../src/database/client.js';
|
|
import { RedditUsersRepository } from '../src/database/repositories.js';
|
|
import { ChatService } from '../src/services/chat/service.js';
|
|
import { ChatCache } from '../src/services/chat/cache.js';
|
|
import { MatrixAuthError, MatrixClient } from '../src/services/chat/matrix/client.js';
|
|
import { createMockLogger } from './mockLogger.js';
|
|
import { authenticatedAccountAuth } from './testUtils.js';
|
|
|
|
const creds = {
|
|
homeserver: 'https://matrix.example.test',
|
|
accessToken: 'tok',
|
|
userId: '@me:example.test',
|
|
capturedAt: Date.now(),
|
|
};
|
|
|
|
function buildMatrix() {
|
|
const logger = createMockLogger();
|
|
const fetchImpl = (() =>
|
|
Promise.resolve(
|
|
new Response('{}', { status: 200, headers: { 'content-type': 'application/json' } }),
|
|
)) as typeof fetch;
|
|
return new MatrixClient(creds, logger, { minRequestIntervalMs: 0, fetchImpl });
|
|
}
|
|
|
|
describe('ChatService username persistence', () => {
|
|
function tmpCache(logger: Parameters<typeof ChatCache>[0]) {
|
|
return new ChatCache(
|
|
logger,
|
|
path.join(os.tmpdir(), `chat-cache-${Date.now()}-${Math.random()}.json`),
|
|
);
|
|
}
|
|
|
|
it('seeds the in-memory cache from RedditUsersRepository on construction', () => {
|
|
const db = new Database(':memory:');
|
|
runMigrations(db);
|
|
const repo = new RedditUsersRepository(db);
|
|
repo.upsert({ t2Id: 't2_abc', username: 'alice' });
|
|
|
|
const logger = createMockLogger();
|
|
const svc = new ChatService(
|
|
buildMatrix(),
|
|
logger,
|
|
undefined, undefined, undefined, undefined,
|
|
tmpCache(logger),
|
|
undefined,
|
|
repo,
|
|
authenticatedAccountAuth,
|
|
);
|
|
|
|
expect(svc.resolveSenderDisplay('@t2_abc:reddit.com')).toBe('alice');
|
|
expect(svc.resolveSenderDisplay('t2_abc')).toBe('alice');
|
|
db.close();
|
|
});
|
|
|
|
it('falls back to t2 form when no mapping is known', () => {
|
|
const logger = createMockLogger();
|
|
const svc = new ChatService(
|
|
buildMatrix(),
|
|
logger,
|
|
undefined, undefined, undefined, undefined,
|
|
tmpCache(logger),
|
|
undefined,
|
|
undefined,
|
|
authenticatedAccountAuth,
|
|
);
|
|
expect(svc.resolveSenderDisplay('@t2_unknown:reddit.com')).toBe('t2_unknown');
|
|
});
|
|
|
|
it('upgrades cached chats with t2_ usernames from the db map on loadCache', async () => {
|
|
const db = new Database(':memory:');
|
|
runMigrations(db);
|
|
const repo = new RedditUsersRepository(db);
|
|
repo.upsert({ t2Id: 't2_abc', username: 'alice' });
|
|
|
|
const logger = createMockLogger();
|
|
const tmpFile = path.join(os.tmpdir(), `chat-cache-${Date.now()}-${Math.random()}.json`);
|
|
const cache = new ChatCache(logger, tmpFile);
|
|
cache.set([
|
|
{
|
|
username: 't2_abc',
|
|
roomName: 't2_abc',
|
|
lastMessageTime: Date.now(),
|
|
lastMessageSnippet: 'hi',
|
|
isFromUs: false,
|
|
unread: false,
|
|
url: 'https://chat.reddit.com/room/x',
|
|
roomId: '!room:reddit.com',
|
|
messages: [],
|
|
},
|
|
]);
|
|
|
|
const svc = new ChatService(
|
|
buildMatrix(),
|
|
logger,
|
|
undefined, undefined, undefined, undefined,
|
|
cache,
|
|
undefined,
|
|
repo,
|
|
authenticatedAccountAuth,
|
|
);
|
|
|
|
await svc.loadCache();
|
|
const chats = await svc.getCachedConversations();
|
|
expect(chats[0].username).toBe('alice');
|
|
expect(chats[0].roomName).toBe('alice');
|
|
db.close();
|
|
});
|
|
|
|
it('aborts username enrichment after one failed token refresh', async () => {
|
|
const logger = createMockLogger();
|
|
const cache = tmpCache(logger);
|
|
cache.set([
|
|
{
|
|
username: 'unknown',
|
|
roomName: 'unknown',
|
|
lastMessageTime: Date.now(),
|
|
lastMessageSnippet: '',
|
|
isFromUs: false,
|
|
unread: false,
|
|
roomId: '!first:reddit.com',
|
|
messages: [],
|
|
},
|
|
{
|
|
username: 'unknown',
|
|
roomName: 'unknown',
|
|
lastMessageTime: Date.now(),
|
|
lastMessageSnippet: '',
|
|
isFromUs: false,
|
|
unread: false,
|
|
roomId: '!second:reddit.com',
|
|
messages: [],
|
|
},
|
|
]);
|
|
|
|
const matrix = buildMatrix();
|
|
matrix.getRoomFullState = vi.fn().mockRejectedValue(new MatrixAuthError('expired'));
|
|
matrix.getRoomMessages = vi.fn().mockRejectedValue(new MatrixAuthError('expired'));
|
|
const tokenRefresher = {
|
|
refresh: vi.fn().mockRejectedValue(new Error('interactive login required')),
|
|
};
|
|
const usernameResolver = { resolveT2Ids: vi.fn().mockResolvedValue({}) };
|
|
const svc = new ChatService(
|
|
matrix,
|
|
logger,
|
|
undefined,
|
|
undefined,
|
|
tokenRefresher,
|
|
usernameResolver,
|
|
cache,
|
|
undefined,
|
|
undefined,
|
|
authenticatedAccountAuth,
|
|
);
|
|
|
|
await svc.enrichUsernames();
|
|
|
|
expect(tokenRefresher.refresh).toHaveBeenCalledOnce();
|
|
expect(matrix.getRoomFullState).toHaveBeenCalledOnce();
|
|
expect(matrix.getRoomMessages).not.toHaveBeenCalled();
|
|
});
|
|
});
|