This repository has no description
Something went wrong. Try again.
4.3 kB · 149 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150// SPDX-License-Identifier: AGPL-3.0-or-later
import type {UserPartialResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';import type {INatsConnectionManager} from '@pkgs/nats/src/INatsConnectionManager';import {type NatsConnection, StringCodec} from 'nats';import {describe, expect, it} from 'vitest';import {createUserID} from '../BrandedTypes';import {NatsUsersServiceClient} from './UsersServiceClient';
interface FakeRequest { subject: string; body: Record<string, unknown>; timeout: number | undefined;}
class FakeNatsConnectionManager implements INatsConnectionManager { private readonly codec = StringCodec(); private closed = true; private readonly responses: Array<unknown>; readonly requests: Array<FakeRequest> = []; connectCalls = 0; drained = false;
constructor(responses: Array<unknown>) { this.responses = [...responses]; }
async connect(): Promise<void> { this.connectCalls += 1; this.closed = false; }
getConnection(): NatsConnection { if (this.closed) { throw new Error('not connected'); } return { request: async (subject: string, data: Uint8Array, options?: {timeout?: number}) => { this.requests.push({ subject, body: JSON.parse(this.codec.decode(data)) as Record<string, unknown>, timeout: options?.timeout, }); const response = this.responses.shift(); if (response instanceof Error) { throw response; } return { data: this.codec.encode(JSON.stringify(response)), }; }, } as unknown as NatsConnection; }
async drain(): Promise<void> { this.drained = true; this.closed = true; }
isClosed(): boolean { return this.closed; }}
describe('NatsUsersServiceClient', () => { it('requests API user partials with string snowflakes and maps the response by id', async () => { const userId = createUserID(9223372036854775807n); const partial: UserPartialResponse = { id: userId.toString(), username: 'Ada', discriminator: '0007', global_name: 'Ada Lovelace', avatar: 'avatar_hash', avatar_color: 0x336699, flags: 1, }; const manager = new FakeNatsConnectionManager([{FoundApiPartials: [partial]}]); const client = new NatsUsersServiceClient(manager, 321, 'svc.users.test');
const result = await client.getUserPartialResponses([userId]);
expect(manager.connectCalls).toBe(1); expect(manager.requests).toEqual([ { subject: 'svc.users.test', body: { op: 'GetApiPartialsByIds', user_ids: ['9223372036854775807'], }, timeout: 321, }, ]); expect(result.get(userId)).toEqual(partial); });
it('sends invalidation requests with a string user id', async () => { const userId = createUserID(9007199254740993n); const manager = new FakeNatsConnectionManager(['Invalidated']); const client = new NatsUsersServiceClient(manager, 250, 'svc.users.test');
await client.invalidateUserCache(userId);
expect(manager.requests).toEqual([ { subject: 'svc.users.test', body: { op: 'Invalidate', user_id: '9007199254740993', }, timeout: 250, }, ]); });
it('coalesces concurrent overlapping partial lookups without retaining a completed cache entry', async () => { const userId = createUserID(9223372036854775807n); const secondUserId = createUserID(9223372036854775806n); const partial: UserPartialResponse = { id: userId.toString(), username: 'Cached', discriminator: '0001', global_name: null, avatar: null, avatar_color: null, flags: 0, }; const secondPartial: UserPartialResponse = { ...partial, id: secondUserId.toString(), username: 'Second', }; const manager = new FakeNatsConnectionManager([ {FoundApiPartials: [secondPartial, partial]}, {FoundApiPartials: [partial]}, ]); const client = new NatsUsersServiceClient(manager, 250, 'svc.users.test', 100);
const [first, second] = await Promise.all([ client.getUserPartialResponses([secondUserId, userId, userId]), client.getUserPartialResponses([userId]), ]); await client.getUserPartialResponses([userId]);
expect(manager.requests).toHaveLength(2); expect(first.get(userId)).toEqual(partial); expect(first.get(secondUserId)).toEqual(secondPartial); expect(second.get(userId)).toEqual(partial); });});