feat(organizations): migrate source storage to polymorphic records
This commit is contained in:
257
ts_client/test/client.test.ts
Normal file
257
ts_client/test/client.test.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
MostovikOrganizationsClient,
|
||||
type ApiErrorPayload,
|
||||
type FetchLike,
|
||||
type GenericSourcePayload,
|
||||
type Organization,
|
||||
type OrganizationListResponse,
|
||||
} from "../src/index.js";
|
||||
|
||||
function jsonResponse(payload: unknown, init: ResponseInit = {}): Response {
|
||||
return new Response(JSON.stringify(payload), {
|
||||
status: init.status ?? 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
...init.headers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
test("listOrganizations serializes all supported organization filters", async () => {
|
||||
const calls: RequestInfo[] = [];
|
||||
const fetchImpl: FetchLike = async (input) => {
|
||||
calls.push(input);
|
||||
const payload: OrganizationListResponse = {
|
||||
success: true,
|
||||
data: [],
|
||||
errors: null,
|
||||
meta: {
|
||||
pagination: {
|
||||
page: 2,
|
||||
page_size: 50,
|
||||
total_count: 0,
|
||||
total_pages: 0,
|
||||
has_next: false,
|
||||
has_previous: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
return jsonResponse(payload);
|
||||
};
|
||||
const client = new MostovikOrganizationsClient({
|
||||
baseUrl: "https://api.example.test/",
|
||||
fetch: fetchImpl,
|
||||
headers: { Authorization: "Bearer static-token" },
|
||||
});
|
||||
|
||||
const response = await client.listOrganizations({
|
||||
page: 2,
|
||||
page_size: 50,
|
||||
search: "мост",
|
||||
ordering: "-name",
|
||||
name: "Северный",
|
||||
inn: "7711111111",
|
||||
kpp: "771101001",
|
||||
ogrn: "1027700132111",
|
||||
ogrip: "304500116000157",
|
||||
registry: "8e7a3cb8-6fb2-43a8-b847-62d84ea9f34f",
|
||||
registry_name: "Росатом",
|
||||
has_registry: false,
|
||||
has_industrial: true,
|
||||
has_industrial_products: false,
|
||||
has_manufactures: true,
|
||||
has_inspections: false,
|
||||
has_procurements: true,
|
||||
has_procurements_44fz: false,
|
||||
has_procurements_223fz: true,
|
||||
has_contracts: false,
|
||||
has_unfair_suppliers: true,
|
||||
has_fas_goz: false,
|
||||
has_arbitration: true,
|
||||
has_fedresurs_bankruptcy: false,
|
||||
has_fstec: true,
|
||||
has_vacancies: true,
|
||||
has_trudvsem: false,
|
||||
has_fns_reports: false,
|
||||
data: ["industrial", "fns_reports"],
|
||||
exclude_data_sources: "vacancies",
|
||||
});
|
||||
|
||||
assert.equal(response.success, true);
|
||||
assert.equal(calls.length, 1);
|
||||
|
||||
const url = new URL(String(calls[0]));
|
||||
assert.equal(url.origin, "https://api.example.test");
|
||||
assert.equal(url.pathname, "/api/v2/organizations/");
|
||||
assert.equal(url.searchParams.get("page"), "2");
|
||||
assert.equal(url.searchParams.get("page_size"), "50");
|
||||
assert.equal(url.searchParams.get("ordering"), "-name");
|
||||
assert.equal(url.searchParams.get("has_registry"), "false");
|
||||
assert.deepEqual(url.searchParams.getAll("data"), ["industrial", "fns_reports"]);
|
||||
assert.equal(url.searchParams.get("exclude_data_sources"), "vacancies");
|
||||
assert.equal(url.searchParams.get("has_vacancies"), "true");
|
||||
assert.equal(url.searchParams.get("has_trudvsem"), "false");
|
||||
});
|
||||
|
||||
test("getOrganization requests detail endpoint and returns typed source data", async () => {
|
||||
const calls: RequestInfo[] = [];
|
||||
const uid = "5dc142b3-dcf4-4b90-807a-a80e883dd05c";
|
||||
const payload: Organization = {
|
||||
uid,
|
||||
name: "ООО \"Данные\"",
|
||||
normalized_name: "ООО \"Данные\"",
|
||||
inn: "7777777777",
|
||||
kpp: "777701001",
|
||||
ogrn: "1027700132777",
|
||||
ogrip: "",
|
||||
registries: [
|
||||
{
|
||||
id: "0b85bf08-c6d9-4c07-98c3-a057630e3a35",
|
||||
name: "Росатом ГОЗ",
|
||||
},
|
||||
],
|
||||
data_sources: [
|
||||
{
|
||||
source: "industrial",
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
source: "fns_reports",
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
data: {
|
||||
industrial: [
|
||||
{
|
||||
id: 1,
|
||||
load_batch: 10,
|
||||
issue_date: "01.01.2025",
|
||||
issue_date_normalized: "2025-01-01",
|
||||
certificate_number: "CERT-1",
|
||||
expiry_date: "",
|
||||
expiry_date_normalized: null,
|
||||
certificate_file_url: "https://example.test/cert.pdf",
|
||||
organisation_name: "ООО \"Данные\"",
|
||||
inn: "7777777777",
|
||||
ogrn: "1027700132777",
|
||||
registry_organization: null,
|
||||
created_at: "2026-05-01T10:00:00Z",
|
||||
updated_at: "2026-05-01T10:00:00Z",
|
||||
},
|
||||
],
|
||||
fns_reports: [
|
||||
{
|
||||
id: 2,
|
||||
external_id: "fin-1",
|
||||
ogrn: "1027700132777",
|
||||
registry_organization: null,
|
||||
file_name: "fin.xlsx",
|
||||
file_hash: "a".repeat(64),
|
||||
load_batch: 11,
|
||||
status: "success",
|
||||
source: "api",
|
||||
error_message: "",
|
||||
created_at: "2026-05-01T10:00:00Z",
|
||||
updated_at: "2026-05-01T10:00:00Z",
|
||||
lines_count: 1,
|
||||
lines: {
|
||||
"2024": {
|
||||
active: {
|
||||
"1100": {
|
||||
form_code: "1",
|
||||
name: "Внеоборотные активы",
|
||||
period_start: 100,
|
||||
period_end: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const fetchImpl: FetchLike = async (input) => {
|
||||
calls.push(input);
|
||||
return jsonResponse(payload);
|
||||
};
|
||||
const client = new MostovikOrganizationsClient({
|
||||
baseUrl: "https://api.example.test",
|
||||
fetch: fetchImpl,
|
||||
});
|
||||
|
||||
const organization = await client.getOrganization(uid, {
|
||||
data_sources: ["industrial", "fns_reports"],
|
||||
});
|
||||
|
||||
assert.equal(organization.uid, uid);
|
||||
assert.equal(organization.data.industrial?.[0]?.certificate_number, "CERT-1");
|
||||
assert.equal(
|
||||
organization.data.fns_reports?.[0]?.lines["2024"]?.active?.["1100"]?.period_end,
|
||||
200,
|
||||
);
|
||||
const url = new URL(String(calls[0]));
|
||||
assert.equal(url.pathname, `/api/v2/organizations/${uid}/`);
|
||||
assert.deepEqual(url.searchParams.getAll("data_sources"), [
|
||||
"industrial",
|
||||
"fns_reports",
|
||||
]);
|
||||
});
|
||||
|
||||
test("throws ApiClientError with status and payload for non-2xx responses", async () => {
|
||||
const errorPayload: ApiErrorPayload = {
|
||||
success: false,
|
||||
data: null,
|
||||
errors: [
|
||||
{
|
||||
code: "validation_error",
|
||||
message: "Validation failed",
|
||||
details: {
|
||||
fields: {
|
||||
data: ["Unknown data source(s): unknown"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
request_id: "test-request-id",
|
||||
},
|
||||
};
|
||||
const fetchImpl: FetchLike = async () =>
|
||||
jsonResponse(errorPayload, { status: 400 });
|
||||
const client = new MostovikOrganizationsClient({
|
||||
baseUrl: "https://api.example.test",
|
||||
fetch: fetchImpl,
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
() => client.getOrganization("uid", { data: "industrial" }),
|
||||
(error: unknown) => {
|
||||
assert.equal(error instanceof Error, true);
|
||||
assert.equal((error as { name?: string }).name, "ApiClientError");
|
||||
assert.equal((error as { status?: number }).status, 400);
|
||||
assert.deepEqual((error as { payload?: ApiErrorPayload }).payload, errorPayload);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("generic source payload is represented as a typed JSON object", () => {
|
||||
const payload: GenericSourcePayload = {
|
||||
provider: "checko",
|
||||
target: {
|
||||
inn: "7711111199",
|
||||
ogrn: "1027700132199",
|
||||
},
|
||||
suppliers: [
|
||||
{
|
||||
inn: "7722222299",
|
||||
amount: 1000,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assert.equal(payload.provider, "checko");
|
||||
});
|
||||
Reference in New Issue
Block a user