From b75755e6a6b7112c193ffe32ac91dfeb2ff9b3ed Mon Sep 17 00:00:00 2001 From: admin123 Date: Wed, 22 Apr 2026 13:51:39 +0800 Subject: [PATCH] feat: add batch submit client --- src/shared/batch-submit-client.ts | 65 ++++++++++++++++++++++++ tests/batch-submit-client.test.ts | 82 +++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/shared/batch-submit-client.ts create mode 100644 tests/batch-submit-client.test.ts diff --git a/src/shared/batch-submit-client.ts b/src/shared/batch-submit-client.ts new file mode 100644 index 0000000..f8518bc --- /dev/null +++ b/src/shared/batch-submit-client.ts @@ -0,0 +1,65 @@ +import type { BatchPayload } from "../content/market/batch-payload"; +import { isAuthResponseMessage } from "./auth-messages"; + +interface FetchResponseLike { + json(): Promise; + ok: boolean; + status: number; +} + +type FetchLike = ( + input: string, + init?: RequestInit +) => Promise; + +type SendMessageLike = (message: unknown) => Promise; + +export function createBatchSubmitClient(options: { + baseUrl: string; + fetchImpl?: FetchLike; + sendMessage: SendMessageLike; +}) { + const fetchImpl = options.fetchImpl ?? fetch; + + return { + async submitBatch(payload: BatchPayload) { + const token = await readAccessToken(options.sendMessage); + const response = await fetchImpl( + new URL("/api/mock/batches", options.baseUrl).toString(), + { + body: JSON.stringify(payload), + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json" + }, + method: "POST" + } + ); + + if (response.status === 401 || response.status === 403) { + throw new Error("batch submit unauthorized"); + } + + if (!response.ok) { + throw new Error(`batch submit failed: ${response.status}`); + } + + return response.json(); + } + }; +} + +async function readAccessToken(sendMessage: SendMessageLike): Promise { + const response = await sendMessage({ type: "auth:get-access-token" }); + + if ( + !isAuthResponseMessage(response) || + !response.ok || + response.type !== "auth:token" || + !response.value.accessToken.trim() + ) { + throw new Error("batch submit token unavailable"); + } + + return response.value.accessToken; +} diff --git a/tests/batch-submit-client.test.ts b/tests/batch-submit-client.test.ts new file mode 100644 index 0000000..86481e6 --- /dev/null +++ b/tests/batch-submit-client.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, test, vi } from "vitest"; + +import { createBatchSubmitClient } from "../src/shared/batch-submit-client"; + +describe("batch-submit-client", () => { + test("posts the batch payload with a Bearer token", async () => { + const sendMessage = vi.fn(async () => ({ + ok: true, + type: "auth:token", + value: { accessToken: "abc123" } + })); + const fetchImpl = vi.fn(async () => ({ + ok: true, + status: 200, + json: async () => ({ acceptedCount: 2, ok: true }) + })); + + const client = createBatchSubmitClient({ + baseUrl: "http://127.0.0.1:4319", + fetchImpl, + sendMessage + }); + + await client.submitBatch({ + authors: [{ authorId: "111", authorName: "达人A" }], + batchId: "批次A-2026-04-22T12:30:00.000Z", + batchName: "批次A", + createdAt: "2026-04-22T12:30:00.000Z", + creatorName: "王少卿", + logtoUserId: "p7pdhhtde8kj", + resource: "https://talent-search.intelligrow.cn" + }); + + expect(fetchImpl).toHaveBeenCalledWith( + "http://127.0.0.1:4319/api/mock/batches", + expect.objectContaining({ + body: JSON.stringify({ + authors: [{ authorId: "111", authorName: "达人A" }], + batchId: "批次A-2026-04-22T12:30:00.000Z", + batchName: "批次A", + createdAt: "2026-04-22T12:30:00.000Z", + creatorName: "王少卿", + logtoUserId: "p7pdhhtde8kj", + resource: "https://talent-search.intelligrow.cn" + }), + headers: expect.objectContaining({ + Authorization: "Bearer abc123", + "Content-Type": "application/json" + }), + method: "POST" + }) + ); + }); + + test("throws on unauthorized responses", async () => { + const client = createBatchSubmitClient({ + baseUrl: "http://127.0.0.1:4319", + fetchImpl: vi.fn(async () => ({ + ok: false, + status: 401, + json: async () => ({ error: "unauthorized", ok: false }) + })), + sendMessage: vi.fn(async () => ({ + ok: true, + type: "auth:token", + value: { accessToken: "abc123" } + })) + }); + + await expect( + client.submitBatch({ + authors: [], + batchId: "批次A-2026-04-22T12:30:00.000Z", + batchName: "批次A", + createdAt: "2026-04-22T12:30:00.000Z", + creatorName: "王少卿", + logtoUserId: "p7pdhhtde8kj", + resource: "https://talent-search.intelligrow.cn" + }) + ).rejects.toThrow(/unauthorized/i); + }); +});