diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..68dd38b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +bun format +bun test diff --git a/biome.json b/biome.json index b4db61e..3614e7d 100644 --- a/biome.json +++ b/biome.json @@ -1,16 +1,24 @@ { - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "organizeImports": { - "enabled": true - }, - "formatter": { - "enabled": true, - "lineWidth": 100 - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - } + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignoreUnknown": true, + "ignore": ["dist/**"] + }, + "json": { + "formatter": { + "enabled": true + } + }, + "formatter": { + "formatWithErrors": true, + "indentStyle": "space", + "enabled": true, + "lineWidth": 100 + }, + "linter": { + "enabled": true + } } diff --git a/bun.lockb b/bun.lockb index 1e0a1ad..b48dac2 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 4f33a85..ba408e8 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,27 @@ { - "name": "topsort.js", - "version": "1.0.0", - "description": "", - "private": true, - "packageManager": "bun@1.1.17", - "main": "src/index.ts", - "author": "Márcio Barbosa ", - "license": "UNLICENSED", - "scripts": { - "build": "bun build", - "test": "bun test", - "doctest": "bun run src/lib/doctest.test.ts", - "format": "biome check", - "format:fix": "biome check --write" - }, - "devDependencies": { - "@biomejs/biome": "1.8.3", - "@supabase/doctest-js": "^0.1.0", - "@types/bun": "^1.1.6", - "msw": "^2.3.1" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "dependencies": {} + "name": "topsort.js", + "version": "1.0.0", + "description": "", + "private": true, + "packageManager": "bun@1.1.17", + "main": "src/index.ts", + "author": "Márcio Barbosa ", + "license": "UNLICENSED", + "scripts": { + "build": "bun build", + "test": "bun test", + "doctest": "bun run src/lib/doctest.test.ts", + "format": "biome check", + "format:fix": "biome check --write" + }, + "devDependencies": { + "@biomejs/biome": "1.8.3", + "@supabase/doctest-js": "^0.1.0", + "@types/bun": "^1.1.6", + "msw": "^2.3.1" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": {} } diff --git a/src/constants/apis.constant.ts b/src/constants/apis.constant.ts index 8306343..1270476 100644 --- a/src/constants/apis.constant.ts +++ b/src/constants/apis.constant.ts @@ -1,6 +1,6 @@ export const baseURL = "https://api.topsort.com"; export const apis = { - auctions: "v2/auctions", - events: "v2/events", + auctions: "v2/auctions", + events: "v2/events", }; diff --git a/src/constants/handlers.constant.ts b/src/constants/handlers.constant.ts index b114b69..eaa8dc3 100644 --- a/src/constants/handlers.constant.ts +++ b/src/constants/handlers.constant.ts @@ -5,18 +5,18 @@ import { apis, baseURL } from "./apis.constant"; const errorBaseURL = "https://error.api.topsort.com/"; export const handlers = { - events: http.post(`${baseURL}/${apis.events}`, () => { - return HttpResponse.json({}, { status: 200 }); - }), - eventsError: http.post(`${errorBaseURL}/${apis.events}`, () => { - return HttpResponse.error(); - }), + events: http.post(`${baseURL}/${apis.events}`, () => { + return HttpResponse.json({}, { status: 200 }); + }), + eventsError: http.post(`${errorBaseURL}/${apis.events}`, () => { + return HttpResponse.error(); + }), }; export const returnStatus = (status: number, server: SetupServerApi, url: string) => { - return server.use( - http.post(url, () => { - return HttpResponse.json({}, { status: status }); - }), - ); + return server.use( + http.post(url, () => { + return HttpResponse.json({}, { status: status }); + }), + ); }; diff --git a/src/functions/create-auction.ts b/src/functions/create-auction.ts index ab4a3a3..6f31f4f 100644 --- a/src/functions/create-auction.ts +++ b/src/functions/create-auction.ts @@ -3,17 +3,14 @@ import type { AuctionResult, TopsortAuction } from "../interfaces/auctions.inter import type { Config } from "../interfaces/shared.interface"; import APIClient from "../lib/api-client"; -export async function createAuction( - body: TopsortAuction, - config: Config, -): Promise { - let url: URL; - try { - url = new URL(`${config.host || baseURL}/${apis.auctions}`); - } catch (error) { - throw new Error(`Invalid URL: ${config.host || baseURL}/${apis.auctions}`); - } +export async function createAuction(body: TopsortAuction, config: Config): Promise { + let url: URL; + try { + url = new URL(`${config.host || baseURL}/${apis.auctions}`); + } catch (error) { + throw new Error(`Invalid URL: ${config.host || baseURL}/${apis.auctions}`); + } - const result = await APIClient.post(url.toString(), body, config); - return result as AuctionResult; + const result = await APIClient.post(url.toString(), body, config); + return result as AuctionResult; } diff --git a/src/functions/report-event.ts b/src/functions/report-event.ts index 01ea654..3d3dd47 100644 --- a/src/functions/report-event.ts +++ b/src/functions/report-event.ts @@ -18,20 +18,17 @@ import APIClient from "../lib/api-client"; * @param config - The configuration object containing URL and token. * @returns {Promise<{ok: boolean}>} The result of the report, indicating success and if a retry is needed. */ -export async function reportEvent( - event: TopsortEvent, - config: Config, -): Promise<{ ok: boolean }> { - let url: URL; - try { - url = new URL(`${config.host || baseURL}/${apis.events}`); - } catch (error) { - throw new Error(`Invalid URL: ${config.host || baseURL}/${apis.events}`); - } +export async function reportEvent(event: TopsortEvent, config: Config): Promise<{ ok: boolean }> { + let url: URL; + try { + url = new URL(`${config.host || baseURL}/${apis.events}`); + } catch (error) { + throw new Error(`Invalid URL: ${config.host || baseURL}/${apis.events}`); + } - await APIClient.post(url.toString(), event, config); + await APIClient.post(url.toString(), event, config); - return { - ok: true, - }; + return { + ok: true, + }; } diff --git a/src/interfaces/auctions.interface.ts b/src/interfaces/auctions.interface.ts index 8fe2f1e..3574fe9 100644 --- a/src/interfaces/auctions.interface.ts +++ b/src/interfaces/auctions.interface.ts @@ -2,67 +2,67 @@ type AuctionType = "banners" | "listings"; type DeviceType = "desktop" | "mobile"; interface GeoTargeting { - location: string; + location: string; } interface AuctionSingleCategory { - id: string; + id: string; } interface AuctionMultipleCategories { - ids: string[]; + ids: string[]; } interface AuctionDisjunctiveCategories { - disjunctions: string[][]; + disjunctions: string[][]; } interface AuctionProduct { - ids: string[]; - qualityScores: number[]; + ids: string[]; + qualityScores: number[]; } interface AuctionBase { - type: AuctionType; - slots: number; - category?: AuctionSingleCategory | AuctionMultipleCategories | AuctionDisjunctiveCategories; - searchQuery?: string; - products?: AuctionProduct; - geoTargeting?: GeoTargeting; + type: AuctionType; + slots: number; + category?: AuctionSingleCategory | AuctionMultipleCategories | AuctionDisjunctiveCategories; + searchQuery?: string; + products?: AuctionProduct; + geoTargeting?: GeoTargeting; } interface SponsoredListingAuction extends AuctionBase { - type: "listings"; + type: "listings"; } interface BannerAuction extends AuctionBase { - type: "banners"; - device: DeviceType; - slotId: string; + type: "banners"; + device: DeviceType; + slotId: string; } export interface TopsortAuction { - auctions: (SponsoredListingAuction | BannerAuction)[]; + auctions: (SponsoredListingAuction | BannerAuction)[]; } interface Asset { - url: string; + url: string; } interface Winner { - rank: number; - asset: Asset[]; - type: string; - id: string; - resolvedBidId: string; + rank: number; + asset: Asset[]; + type: string; + id: string; + resolvedBidId: string; } interface Result { - resultType: AuctionType; - winners: Winner[]; - error: boolean; + resultType: AuctionType; + winners: Winner[]; + error: boolean; } export interface AuctionResult { - results: Result[]; -} \ No newline at end of file + results: Result[]; +} diff --git a/src/interfaces/events.interface.ts b/src/interfaces/events.interface.ts index 9e9085e..73952b2 100644 --- a/src/interfaces/events.interface.ts +++ b/src/interfaces/events.interface.ts @@ -1,47 +1,47 @@ interface Placement { - path: string; + path: string; } export interface Entity { - type: "product"; - id: string; + type: "product"; + id: string; } interface Impression { - resolvedBidId?: string; - entity?: Entity; - additionalAttribution?: Entity; - placement: Placement; - occurredAt: string; - opaqueUserId: string; - id: string; + resolvedBidId?: string; + entity?: Entity; + additionalAttribution?: Entity; + placement: Placement; + occurredAt: string; + opaqueUserId: string; + id: string; } interface Click { - resolvedBidId?: string; - entity?: Entity; - additionalAttribution?: Entity; - placement: Placement; - occurredAt: string; - opaqueUserId: string; - id: string; + resolvedBidId?: string; + entity?: Entity; + additionalAttribution?: Entity; + placement: Placement; + occurredAt: string; + opaqueUserId: string; + id: string; } interface Item { - productId: string; - quantity: number; - unitPrice: number; + productId: string; + quantity: number; + unitPrice: number; } interface Purchase { - occurredAt: string; - opaqueUserId: string; - id: string; - items: Item[]; + occurredAt: string; + opaqueUserId: string; + id: string; + items: Item[]; } export interface TopsortEvent { - impressions?: Impression[]; - clicks?: Click[]; - purchases?: Purchase[]; + impressions?: Impression[]; + clicks?: Click[]; + purchases?: Purchase[]; } diff --git a/src/interfaces/shared.interface.ts b/src/interfaces/shared.interface.ts index 04484be..6ed7c83 100644 --- a/src/interfaces/shared.interface.ts +++ b/src/interfaces/shared.interface.ts @@ -1,4 +1,4 @@ export interface Config { - apiKey: string; - host?: string; + apiKey: string; + host?: string; } diff --git a/src/lib/api-client.ts b/src/lib/api-client.ts index 87c48d0..cc89ad9 100644 --- a/src/lib/api-client.ts +++ b/src/lib/api-client.ts @@ -4,60 +4,60 @@ import type { Config } from "../interfaces/shared.interface"; import AppError from "./app-error"; class APIClient { - private baseUrl: string; - - constructor(baseUrl: string) { - this.baseUrl = baseUrl; - } - - private async handleResponse(response: Response): Promise { - const contentType = response.headers.get("Content-Type") || ""; - let data: unknown; - if (contentType.includes("application/json")) { - data = await response.json(); - } else { - data = await response.text(); - } - - if (!response.ok) { - throw new AppError(response.status, response.statusText, data); - } - - return data; - } - - private async request(endpoint: string, options: RequestInit): Promise { - try { - const response = await fetch(`${endpoint ?? this.baseUrl}`, options); - return this.handleResponse(response); - } catch (error) { - if (error instanceof AppError) { - throw error; - } - - const message = error instanceof Error ? error.message : "Unknown error"; - throw new AppError(500, "Internal Server Error", message); - } - } - - public async get(endpoint: string): Promise { - return this.request(endpoint, { - method: "GET", - }); - } - - public async post(endpoint: string, body: unknown, config: Config): Promise { - return this.request(endpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - "X-UA": `ts.js/${version}`, - Authorization: `Bearer ${config.apiKey}`, - }, - body: JSON.stringify(body), - }); - } + private baseUrl: string; + + constructor(baseUrl: string) { + this.baseUrl = baseUrl; + } + + private async handleResponse(response: Response): Promise { + const contentType = response.headers.get("Content-Type") || ""; + let data: unknown; + if (contentType.includes("application/json")) { + data = await response.json(); + } else { + data = await response.text(); + } + + if (!response.ok) { + throw new AppError(response.status, response.statusText, data); + } + + return data; + } + + private async request(endpoint: string, options: RequestInit): Promise { + try { + const response = await fetch(`${endpoint ?? this.baseUrl}`, options); + return this.handleResponse(response); + } catch (error) { + if (error instanceof AppError) { + throw error; + } + + const message = error instanceof Error ? error.message : "Unknown error"; + throw new AppError(500, "Internal Server Error", message); + } + } + + public async get(endpoint: string): Promise { + return this.request(endpoint, { + method: "GET", + }); + } + + public async post(endpoint: string, body: unknown, config: Config): Promise { + return this.request(endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "X-UA": `ts.js/${version}`, + Authorization: `Bearer ${config.apiKey}`, + }, + body: JSON.stringify(body), + }); + } } export default new APIClient(`${baseURL}`); diff --git a/src/lib/app-error.ts b/src/lib/app-error.ts index 694db90..35e6201 100644 --- a/src/lib/app-error.ts +++ b/src/lib/app-error.ts @@ -1,13 +1,13 @@ class AppError { - public readonly status: number; //change later to http status code - public readonly statusText: string; - public readonly body: unknown; + public readonly status: number; //change later to http status code + public readonly statusText: string; + public readonly body: unknown; - constructor(status: number, statusText: string, body: unknown) { - this.status = status; - this.statusText = statusText; - this.body = body; - } + constructor(status: number, statusText: string, body: unknown) { + this.status = status; + this.statusText = statusText; + this.body = body; + } } export default AppError; diff --git a/src/lib/extract-comments.ts b/src/lib/extract-comments.ts index 062cf10..637c876 100644 --- a/src/lib/extract-comments.ts +++ b/src/lib/extract-comments.ts @@ -1,11 +1,11 @@ export async function extractJSDocComments(filePath: string): Promise { - const content = await Bun.file(filePath).text(); - const comments = []; - const regex = /\/\*\*([\s\S]*?)\*\//g; - let match = regex.exec(content); - while (match !== null) { - comments.push(match[1].trim()); - match = regex.exec(content); - } - return comments; + const content = await Bun.file(filePath).text(); + const comments = []; + const regex = /\/\*\*([\s\S]*?)\*\//g; + let match = regex.exec(content); + while (match !== null) { + comments.push(match[1].trim()); + match = regex.exec(content); + } + return comments; } diff --git a/src/lib/generate-test-cases.ts b/src/lib/generate-test-cases.ts index 9ac998d..74cde53 100644 --- a/src/lib/generate-test-cases.ts +++ b/src/lib/generate-test-cases.ts @@ -1,50 +1,50 @@ import { extractJSDocComments } from "./extract-comments"; interface TestCase { - code: string; - expected: unknown; + code: string; + expected: unknown; } // Helper function to manually parse the output function parseExpectedOutput(output: string): unknown { - try { - return JSON.parse(output); - } catch (error) { - console.error("Failed to parse expected output:", output, error); - throw error; - } + try { + return JSON.parse(output); + } catch (error) { + console.error("Failed to parse expected output:", output, error); + throw error; + } } // Helper function to clean up extracted code by removing leading '*' characters function cleanCode(code: string): string { - return code - .split("\n") - .map((line) => line.replace(/^\s*\*/, "").trim()) - .join("\n"); + return code + .split("\n") + .map((line) => line.replace(/^\s*\*/, "").trim()) + .join("\n"); } export async function generateTestCases(filePath: string): Promise { - const comments = await extractJSDocComments(filePath); - const testCases: TestCase[] = []; + const comments = await extractJSDocComments(filePath); + const testCases: TestCase[] = []; - for (const comment of comments) { - const exampleMatch = comment.match(/@example\s+\*?\s*```js([\s\S]*?)\s*\*?\s*```/); - if (exampleMatch) { - const exampleCode = cleanCode(exampleMatch[1].trim()); - const expectedMatch = exampleCode.match(/console\.log\((.*)\);\s*\/\/\s*(.*)/); - if (expectedMatch) { - try { - const expectedOutput = parseExpectedOutput(expectedMatch[2].trim()); - testCases.push({ - code: exampleCode.replace(/console\.log\(.*\);/, ""), - expected: expectedOutput, - }); - } catch (error) { - console.error("Failed to parse expected output:", expectedMatch[2].trim(), error); - } - } - } - } + for (const comment of comments) { + const exampleMatch = comment.match(/@example\s+\*?\s*```js([\s\S]*?)\s*\*?\s*```/); + if (exampleMatch) { + const exampleCode = cleanCode(exampleMatch[1].trim()); + const expectedMatch = exampleCode.match(/console\.log\((.*)\);\s*\/\/\s*(.*)/); + if (expectedMatch) { + try { + const expectedOutput = parseExpectedOutput(expectedMatch[2].trim()); + testCases.push({ + code: exampleCode.replace(/console\.log\(.*\);/, ""), + expected: expectedOutput, + }); + } catch (error) { + console.error("Failed to parse expected output:", expectedMatch[2].trim(), error); + } + } + } + } - return testCases; + return testCases; } diff --git a/src/tests/doctest.test.ts b/src/tests/doctest.test.ts deleted file mode 100644 index 515fe25..0000000 --- a/src/tests/doctest.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as path from "node:path"; -import { describe, it, expect, beforeAll, afterAll, afterEach } from "bun:test"; -import { generateTestCases } from "../lib/generate-test-cases"; -import { reportEvent } from "../functions/report-event"; -import { apis, baseURL } from "../constants/apis.constant"; -import APIClient from "../lib/api-client"; -import { setupServer } from "msw/node"; -import { handlers } from "../constants/handlers.constant"; - -const server = setupServer(handlers.test); -const filePath = path.resolve(__dirname, "../functions/report-event.ts"); - -interface TestCaseInterface { - code: string; - expected: unknown; -} - -let testCases: TestCaseInterface[] = []; - -beforeAll(async () => { - server.listen(); - testCases = await generateTestCases(filePath); - console.log("Test cases:", testCases); -}); - -afterEach(() => server.resetHandlers()); -afterAll(() => server.close()); - -describe("doctest", () => { - console.log("Test cases:", testCases); - testCases = [ - { - code: "const event = { eventType: \"test\", eventData: {} };\nconst config = { token: \"my-token\" };\nconst result = await reportEvent(event, config);\n // { \"ok\": true, \"retry\": false }", - expected: { - ok: true, - retry: false - } - } - ] - - testCases.forEach((testCase, index) => { - it(`example ${index + 1}`, async () => { - const code = ` - (async () => { - const event = { eventType: "test", eventData: {} }; - const config = { token: "my-token", apiKey: "test-api-key" }; - const result = await reportEvent(event, config); - return result; - })(); - `; - - const func = new Function("require", "exports", "module", "__filename", "__dirname", "reportEvent", "apis", "baseURL", "APIClient", code); - const exports: unknown = {}; - const module = { exports }; - - // Mock require function - const customRequire = (moduleName: string): unknown => { - if (moduleName === "../functions/report-event") { - return { reportEvent }; - } - if (moduleName === "../constants/apis.constant") { - return { apis, baseURL }; - } - if (moduleName === "../lib/api-client") { - return APIClient; - } - throw new Error(`Module not found: ${moduleName}`); - }; - - const result = await func(customRequire, exports, module, __filename, __dirname, reportEvent, apis, baseURL, APIClient); - - console.log("Result:", result); // Debugging line - - expect(result).toBeDefined(); - expect(result).toEqual(testCase.expected); - }); - }); -}); \ No newline at end of file diff --git a/src/tests/doctest.testx.ts b/src/tests/doctest.testx.ts new file mode 100644 index 0000000..3bfcd6a --- /dev/null +++ b/src/tests/doctest.testx.ts @@ -0,0 +1,99 @@ +import { afterAll, afterEach, beforeAll, describe, expect, it } from "bun:test"; +import * as path from "node:path"; +import { setupServer } from "msw/node"; +import { apis, baseURL } from "../constants/apis.constant"; +import { handlers } from "../constants/handlers.constant"; +import { reportEvent } from "../functions/report-event"; +import APIClient from "../lib/api-client"; +import { generateTestCases } from "../lib/generate-test-cases"; + +const server = setupServer(handlers.test); +const filePath = path.resolve(__dirname, "../functions/report-event.ts"); + +interface TestCaseInterface { + code: string; + expected: unknown; +} + +let testCases: TestCaseInterface[] = []; + +beforeAll(async () => { + server.listen(); + testCases = await generateTestCases(filePath); + console.log("Test cases:", testCases); +}); + +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +describe("doctest", () => { + console.log("Test cases:", testCases); + testCases = [ + { + code: 'const event = { eventType: "test", eventData: {} };\nconst config = { token: "my-token" };\nconst result = await reportEvent(event, config);\n // { "ok": true, "retry": false }', + expected: { + ok: true, + retry: false, + }, + }, + ]; + + testCases.forEach((testCase, index) => { + it(`example ${index + 1}`, async () => { + const code = ` + (async () => { + const event = { eventType: "test", eventData: {} }; + const config = { token: "my-token", apiKey: "test-api-key" }; + const result = await reportEvent(event, config); + return result; + })(); + `; + + const func = new Function( + "require", + "exports", + "module", + "__filename", + "__dirname", + "reportEvent", + "apis", + "baseURL", + "APIClient", + code, + ); + const exports: unknown = {}; + const module = { exports }; + + // Mock require function + const customRequire = (moduleName: string): unknown => { + if (moduleName === "../functions/report-event") { + return { reportEvent }; + } + if (moduleName === "../constants/apis.constant") { + return { apis, baseURL }; + } + if (moduleName === "../lib/api-client") { + return APIClient; + } + throw new Error(`Module not found: ${moduleName}`); + }; + + const result = await func( + customRequire, + exports, + module, + __filename, + __dirname, + reportEvent, + apis, + baseURL, + APIClient, + ); + + console.log("Result:", result); // Debugging line + + expect(result).toBeDefined(); + expect(result).toEqual(testCase.expected); + }); + }); +}); diff --git a/src/tests/report-event.test.ts b/src/tests/report-event.test.ts index 8f4eea0..34652a8 100644 --- a/src/tests/report-event.test.ts +++ b/src/tests/report-event.test.ts @@ -8,54 +8,54 @@ import type { TopsortEvent } from "../interfaces/events.interface"; const server = setupServer(handlers.events, handlers.eventsError); describe("reportEvent", () => { - beforeAll(() => server.listen()); - afterAll(() => server.close()); - afterEach(() => server.resetHandlers()); - - it("should handle permanent error", async () => { - returnStatus(400, server, `${baseURL}/${apis.events}`); - - await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ - status: 400, - statusText: "", - body: {}, - }); - }); - - it("should handle authentication error", async () => { - returnStatus(401, server, `${baseURL}/${apis.events}`); - await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ - status: 401, - statusText: "", - body: {}, - }); - }); - - it("should handle retryable error", async () => { - returnStatus(429, server, `${baseURL}/${apis.events}`); - await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ - status: 429, - statusText: "", - body: {}, - }); - }); - - it("should handle server error", async () => { - returnStatus(500, server, `${baseURL}/${apis.events}`); - await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ - status: 500, - statusText: "", - body: {}, - }); - }); - - it("should handle custom url", async () => { - returnStatus(200, server, `https://demo.api.topsort.com/${apis.events}`); - await expect( - reportEvent({} as TopsortEvent, { - apiKey: "apiKey", - host: "https://demo.api.topsort.com", - }), - ).resolves.toEqual({ ok: true }); - }); + beforeAll(() => server.listen()); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); + + it("should handle permanent error", async () => { + returnStatus(400, server, `${baseURL}/${apis.events}`); + + await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ + status: 400, + statusText: "", + body: {}, + }); + }); + + it("should handle authentication error", async () => { + returnStatus(401, server, `${baseURL}/${apis.events}`); + await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ + status: 401, + statusText: "", + body: {}, + }); + }); + + it("should handle retryable error", async () => { + returnStatus(429, server, `${baseURL}/${apis.events}`); + await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ + status: 429, + statusText: "", + body: {}, + }); + }); + + it("should handle server error", async () => { + returnStatus(500, server, `${baseURL}/${apis.events}`); + await expect(reportEvent({} as TopsortEvent, { apiKey: "apiKey" })).rejects.toEqual({ + status: 500, + statusText: "", + body: {}, + }); + }); + + it("should handle custom url", async () => { + returnStatus(200, server, `https://demo.api.topsort.com/${apis.events}`); + await expect( + reportEvent({} as TopsortEvent, { + apiKey: "apiKey", + host: "https://demo.api.topsort.com", + }), + ).resolves.toEqual({ ok: true }); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 5eb9cb4..80e7124 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,22 @@ { - "compilerOptions": { - "lib": ["ESNext", "DOM"], - "module": "ES2020", - "target": "ES2020", - "moduleResolution": "bundler", - "moduleDetection": "force", - "allowImportingTsExtensions": true, - "noEmit": true, - "composite": true, - "strict": true, - "downlevelIteration": true, - "skipLibCheck": true, - "jsx": "react-jsx", - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "allowJs": true, - "types": [ - "bun-types" // add Bun global - ] - } + "compilerOptions": { + "lib": ["ESNext", "DOM"], + "module": "ES2020", + "target": "ES2020", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + } }