82 lines
3.4 KiB
TypeScript
82 lines
3.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "bun:test";
|
|
|
|
import { createUserBillingClient } from "./user-billing-client.ts";
|
|
|
|
test("user-billing login retries transient dependency failures only", async () => {
|
|
const calls = [];
|
|
const client = createUserBillingClient({
|
|
env: {
|
|
HWLAB_USER_BILLING_URL: "http://user-billing.test",
|
|
HWLAB_USER_BILLING_LOGIN_RETRY_ATTEMPTS: "2",
|
|
HWLAB_USER_BILLING_LOGIN_RETRY_DELAY_MS: "1"
|
|
},
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), method: init?.method });
|
|
if (calls.length === 1) throw new Error("The operation timed out.");
|
|
return new Response(JSON.stringify({ token: "hws_retry_ok", tokenType: "Bearer" }), { status: 200, headers: { "content-type": "application/json" } });
|
|
}
|
|
});
|
|
|
|
const result = await client.login({ username: "admin", password: "redacted" });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.retryCount, 1);
|
|
assert.equal(result.transientObserved, true);
|
|
assert.equal(calls.length, 2);
|
|
|
|
const authFailures = [];
|
|
const invalidClient = createUserBillingClient({
|
|
env: {
|
|
HWLAB_USER_BILLING_URL: "http://user-billing.test",
|
|
HWLAB_USER_BILLING_LOGIN_RETRY_ATTEMPTS: "2",
|
|
HWLAB_USER_BILLING_LOGIN_RETRY_DELAY_MS: "1"
|
|
},
|
|
fetchImpl: async (url, init) => {
|
|
authFailures.push({ url: String(url), method: init?.method });
|
|
return new Response(JSON.stringify({ error: { code: "invalid_credentials", message: "invalid email, username or password" } }), { status: 401, headers: { "content-type": "application/json" } });
|
|
}
|
|
});
|
|
|
|
const invalid = await invalidClient.login({ username: "admin", password: "wrong" });
|
|
assert.equal(invalid.ok, false);
|
|
assert.equal(invalid.status, 401);
|
|
assert.equal(invalid.error.code, "invalid_credentials");
|
|
assert.equal(authFailures.length, 1);
|
|
|
|
const unexpectedCalls = [];
|
|
const unconfiguredClient = createUserBillingClient({
|
|
env: {
|
|
HWLAB_USER_BILLING_LOGIN_RETRY_ATTEMPTS: "2",
|
|
HWLAB_USER_BILLING_LOGIN_RETRY_DELAY_MS: "1"
|
|
},
|
|
fetchImpl: async (url, init) => {
|
|
unexpectedCalls.push({ url: String(url), method: init?.method });
|
|
throw new Error("fetch should not run without a user-billing URL");
|
|
}
|
|
});
|
|
|
|
const unconfigured = await unconfiguredClient.login({ username: "admin", password: "redacted" });
|
|
assert.equal(unconfigured.ok, false);
|
|
assert.equal(unconfigured.status, 503);
|
|
assert.equal(unconfigured.error.code, "user_billing_not_configured");
|
|
assert.equal(unconfigured.retryable, undefined);
|
|
assert.equal(unexpectedCalls.length, 0);
|
|
});
|
|
|
|
test("user-billing login propagates traceparent to dependency requests", async () => {
|
|
const traceparent = "00-11111111111111111111111111111111-2222222222222222-01";
|
|
const calls = [];
|
|
const client = createUserBillingClient({
|
|
env: { HWLAB_USER_BILLING_URL: "http://user-billing.test" },
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), traceparent: init?.headers?.traceparent });
|
|
return new Response(JSON.stringify({ token: "hws_trace_ok", tokenType: "Bearer" }), { status: 200, headers: { "content-type": "application/json" } });
|
|
}
|
|
});
|
|
|
|
const result = await client.login({ username: "admin", password: "redacted" }, { traceparent });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(calls.length, 1);
|
|
assert.equal(calls[0].traceparent, traceparent);
|
|
});
|