* fix: accept empty DataForSEO task results Treat successful tasks with null items as empty payloads so empty ranked keyword responses do not fail billing validation. * refactor: simplify DataForSEO null result handling Add .nullable() to the existing structured result schema instead of loosening the type to unknown[] and re-parsing in parseTaskItems. * refactor: simplify backlinks zod parsing with structured result schema Same pattern as the dataforseoSchemas fix: give taskSchema.result a structured type with .items instead of z.unknown(), removing the intermediate resultItemsSchema and two-step parsing in parseItems. * refactor: replace manual type guards with zod schemas - progress-kv.ts: replace isCrawledUrlEntry type guard with a zod schema and use jsonCodec(z.array(...)) instead of parsing unknown then filtering - helpers.ts: tighten normalizeIntent param from unknown to string | null | undefined to match actual call sites - dataforseoBacklinksSupport.ts: allow null result elements to match API responses where result contains [null] * refactor: filter null result elements at the source Filter out null elements from task.result in postBacklinks so downstream functions receive clean BacklinksTaskResult[] instead of (BacklinksTaskResult | null)[]. * save
76 lines
1.9 KiB
TypeScript
76 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
domainRankedKeywordItemSchema,
|
|
parseTaskItems,
|
|
relatedKeywordItemSchema,
|
|
successfulDataforseoTaskSchema,
|
|
} from "@/server/lib/dataforseoSchemas";
|
|
|
|
describe("dataforseoSchemas", () => {
|
|
it("accepts null items for empty successful tasks", () => {
|
|
const task = {
|
|
id: "04042314-1577-0387-0000-33dc4b485cfd",
|
|
status_code: 20000,
|
|
status_message: "Ok.",
|
|
path: ["v3", "dataforseo_labs", "google", "related_keywords", "live"],
|
|
cost: 0.02,
|
|
result_count: 1,
|
|
result: [
|
|
{
|
|
se_type: "google",
|
|
seed_keyword: "canva ai video alternative",
|
|
location_code: 2840,
|
|
language_code: "en",
|
|
total_count: null,
|
|
items_count: 0,
|
|
items: null,
|
|
},
|
|
],
|
|
};
|
|
|
|
const parsedTask = successfulDataforseoTaskSchema.parse(task);
|
|
|
|
expect(
|
|
parseTaskItems(
|
|
"google-related-keywords-live",
|
|
parsedTask,
|
|
relatedKeywordItemSchema,
|
|
),
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("accepts empty ranked keyword tasks with null items", () => {
|
|
const task = {
|
|
id: "04070246-1577-0381-0000-2c56c059f67e",
|
|
status_code: 20000,
|
|
status_message: "Ok.",
|
|
path: ["v3", "dataforseo_labs", "google", "ranked_keywords", "live"],
|
|
cost: 0.01,
|
|
result_count: 1,
|
|
result: [
|
|
{
|
|
se_type: "google",
|
|
target: "openseo.so",
|
|
location_code: 2840,
|
|
language_code: "en",
|
|
total_count: null,
|
|
items_count: 0,
|
|
metrics: null,
|
|
metrics_absolute: null,
|
|
items: null,
|
|
},
|
|
],
|
|
};
|
|
|
|
const parsedTask = successfulDataforseoTaskSchema.parse(task);
|
|
|
|
expect(
|
|
parseTaskItems(
|
|
"google-ranked-keywords-live",
|
|
parsedTask,
|
|
domainRankedKeywordItemSchema,
|
|
),
|
|
).toEqual([]);
|
|
});
|
|
});
|