const { test } = require("node:test"); const assert = require("node:assert"); const { buildAuthorizeUrl, exchangeCodeForTokens, refreshAccessToken } = require("../src/lib/lineworksOAuth"); test("buildAuthorizeUrl builds the LINEWORKS authorize URL with required query params", () => { const url = new URL(buildAuthorizeUrl({ clientId: "cid", redirectUri: "https://lwauth29.next-hd.net/auth/callback", scope: "form form.read", state: "state-abc", })); assert.strictEqual(url.origin + url.pathname, "https://auth.worksmobile.com/oauth2/v2.0/authorize"); assert.strictEqual(url.searchParams.get("client_id"), "cid"); assert.strictEqual(url.searchParams.get("redirect_uri"), "https://lwauth29.next-hd.net/auth/callback"); assert.strictEqual(url.searchParams.get("scope"), "form form.read"); assert.strictEqual(url.searchParams.get("state"), "state-abc"); assert.strictEqual(url.searchParams.get("response_type"), "code"); }); test("exchangeCodeForTokens posts grant_type=authorization_code and returns camelCase token fields", async () => { let capturedBody; const fetchImpl = async (url, opts) => { capturedBody = opts.body; return { ok: true, json: async () => ({ access_token: "at-1", refresh_token: "rt-1", expires_in: 3600 }) }; }; const result = await exchangeCodeForTokens({ clientId: "cid", clientSecret: "secret", redirectUri: "https://x/callback", code: "code-1", fetchImpl }); assert.deepStrictEqual(result, { accessToken: "at-1", refreshToken: "rt-1", expiresIn: 3600 }); assert.ok(capturedBody.includes("grant_type=authorization_code")); assert.ok(capturedBody.includes("code=code-1")); }); test("refreshAccessToken posts grant_type=refresh_token and returns camelCase token fields", async () => { let capturedBody; const fetchImpl = async (url, opts) => { capturedBody = opts.body; return { ok: true, json: async () => ({ access_token: "at-2", refresh_token: "rt-2", expires_in: 3600 }) }; }; const result = await refreshAccessToken({ clientId: "cid", clientSecret: "secret", refreshToken: "rt-1", fetchImpl }); assert.deepStrictEqual(result, { accessToken: "at-2", refreshToken: "rt-2", expiresIn: 3600 }); assert.ok(capturedBody.includes("grant_type=refresh_token")); assert.ok(capturedBody.includes("refresh_token=rt-1")); }); test("exchangeCodeForTokens throws when the response is not ok", async () => { const fetchImpl = async () => ({ ok: false, status: 400, json: async () => ({ error: "invalid_grant" }) }); await assert.rejects(() => exchangeCodeForTokens({ clientId: "cid", clientSecret: "secret", redirectUri: "https://x/callback", code: "bad", fetchImpl })); });