File size: 3,267 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { Writable } from 'node:stream';

import { pino, type Logger } from 'pino';
import { afterAll, beforeAll, assert, describe, expect, it } from 'vitest';

import { extractEnvelopeCode } from '../src/requestLogging';
import { type RunningServer, startServer } from '../src/start';
import { TEST_HOST_IDENTITY } from './helpers/hostIdentity';

function captureLogger(): { logger: Logger; lines: string[] } {
  const lines: string[] = [];
  const stream = new Writable({
    write(chunk, _enc, cb) {
      lines.push(chunk.toString());
      cb();
    },
  });
  return { logger: pino({ level: 'info' }, stream), lines };
}

function parseEntries(lines: string[]): Record<string, unknown>[] {
  return lines
    .map((line) => {
      try {
        return JSON.parse(line) as Record<string, unknown>;
      } catch {
        return null;
      }
    })
    .filter((entry): entry is Record<string, unknown> => entry !== null);
}

describe('requestLogging', () => {
  let server: RunningServer | undefined;
  let home: string | undefined;
  let lines: string[];

  beforeAll(async () => {
    home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-request-log-'));
    const captured = captureLogger();
    lines = captured.lines;
    server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logger: captured.logger });
  });

  afterAll(async () => {
    if (server !== undefined) {
      await server.close();
      server = undefined;
    }
    if (home !== undefined) {
      await rm(home, { recursive: true, force: true });
      home = undefined;
    }
  });

  it('logs the envelope code instead of the HTTP status code', async () => {
    const res = await fetch(`http://127.0.0.1:${String(server!.port)}/api/v1/healthz`);
    expect(res.status).toBe(200);
    expect(((await res.json()) as { code: number }).code).toBe(0);

    await new Promise((resolve) => setImmediate(resolve));

    const completed = parseEntries(lines).filter((entry) => entry['msg'] === 'request completed');
    expect(completed.length).toBeGreaterThanOrEqual(1);
    const entry = completed[completed.length - 1];
    assert(entry !== undefined);

    expect(entry['code']).toBe(0);
    expect(entry).not.toHaveProperty('statusCode');
    expect(entry['res']).toBeUndefined();
    expect(entry['req']).toMatchObject({ method: 'GET', url: '/api/v1/healthz' });
    expect(typeof entry['responseTime']).toBe('number');
  });
});

describe('extractEnvelopeCode', () => {
  it('extracts a leading code from an envelope body', () => {
    expect(extractEnvelopeCode('{"code":0,"msg":"success","data":null,"request_id":"r"}')).toBe(0);
    expect(
      extractEnvelopeCode('{"code":40001,"msg":"validation.failed","data":null,"request_id":"r"}'),
    ).toBe(40001);
  });

  it('returns undefined for non-envelope or non-string payloads', () => {
    expect(extractEnvelopeCode(undefined)).toBeUndefined();
    expect(extractEnvelopeCode(Buffer.from('{"code":1}'))).toBeUndefined();
    expect(extractEnvelopeCode('<html/>')).toBeUndefined();
    expect(extractEnvelopeCode('{"msg":"no code"}')).toBeUndefined();
  });
});