Files
OmniRoute/tests/unit/qoder-executor.test.mjs
T
2026-03-28 23:35:59 -03:00

151 lines
6.1 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import crypto from "node:crypto";
// ═══════════════════════════════════════════════════════════════
// QoderExecutor Unit Tests
// Tests for HMAC-SHA256 signature, headers, URL building
// Fixes: https://github.com/diegosouzapw/OmniRoute/issues/114
// ═══════════════════════════════════════════════════════════════
const { QoderExecutor } = await import("../../open-sse/executors/qoder.ts");
// ─── Constructor ──────────────────────────────────────────────
test("QoderExecutor: constructor sets provider to 'qoder'", () => {
const executor = new QoderExecutor();
assert.equal(executor.getProvider(), "qoder");
});
// ─── createQoderSignature ─────────────────────────────────────
test("QoderExecutor: createQoderSignature returns valid HMAC-SHA256 hex", () => {
const executor = new QoderExecutor();
const userAgent = "Qoder-Cli";
const sessionID = "session-test-123";
const timestamp = 1700000000000;
const apiKey = "test-api-key-secret";
const signature = executor.createQoderSignature(userAgent, sessionID, timestamp, apiKey);
// Verify it's a valid hex string (64 chars for SHA256)
assert.match(signature, /^[0-9a-f]{64}$/);
// Verify reproducibility — same inputs produce same signature
const signature2 = executor.createQoderSignature(userAgent, sessionID, timestamp, apiKey);
assert.equal(signature, signature2);
// Verify against manual HMAC computation
const payload = `${userAgent}:${sessionID}:${timestamp}`;
const expected = crypto.createHmac("sha256", apiKey).update(payload).digest("hex");
assert.equal(signature, expected);
});
test("QoderExecutor: createQoderSignature returns empty string when apiKey is empty", () => {
const executor = new QoderExecutor();
const result = executor.createQoderSignature("agent", "session", 123, "");
assert.equal(result, "");
});
test("QoderExecutor: createQoderSignature returns empty string when apiKey is null", () => {
const executor = new QoderExecutor();
const result = executor.createQoderSignature("agent", "session", 123, null);
assert.equal(result, "");
});
// ─── buildHeaders ─────────────────────────────────────────────
test("QoderExecutor: buildHeaders includes qoder-specific headers", () => {
const executor = new QoderExecutor();
const credentials = { apiKey: "test-key-123" };
const headers = executor.buildHeaders(credentials, true);
// Must include required qoder headers
assert.ok(headers["session-id"], "Missing session-id header");
assert.ok(headers["x-qoder-timestamp"], "Missing x-qoder-timestamp header");
assert.ok(headers["x-qoder-signature"], "Missing x-qoder-signature header");
// session-id format
assert.ok(
headers["session-id"].startsWith("session-"),
"session-id should start with 'session-'"
);
// timestamp is a number string
assert.match(headers["x-qoder-timestamp"], /^\d+$/);
// signature is hex
assert.match(headers["x-qoder-signature"], /^[0-9a-f]{64}$/);
// Authorization
assert.equal(headers["Authorization"], "Bearer test-key-123");
// Content-Type
assert.equal(headers["Content-Type"], "application/json");
// Streaming Accept
assert.equal(headers["Accept"], "text/event-stream");
});
test("QoderExecutor: buildHeaders omits Accept header when stream is false", () => {
const executor = new QoderExecutor();
const credentials = { apiKey: "test-key" };
const headers = executor.buildHeaders(credentials, false);
assert.equal(headers["Accept"], undefined);
});
test("QoderExecutor: buildHeaders uses accessToken when apiKey is missing", () => {
const executor = new QoderExecutor();
const credentials = { accessToken: "oauth-token-123" };
const headers = executor.buildHeaders(credentials);
assert.equal(headers["Authorization"], "Bearer oauth-token-123");
// Signature should still be generated using the accessToken
assert.ok(headers["x-qoder-signature"].length > 0);
});
test("QoderExecutor: buildHeaders generates unique session IDs per call", () => {
const executor = new QoderExecutor();
const credentials = { apiKey: "key" };
const headers1 = executor.buildHeaders(credentials);
const headers2 = executor.buildHeaders(credentials);
assert.notEqual(headers1["session-id"], headers2["session-id"]);
});
// ─── buildUrl ─────────────────────────────────────────────────
test("QoderExecutor: buildUrl returns config baseUrl", () => {
const executor = new QoderExecutor();
const url = executor.buildUrl("qwen3-coder-plus", true);
assert.equal(url, "https://apis.qoder.cn/v1/chat/completions");
});
// ─── transformRequest ─────────────────────────────────────────
test("QoderExecutor: transformRequest passes body through unchanged", () => {
const executor = new QoderExecutor();
const body = {
model: "deepseek-r1",
messages: [{ role: "user", content: "Hello" }],
stream: true,
};
const result = executor.transformRequest("deepseek-r1", body, true, {});
assert.deepEqual(result, body);
});
// ─── Integration: executor registry ───────────────────────────
test("QoderExecutor: getExecutor('qoder') returns QoderExecutor instance", async () => {
const { getExecutor } = await import("../../open-sse/executors/index.ts");
const executor = getExecutor("qoder");
assert.ok(executor instanceof QoderExecutor, "Should return QoderExecutor instance");
});