Add fetchDictionaries parallel-fetch layer
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { fetchDictionaries } from "./api.js";
|
||||
import type { ApiClient } from "@/shared/api/client.js";
|
||||
|
||||
function makeClient(
|
||||
responses: Record<string, unknown>,
|
||||
): ApiClient {
|
||||
return {
|
||||
get: vi.fn(async (path: string) => {
|
||||
const match = Object.keys(responses).find((k) => path.includes(k));
|
||||
if (!match) throw new Error(`Unexpected path: ${path}`);
|
||||
return responses[match];
|
||||
}),
|
||||
} as unknown as ApiClient;
|
||||
}
|
||||
|
||||
describe("fetchDictionaries", () => {
|
||||
it("fetches four endpoints in parallel and returns a combined shape", async () => {
|
||||
const client = makeClient({
|
||||
world_regions: [{ world_region_id: 1, title: { ru: "Европа" } }],
|
||||
countries: [{ code: "RU", title: { ru: "Россия" }, world_region_id: 1 }],
|
||||
cities: [
|
||||
{ code: "MOW", title: { ru: "Москва" }, country_code: "RU" },
|
||||
],
|
||||
airports: [
|
||||
{ code: "SVO", city_code: "MOW", title: { ru: "Шереметьево" }, has_afl_flights: true },
|
||||
],
|
||||
});
|
||||
|
||||
const result = await fetchDictionaries(client);
|
||||
|
||||
expect(result.regions).toHaveLength(1);
|
||||
expect(result.countries).toHaveLength(1);
|
||||
expect(result.cities).toHaveLength(1);
|
||||
expect(result.airports).toHaveLength(1);
|
||||
expect(result.cities[0]!.code).toBe("MOW");
|
||||
});
|
||||
|
||||
it("calls exactly the four dictionary paths", async () => {
|
||||
const getMock = vi.fn(async (_path: string) => []);
|
||||
const client = { get: getMock } as unknown as ApiClient;
|
||||
|
||||
await fetchDictionaries(client);
|
||||
|
||||
const calledPaths = getMock.mock.calls.map((c) => c[0] as string);
|
||||
expect(calledPaths).toContain("dictionary/1/world_regions");
|
||||
expect(calledPaths).toContain("dictionary/1/countries");
|
||||
expect(calledPaths).toContain("dictionary/1/cities");
|
||||
expect(calledPaths).toContain("dictionary/1/airports");
|
||||
expect(getMock).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it("propagates a rejection from any endpoint", async () => {
|
||||
const client = {
|
||||
get: vi.fn(async (path: string) => {
|
||||
if (path.includes("airports")) throw new Error("boom");
|
||||
return [];
|
||||
}),
|
||||
} as unknown as ApiClient;
|
||||
|
||||
await expect(fetchDictionaries(client)).rejects.toThrow("boom");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Parallel fetch of the four dictionary endpoints.
|
||||
*
|
||||
* Paths match the Angular `NetworkService.getDictionary` pattern:
|
||||
* `/dictionary/1/{name}`. Locale is not part of the path; the server
|
||||
* returns all titles as a `Record<lang, string>` and the consumer picks.
|
||||
*/
|
||||
|
||||
import type { ApiClient } from "@/shared/api/client.js";
|
||||
import type {
|
||||
IRawDictionaries,
|
||||
IRawRegion,
|
||||
IRawCountry,
|
||||
IRawCity,
|
||||
IRawAirport,
|
||||
} from "./types.js";
|
||||
|
||||
export async function fetchDictionaries(
|
||||
client: ApiClient,
|
||||
): Promise<IRawDictionaries> {
|
||||
const [regions, countries, cities, airports] = await Promise.all([
|
||||
client.get<IRawRegion[]>("dictionary/1/world_regions"),
|
||||
client.get<IRawCountry[]>("dictionary/1/countries"),
|
||||
client.get<IRawCity[]>("dictionary/1/cities"),
|
||||
client.get<IRawAirport[]>("dictionary/1/airports"),
|
||||
]);
|
||||
|
||||
return { regions, countries, cities, airports };
|
||||
}
|
||||
Reference in New Issue
Block a user