Skip to content

Commit a62ea66

Browse files
committed
test(services): add comprehensive tests for untested service modules
Add tests for sessionService, guildService, seedService, and initResourceDownloadList. Expand coverage for encryptionHelpers (encryptAndSend) and questService (getBlockHashsFromQuestHash). Key coverage gains: seedService 0→100%, initResourceDownloadList 0→97%, encryptionHelpers 15→89%, questService 22→89%. Overall 56%→63%.
1 parent a7dd498 commit a62ea66

6 files changed

Lines changed: 1537 additions & 19 deletions

File tree

src/services/crypto/encryptionHelpers.spec.ts

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,61 @@
1-
import { describe, it, expect, beforeEach } from 'vitest';
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
3+
vi.mock('../../model/session', () => ({
4+
default: {
5+
updateOne: vi.fn().mockResolvedValue({}),
6+
deleteOne: vi.fn().mockResolvedValue({}),
7+
deleteMany: vi.fn().mockResolvedValue({}),
8+
find: vi.fn().mockResolvedValue([]),
9+
},
10+
}));
11+
212
import {
313
clearAllSessions,
414
decryptAndParse,
15+
encryptAndSend,
516
getSessionCount,
617
getAllActiveSessions,
718
getSessionStats,
819
getPerformanceStats,
920
verifySessionToken,
1021
generateUniqueId,
1122
findSessionsByUser,
23+
getSessionInfo,
1224
} from './encryptionHelpers.js';
1325
import { EncryptionService } from './encryptionService.js';
26+
import type { Response, Request } from 'express';
27+
28+
function makeReq(overrides: Record<string, unknown> = {}): Request {
29+
return {
30+
body: {},
31+
query: {},
32+
ip: '127.0.0.1',
33+
socket: { remoteAddress: '127.0.0.1' },
34+
get: (header: string) => (header === 'User-Agent' ? 'TestAgent/1.0' : undefined),
35+
...overrides,
36+
} as unknown as Request;
37+
}
38+
39+
function makeRes(): Response & { _status: number; _data: Buffer | null; _headers: Record<string, string> } {
40+
const res = {
41+
_status: 0,
42+
_data: null as Buffer | null,
43+
_headers: {} as Record<string, string>,
44+
status(code: number) {
45+
res._status = code;
46+
return res;
47+
},
48+
header(name: string, value: string) {
49+
res._headers[name] = value;
50+
return res;
51+
},
52+
send(data: Buffer) {
53+
res._data = data;
54+
return res;
55+
},
56+
};
57+
return res as unknown as Response & { _status: number; _data: Buffer | null; _headers: Record<string, string> };
58+
}
1459

1560
describe('encryptionHelpers - session management', () => {
1661
beforeEach(() => {
@@ -82,3 +127,113 @@ describe('decryptAndParse', () => {
82127
expect(() => decryptAndParse(Buffer.alloc(0))).toThrow();
83128
});
84129
});
130+
131+
describe('encryptAndSend', () => {
132+
const encryptionService = new EncryptionService();
133+
134+
beforeEach(() => {
135+
clearAllSessions();
136+
});
137+
138+
it('sends encrypted response with correct status and content type', () => {
139+
const req = makeReq({ body: { user_id: 'u1' } });
140+
const res = makeRes();
141+
142+
encryptAndSend({ result: 'ok' }, res, req);
143+
144+
expect(res._status).toBe(200);
145+
expect(res._headers['Content-Type']).toBe('application/octet-stream');
146+
expect(res._data).toBeInstanceOf(Buffer);
147+
});
148+
149+
it('response decrypts to valid JSON with protocol fields', () => {
150+
const req = makeReq({ body: { user_id: 'u1' } });
151+
const res = makeRes();
152+
153+
encryptAndSend({ result: 'ok' }, res, req);
154+
155+
const decrypted = JSON.parse(
156+
encryptionService.decrypt(res._data!).replace(/\0+$/, '').trim(),
157+
) as Record<string, unknown>;
158+
expect(decrypted.result).toBe('ok');
159+
expect(decrypted.res_ver).toBe(282);
160+
expect(decrypted.banner_ver).toBe(91);
161+
expect(decrypted.app_ver).toBe('09.03.06');
162+
expect(decrypted.session_id).toEqual(expect.any(String));
163+
expect(decrypted.error_code).toBe(0);
164+
expect(decrypted.now_time).toEqual(expect.any(Number));
165+
});
166+
167+
it('includes custom error codes in response', () => {
168+
const req = makeReq({ body: { user_id: 'u1' } });
169+
const res = makeRes();
170+
171+
encryptAndSend({}, res, req, 4004, 2, 'not_auth');
172+
173+
const decrypted = JSON.parse(
174+
encryptionService.decrypt(res._data!).replace(/\0+$/, '').trim(),
175+
) as Record<string, unknown>;
176+
expect(decrypted.error_code).toBe(4004);
177+
expect(decrypted.error_category).toBe(2);
178+
expect(decrypted.error_detail).toBe('not_auth');
179+
});
180+
181+
it('sends with custom status code', () => {
182+
const req = makeReq({ body: { user_id: 'u1' } });
183+
const res = makeRes();
184+
185+
encryptAndSend({}, res, req, 0, 0, '', 400);
186+
187+
expect(res._status).toBe(400);
188+
});
189+
190+
it('creates session for userId path', () => {
191+
const req = makeReq({ body: { user_id: 'user-42' } });
192+
const res = makeRes();
193+
194+
encryptAndSend({}, res, req);
195+
196+
const session = getSessionInfo('user_user-42');
197+
expect(session).toBeDefined();
198+
expect(session!.account_id).toBe('user-42');
199+
});
200+
201+
it('reuses existing session for same userId', () => {
202+
const req = makeReq({ body: { user_id: 'user-42' } });
203+
const res1 = makeRes();
204+
const res2 = makeRes();
205+
206+
encryptAndSend({}, res1, req);
207+
const firstSession = getSessionInfo('user_user-42');
208+
const firstToken = firstSession!.session_token;
209+
210+
encryptAndSend({}, res2, req);
211+
const secondSession = getSessionInfo('user_user-42');
212+
expect(secondSession!.session_token).toBe(firstToken);
213+
});
214+
215+
it('creates session for clientSessionToken path (session_id in body)', () => {
216+
const req = makeReq({ body: { session_id: 'client-tok-1' } });
217+
const res = makeRes();
218+
219+
encryptAndSend({}, res, req);
220+
221+
// Should create a session keyed by client token
222+
const decrypted = JSON.parse(
223+
encryptionService.decrypt(res._data!).replace(/\0+$/, '').trim(),
224+
) as Record<string, unknown>;
225+
expect(decrypted.session_id).toEqual(expect.any(String));
226+
});
227+
228+
it('creates session for device fallback path (no userId, no session_id)', () => {
229+
const req = makeReq();
230+
const res = makeRes();
231+
232+
encryptAndSend({}, res, req);
233+
234+
const all = getAllActiveSessions();
235+
expect(all.length).toBeGreaterThanOrEqual(1);
236+
const deviceSession = all.find((s) => s.key.startsWith('device_'));
237+
expect(deviceSession).toBeDefined();
238+
});
239+
});

0 commit comments

Comments
 (0)