diff --git a/src/ClientBuilder.ts b/src/ClientBuilder.ts index c98d571..aecfecd 100644 --- a/src/ClientBuilder.ts +++ b/src/ClientBuilder.ts @@ -179,9 +179,15 @@ export default class ClientBuilder { } buildSender(): Sender { - if (this.httpSender) return this.httpSender; - - const httpSender = new HttpSender(this.maxTimeout, this.proxy, this.debug); + if (this.httpSender !== undefined) { + const conflicts: string[] = []; + if (this.maxTimeout !== 10000) conflicts.push("withMaxTimeout()"); + if (this.proxy !== undefined) conflicts.push("withProxy()"); + if (this.debug !== undefined) conflicts.push("withDebug()"); + if (conflicts.length > 0) + throw new Error(`withSender() cannot be combined with: ${conflicts.join(", ")}. These options only apply to the built-in HTTP transport.`); + } + const httpSender = this.httpSender ?? new HttpSender(this.maxTimeout, this.proxy, this.debug); const statusCodeSender = new StatusCodeSender(httpSender); const signingSender = new SigningSender(statusCodeSender, this.signer); let agentSender = new AgentSender(signingSender); diff --git a/tests/test_ClientBuilder.ts b/tests/test_ClientBuilder.ts index 189c6cb..a6b7dbb 100644 --- a/tests/test_ClientBuilder.ts +++ b/tests/test_ClientBuilder.ts @@ -1,6 +1,8 @@ import { expect } from "chai"; import StaticCredentials from "../src/StaticCredentials.js"; import ClientBuilder from "../src/ClientBuilder.js"; +import Lookup from "../src/us_street/Lookup.js"; +import { Request as IRequest, Response as IResponse, Sender } from "../src/types.js"; describe("ClientBuilder", function () { const credentials = new StaticCredentials("test-id", "test-token"); @@ -21,4 +23,36 @@ describe("ClientBuilder", function () { "component-analysis,iana-timezone", ); }); + + it("throws when withSender() is combined with withMaxTimeout().", function () { + expect(() => + new ClientBuilder(credentials).withSender({ send: async () => ({ statusCode: 200, payload: [], error: null, headers: {} }) }).withMaxTimeout(5000).buildUsStreetApiClient() + ).to.throw("withSender() cannot be combined with: withMaxTimeout()"); + }); + + it("throws when withSender() is combined with withProxy().", function () { + expect(() => + new ClientBuilder(credentials).withSender({ send: async () => ({ statusCode: 200, payload: [], error: null, headers: {} }) }).withProxy({ host: "localhost", port: 8080 }).buildUsStreetApiClient() + ).to.throw("withSender() cannot be combined with: withProxy()"); + }); + + it("wraps a custom http sender with the full middleware chain (baseUrl and auth are set).", async function () { + let capturedRequest: IRequest | undefined; + const capturingSender: Sender = { + send(request: IRequest): Promise { + capturedRequest = request; + return Promise.resolve({ statusCode: 200, payload: [], error: null, headers: {} }); + }, + }; + + const client = new ClientBuilder(credentials).withSender(capturingSender).buildUsStreetApiClient(); + + const lookup = new Lookup(); + lookup.street = "1 Rosedale"; + await client.send(lookup); + + expect(capturedRequest!.baseUrl).to.include("us-street.api.smarty.com"); + expect(capturedRequest!.parameters["auth-id"]).to.equal("test-id"); + expect(capturedRequest!.parameters["auth-token"]).to.equal("test-token"); + }); });