feat: add batch submit client

This commit is contained in:
admin123 2026-04-22 13:51:39 +08:00
parent ff2755e218
commit b75755e6a6
2 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,65 @@
import type { BatchPayload } from "../content/market/batch-payload";
import { isAuthResponseMessage } from "./auth-messages";
interface FetchResponseLike {
json(): Promise<unknown>;
ok: boolean;
status: number;
}
type FetchLike = (
input: string,
init?: RequestInit
) => Promise<FetchResponseLike>;
type SendMessageLike = (message: unknown) => Promise<unknown>;
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<string> {
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;
}

View File

@ -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);
});
});