test(seo): guard demand orchestration coverage
This commit is contained in:
parent
2a0e72543e
commit
c3a946879b
|
|
@ -8,8 +8,15 @@ function getNumericConstant(source: string, name: string) {
|
|||
|
||||
const wordstatRepositorySource = await readFile(new URL("../market/wordstatRepository.ts", import.meta.url), "utf8");
|
||||
const marketEnrichmentSource = await readFile(new URL("../market/marketEnrichment.ts", import.meta.url), "utf8");
|
||||
const marketFrontierDiscoverySource = await readFile(new URL("../market/marketFrontierDiscovery.ts", import.meta.url), "utf8");
|
||||
const wordstatProviderSource = await readFile(new URL("../market/wordstatProvider.ts", import.meta.url), "utf8");
|
||||
const anchorReviewSource = await readFile(new URL("../keywords/anchorReview.ts", import.meta.url), "utf8");
|
||||
const keywordAnalysisWorkflowSource = await readFile(new URL("../keywords/keywordAnalysisWorkflow.ts", import.meta.url), "utf8");
|
||||
const keywordCleaningSource = await readFile(new URL("../keywords/keywordCleaning.ts", import.meta.url), "utf8");
|
||||
const keywordContextSnapshotsSource = await readFile(new URL("../keywords/keywordContextSnapshots.ts", import.meta.url), "utf8");
|
||||
const keywordMapSource = await readFile(new URL("../keywords/keywordMap.ts", import.meta.url), "utf8");
|
||||
const yandexEvidenceSource = await readFile(new URL("../evidence/yandexEvidence.ts", import.meta.url), "utf8");
|
||||
const modelProviderRegistrySource = await readFile(new URL("../modelProvider/modelProviderRegistry.ts", import.meta.url), "utf8");
|
||||
const projectRoutesSource = await readFile(new URL("../projects/routes.ts", import.meta.url), "utf8");
|
||||
const materializationSource = await readFile(new URL("../rewrite/materialization.ts", import.meta.url), "utf8");
|
||||
const rewritePlanSource = await readFile(new URL("../rewrite/rewritePlan.ts", import.meta.url), "utf8");
|
||||
|
|
@ -24,6 +31,108 @@ assert.ok(
|
|||
wordstatRepositorySource.includes("WORDSTAT_TOP_RESULTS_PER_SOURCE_LIMIT"),
|
||||
"Wordstat top results must keep per-source balancing, not only global frequency sorting."
|
||||
);
|
||||
assert.ok(
|
||||
(getNumericConstant(wordstatRepositorySource, "WORDSTAT_MAX_SAFE_SEEDS_PER_COLLECTION") ?? 99) <= 16,
|
||||
"Wordstat collection must keep a hard backend safety cap below the old expensive 40-query pass."
|
||||
);
|
||||
assert.ok(
|
||||
wordstatRepositorySource.includes("getRequestedWordstatPhraseSet") &&
|
||||
wordstatRepositorySource.includes("alreadyTriedSeedPhrases") &&
|
||||
wordstatRepositorySource.includes("requested_phrases"),
|
||||
"Wordstat collection must remember already requested phrases, including zero-result probes, before spending quota again."
|
||||
);
|
||||
assert.ok(
|
||||
(getNumericConstant(marketEnrichmentSource, "DEMAND_COLLECTION_CONTEXTUAL_PROBE_BUDGET") ?? 99) <= 8 &&
|
||||
(getNumericConstant(marketEnrichmentSource, "DEMAND_COLLECTION_MARKET_WIDE_PROBE_BUDGET") ?? 99) <= 12 &&
|
||||
marketEnrichmentSource.includes("selectMarketWideWordstatProbeSeeds") &&
|
||||
marketEnrichmentSource.includes("getReusableWordstatEvidencePhraseSet") &&
|
||||
marketEnrichmentSource.includes("getRequestedWordstatPhraseSet"),
|
||||
"Market enrichment must use small ranked Wordstat probe batches and reuse requested/collected evidence instead of fan-out seed generation."
|
||||
);
|
||||
assert.ok(
|
||||
wordstatProviderSource.includes("topRequests") &&
|
||||
wordstatProviderSource.includes("popularQueries") &&
|
||||
wordstatProviderSource.includes("isRealWordstatPhrase"),
|
||||
"Wordstat provider normalization must preserve top/related/popular rows and reject n/a-like garbage phrases."
|
||||
);
|
||||
assert.ok(
|
||||
marketFrontierDiscoverySource.includes('schemaVersion: "market-research-memory.v1"') &&
|
||||
marketFrontierDiscoverySource.includes('schemaVersion: "query-language-fingerprint.v1"') &&
|
||||
marketFrontierDiscoverySource.includes('taskType: "seo.market_frontier_discovery"') &&
|
||||
marketFrontierDiscoverySource.includes("getMarketFrontierMemoryHash") &&
|
||||
marketFrontierDiscoverySource.includes("requestedSeeds") &&
|
||||
marketFrontierDiscoverySource.includes("suppressedPhrases") &&
|
||||
marketFrontierDiscoverySource.includes("mapMarketFrontierProbeSeedsToWordstatCandidates"),
|
||||
"Market-wide demand collection must keep the frontier discovery layer: market memory, query-language fingerprint, model task, and Wordstat probe mapping."
|
||||
);
|
||||
assert.ok(
|
||||
marketEnrichmentSource.includes("getMarketFrontierDiscoveryContract") &&
|
||||
marketEnrichmentSource.includes('"market_frontier_discovery"') &&
|
||||
marketEnrichmentSource.includes("mapMarketFrontierProbeSeedsToWordstatCandidates"),
|
||||
"Market enrichment must route market frontier probe seeds into the ranked Wordstat seed queue."
|
||||
);
|
||||
assert.ok(
|
||||
marketEnrichmentSource.includes('schemaVersion: "wordstat-probe-plan-preview.v1"') &&
|
||||
marketEnrichmentSource.includes("getWordstatProbePlanPreview") &&
|
||||
marketEnrichmentSource.includes("preview_only") &&
|
||||
marketEnrichmentSource.includes("modelCanCallWordstat: false") &&
|
||||
marketEnrichmentSource.includes("buildWordstatProbePlanWarnings") &&
|
||||
marketEnrichmentSource.includes("getProbePatternFamily") &&
|
||||
marketEnrichmentSource.includes("weak_generic_probes") &&
|
||||
marketEnrichmentSource.includes("low_diversity"),
|
||||
"Market enrichment must expose a read-only Wordstat probe preview and critic before spending quota."
|
||||
);
|
||||
assert.ok(
|
||||
projectRoutesSource.includes("/market-enrichment/probe-preview") &&
|
||||
appApiSource.includes("fetchWordstatProbePlanPreview") &&
|
||||
appApiSource.includes("WordstatProbePlanWarning") &&
|
||||
!appSource.includes("wordstat-probe-preview"),
|
||||
"Backend/API must keep read-only Wordstat probe preview available, but Stage 3 UI must not render the debug preview panel."
|
||||
);
|
||||
assert.ok(
|
||||
keywordContextSnapshotsSource.includes("keywordPhrases") &&
|
||||
appApiSource.includes("keywordPhrases: string[]") &&
|
||||
appSource.includes("getKeywordCurationFreshness") &&
|
||||
appSource.includes("keyword-curation-freshness") &&
|
||||
appSource.includes("расширено:"),
|
||||
"Stage 3 must mark old/new keyword cards from expansion checkpoints and show total/updated/expanded counts in the header."
|
||||
);
|
||||
assert.ok(
|
||||
projectRoutesSource.includes("/keyword-context-snapshots/:snapshotId/curation") &&
|
||||
appApiSource.includes("updateKeywordContextSnapshotCuration") &&
|
||||
appSource.includes("saveKeywordCurationLayoutToActiveSnapshot") &&
|
||||
appSource.includes("savingKeywordCurationLayoutProjectId") &&
|
||||
appSource.includes("itemIds: changedItemIds") &&
|
||||
appSource.includes("Сохранить как новый профиль контекста ключей") &&
|
||||
appSource.includes("Сохранить текущую раскладку ключей в выбранный профиль"),
|
||||
"Keyword context Save and Save As must stay separate: topbar creates a snapshot profile, Stage 3 save persists active curation layout and keyword decisions."
|
||||
);
|
||||
assert.ok(
|
||||
keywordCleaningSource.includes("hasEducationIntentMismatch") &&
|
||||
keywordCleaningSource.includes("education_intent_not_in_product_offer") &&
|
||||
keywordMapSource.includes("hasEducationIntentMismatch") &&
|
||||
keywordMapSource.includes("isEditorialOrLocalTailKeywordCandidate"),
|
||||
"Keyword cleaning/map must keep education-intent mismatch out of product lanes and preserve real editorial/local long-tail roles."
|
||||
);
|
||||
assert.ok(
|
||||
keywordAnalysisWorkflowSource.includes("ensureMarketFrontierDiscoveryReady") &&
|
||||
keywordAnalysisWorkflowSource.includes('providerId: "codex_workspace"') &&
|
||||
keywordAnalysisWorkflowSource.includes('taskType: "seo.market_frontier_discovery"') &&
|
||||
keywordAnalysisWorkflowSource.includes('contract.activeStepId === "demand_collection"'),
|
||||
"Market-wide workflow must run/await frontier discovery before spending Wordstat requests."
|
||||
);
|
||||
assert.ok(
|
||||
(getNumericConstant(keywordCleaningSource, "KEYWORD_CLEANING_MODEL_SEED_QUEUE_LIMIT") ?? 999) <= 160 &&
|
||||
(getNumericConstant(keywordCleaningSource, "KEYWORD_CLEANING_MODEL_TOP_RESULT_LIMIT") ?? 999) <= 140 &&
|
||||
keywordCleaningSource.includes("truncateTaskText") &&
|
||||
keywordCleaningSource.includes("getKeywordCleaningModelSeedScore") &&
|
||||
keywordCleaningSource.includes("getKeywordCleaningModelTopResultScore"),
|
||||
"Keyword cleaning model task must keep a slim ranked payload so AI Workspace does not fail with request entity too large."
|
||||
);
|
||||
assert.ok(
|
||||
modelProviderRegistrySource.includes('"seo.market_frontier_discovery"'),
|
||||
"Model provider registry must expose seo.market_frontier_discovery as a runnable task type."
|
||||
);
|
||||
assert.ok(
|
||||
(getNumericConstant(yandexEvidenceSource, "DEFAULT_PHRASE_LIMIT") ?? 0) >= 18,
|
||||
"Yandex Evidence default phrase limit must not fall back to toy 5-phrase coverage."
|
||||
|
|
|
|||
Loading…
Reference in New Issue