perf(test): cache qa scenario catalog

This commit is contained in:
Peter Steinberger
2026-04-20 15:17:07 +01:00
parent 795a8042a1
commit 44030ac4fd

View File

@@ -230,6 +230,9 @@ const QA_SCENARIO_DIR_PATH = "qa/scenarios";
const QA_PACK_FENCE_RE = /```ya?ml qa-pack\r?\n([\s\S]*?)\r?\n```/i;
const QA_SCENARIO_FENCE_RE = /```ya?ml qa-scenario\r?\n([\s\S]*?)\r?\n```/i;
const QA_FLOW_YAML_FENCE_RE = /```ya?ml qa-flow\r?\n([\s\S]*?)\r?\n```/i;
const repoPathCache = new Map<string, string | null>();
let qaScenarioMarkdownPathsCache: string[] | null = null;
let qaScenarioPackCache: QaScenarioPack | null = null;
function walkUpDirectories(start: string): string[] {
const roots: string[] = [];
@@ -245,6 +248,10 @@ function walkUpDirectories(start: string): string[] {
}
function resolveRepoPath(relativePath: string, kind: "file" | "directory" = "file"): string | null {
const cacheKey = `${kind}:${relativePath}`;
if (repoPathCache.has(cacheKey)) {
return repoPathCache.get(cacheKey) ?? null;
}
for (const dir of walkUpDirectories(import.meta.dirname)) {
const candidate = path.join(dir, relativePath);
if (!fs.existsSync(candidate)) {
@@ -252,9 +259,11 @@ function resolveRepoPath(relativePath: string, kind: "file" | "directory" = "fil
}
const stat = fs.statSync(candidate);
if ((kind === "file" && stat.isFile()) || (kind === "directory" && stat.isDirectory())) {
repoPathCache.set(cacheKey, candidate);
return candidate;
}
}
repoPathCache.set(cacheKey, null);
return null;
}
@@ -320,17 +329,21 @@ export function readQaScenarioPackMarkdown(): string {
}
export function readQaScenarioPack(): QaScenarioPack {
if (qaScenarioPackCache) {
return qaScenarioPackCache;
}
const packMarkdown = readTextFile(QA_SCENARIO_PACK_INDEX_PATH).trim();
if (!packMarkdown) {
// The QA scenario pack is optional in npm distributions. Return an empty
// pack so completion cache updates and other consumers don't crash when
// the qa/scenarios/ directory is not shipped with the package.
return {
qaScenarioPackCache = {
version: 1,
agent: { identityMarkdown: DEFAULT_QA_AGENT_IDENTITY_MARKDOWN },
kickoffTask: "QA scenarios not available in this distribution.",
scenarios: [],
};
return qaScenarioPackCache;
}
const parsedPack = parseQaYamlWithContext(
qaScenarioPackSchema,
@@ -368,18 +381,26 @@ export function readQaScenarioPack(): QaScenarioPack {
}
seenScenarioIds.add(scenario.id);
}
return {
qaScenarioPackCache = {
...parsedPack,
scenarios,
};
return qaScenarioPackCache;
}
export function listQaScenarioMarkdownPaths(): string[] {
if (qaScenarioMarkdownPathsCache) {
return qaScenarioMarkdownPathsCache;
}
const resolved = resolveRepoPath(QA_SCENARIO_DIR_PATH, "directory");
if (!resolved) {
return [];
}
return listQaScenarioMarkdownPathsInDirectory(resolved, QA_SCENARIO_DIR_PATH).toSorted();
qaScenarioMarkdownPathsCache = listQaScenarioMarkdownPathsInDirectory(
resolved,
QA_SCENARIO_DIR_PATH,
).toSorted();
return qaScenarioMarkdownPathsCache;
}
function listQaScenarioMarkdownPathsInDirectory(