File size: 2,883 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
95
96
97
98
99
100
101
102
/**
 * Test harness for MitmHandlerBase subclasses.
 *
 * Mocks `globalThis.fetch` so handlers exercise their full intercept() path
 * (router round-trip + SSE pipe) without touching the network. Returns the
 * captured payload, response chunks written to the fake ServerResponse, and
 * the final status code.
 */
import type { IncomingMessage, ServerResponse } from "node:http";
import { Readable } from "node:stream";
import type { MitmHandlerBase } from "../../src/mitm/handlers/base.ts";

export interface HarnessResult {
  fetchCalled: boolean;
  fetchUrl: string | null;
  fetchHeaders: Record<string, string>;
  fetchBody: string;
  status: number;
  responseChunks: string[];
}

function fakeReq(
  headers: Record<string, string> = {},
  url = "/v1/chat/completions"
): IncomingMessage {
  return {
    method: "POST",
    url,
    headers: {
      host: "api.example.com",
      "user-agent": "ut",
      ...headers,
    },
  } as unknown as IncomingMessage;
}

function fakeRes(): { res: ServerResponse; out: HarnessResult } {
  const out: HarnessResult = {
    fetchCalled: false,
    fetchUrl: null,
    fetchHeaders: {},
    fetchBody: "",
    status: 0,
    responseChunks: [],
  };
  let headersSent = false;
  const res = {
    get headersSent() {
      return headersSent;
    },
    writeHead(s: number) {
      out.status = s;
      headersSent = true;
    },
    write(c: Buffer | string) {
      out.responseChunks.push(typeof c === "string" ? c : c.toString());
      return true;
    },
    end(c?: Buffer | string) {
      if (c) out.responseChunks.push(typeof c === "string" ? c : c.toString());
    },
  } as unknown as ServerResponse;
  return { res, out };
}

export async function runHandler(
  handler: MitmHandlerBase,
  body: unknown,
  mappedModel: string,
  opts: {
    upstreamStatus?: number;
    upstreamBody?: string;
    headers?: Record<string, string>;
    url?: string;
  } = {}
): Promise<HarnessResult> {
  const { res, out } = fakeRes();
  const req = fakeReq(opts.headers, opts.url);
  const buf = Buffer.from(typeof body === "string" ? body : JSON.stringify(body));

  const originalFetch = globalThis.fetch;
  globalThis.fetch = (async (url: string, init: RequestInit) => {
    out.fetchCalled = true;
    out.fetchUrl = String(url);
    out.fetchHeaders = (init?.headers ?? {}) as Record<string, string>;
    out.fetchBody = typeof init?.body === "string" ? init.body : "";
    const upstreamBody = opts.upstreamBody ?? "data: hello\n\n";
    const status = opts.upstreamStatus ?? 200;
    const stream = Readable.toWeb(
      Readable.from(Buffer.from(upstreamBody))
    ) as unknown as ReadableStream<Uint8Array>;
    return new Response(stream, { status });
  }) as unknown as typeof fetch;

  try {
    await handler.intercept(req, res, buf, mappedModel);
  } finally {
    globalThis.fetch = originalFetch;
  }
  return out;
}