refactor(test): colocate pure-domain tests and reduce test debt (#471)
* refactor(test): colocate pure-domain tests and reduce test debt Move focused immutable domain/contracts/adapter tests from tests/composition/** beside their production owners under src/** as colocated *.test.ts/*.fixture.ts, and relocate persistence-shaped composition tests under tests/boundary/filesystem/**. Update import paths, the Vitest project topology (shared worker context for pure domain/contracts and recording-port service projects), the test migration ledger, testing docs, and AGENTS.md to match. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * docs: remove test migration ledger The cutover is complete and the ledger is no longer referenced anywhere in source, scripts, or the docs gate. Drop the standalone artifact. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * docs: clean up stale migration artifacts Remove empty directories left by the test migration and replace the "final cutover" language in docs/testing.md with standing PR guidance that no longer references the completed migration. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: repair CI failures from schema, knip, and stale generated artifacts Three root causes fixed: 1. catalog-schemas.mjs: valuesAt only traversed anyOf from Zod's toJSONSchema, not oneOf. The webScreenshotDiffSchema change to a discriminated union produces oneOf, so the product catalog generator could not derive the web_screenshot_diff schema version. Add oneOf traversal. 2. conformancePackage.ts: the test rewrite removed the only imports of ScenarioManifest, ReplayPlan, ShimPlan, and ExpectedEvidence, making them unused exports that knip flags. Remove the dead type aliases. 3. Regenerate stale artifacts (product catalog, MCP tool catalog, skill metadata, conformance manifest) that drifted from the source changes. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
344b9b6dff
commit
e91c8c39c2
302 files changed
+6822
-5781
No files matched your search
@@ -3,33 +3,42 @@ import { posix } from "node:path";
|
||||
|
||||
import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js";
|
||||
|
||||
/** Explicit outcome of bounded, artifact-confined path resolution. */
|
||||
export interface ArtifactPathResolution {
|
||||
type ArtifactPathResolutionContext =
|
||||
| "package-entrypoint"
|
||||
| "filesystem-expression"
|
||||
| "module-specifier"
|
||||
| "html-reference";
|
||||
|
||||
type UnresolvedArtifactPathStatus =
|
||||
| "not-found"
|
||||
| "unavailable"
|
||||
| "external"
|
||||
| "rejected";
|
||||
|
||||
interface ArtifactPathResolutionBase {
|
||||
readonly declared_path: string;
|
||||
readonly resolution_context:
|
||||
| "package-entrypoint"
|
||||
| "filesystem-expression"
|
||||
| "module-specifier"
|
||||
| "html-reference";
|
||||
readonly resolved_path: string | null;
|
||||
readonly resolution_status:
|
||||
| "resolved"
|
||||
| "not-found"
|
||||
| "unavailable"
|
||||
| "external"
|
||||
| "rejected";
|
||||
readonly resolution_context: ArtifactPathResolutionContext;
|
||||
readonly limitations: readonly string[];
|
||||
}
|
||||
|
||||
/** Explicit outcome of bounded, artifact-confined path resolution. */
|
||||
export type ArtifactPathResolution = ArtifactPathResolutionBase &
|
||||
(
|
||||
| {
|
||||
readonly resolved_path: string;
|
||||
readonly resolution_status: "resolved";
|
||||
}
|
||||
| {
|
||||
readonly resolved_path: null;
|
||||
readonly resolution_status: UnresolvedArtifactPathStatus;
|
||||
}
|
||||
);
|
||||
|
||||
/** Values needed to resolve a declaration without filesystem access. */
|
||||
export interface ResolveArtifactPathInput {
|
||||
readonly declaredPath: string;
|
||||
readonly sourcePath: string;
|
||||
readonly context:
|
||||
| "package-entrypoint"
|
||||
| "filesystem-expression"
|
||||
| "module-specifier"
|
||||
| "html-reference";
|
||||
readonly context: ArtifactPathResolutionContext;
|
||||
readonly files: ReadonlyMap<string, JavaScriptArtifactFile>;
|
||||
readonly htmlBaseHref?: string | null;
|
||||
readonly moduleKind?: "import" | "require";
|
||||
@@ -50,11 +59,17 @@ const NODE_BUILTINS = new Set(
|
||||
builtinModules.map((name) => name.replace(/^node:/u, "")),
|
||||
);
|
||||
|
||||
interface CandidateResolution {
|
||||
readonly resolvedPath: string | null;
|
||||
readonly status: ArtifactPathResolution["resolution_status"];
|
||||
readonly limitations: readonly string[];
|
||||
}
|
||||
type CandidateResolution =
|
||||
| {
|
||||
readonly resolvedPath: string;
|
||||
readonly status: "resolved";
|
||||
readonly limitations: readonly string[];
|
||||
}
|
||||
| {
|
||||
readonly resolvedPath: null;
|
||||
readonly status: UnresolvedArtifactPathStatus;
|
||||
readonly limitations: readonly string[];
|
||||
};
|
||||
|
||||
/** Resolve one declaration under its exact syntax/metadata context. */
|
||||
export const resolveArtifactPathByContext = (
|
||||
@@ -72,12 +87,7 @@ const resolveAtDepth = (
|
||||
const confined = confineCandidate(input, candidate);
|
||||
if (typeof confined !== "string") return confined;
|
||||
const resolved = resolveCandidate(input, confined, packageDepth);
|
||||
return outcome(
|
||||
input,
|
||||
resolved.status,
|
||||
resolved.resolvedPath,
|
||||
resolved.limitations,
|
||||
);
|
||||
return outcome(input, resolved);
|
||||
};
|
||||
|
||||
const rejectDeclaration = (
|
||||
@@ -85,17 +95,19 @@ const rejectDeclaration = (
|
||||
): ArtifactPathResolution | null => {
|
||||
const declared = input.declaredPath;
|
||||
if (declared.length === 0)
|
||||
return outcome(input, "rejected", null, ["The declared path is empty."]);
|
||||
return unresolvedOutcome(input, "rejected", [
|
||||
"The declared path is empty.",
|
||||
]);
|
||||
if (declared.length > 4_096)
|
||||
return outcome(input, "rejected", null, [
|
||||
return unresolvedOutcome(input, "rejected", [
|
||||
"The declared path exceeds the 4096-character resolution bound.",
|
||||
]);
|
||||
if (declared.includes("\0") || declared.includes("\\"))
|
||||
return outcome(input, "rejected", null, [
|
||||
return unresolvedOutcome(input, "rejected", [
|
||||
"NUL and backslash path syntax are not admitted for canonical artifact paths.",
|
||||
]);
|
||||
if (/%(?:2e|2f|5c)/iu.test(declared))
|
||||
return outcome(input, "rejected", null, [
|
||||
return unresolvedOutcome(input, "rejected", [
|
||||
"Encoded dot or separator bytes are rejected before artifact path resolution.",
|
||||
]);
|
||||
return null;
|
||||
@@ -114,13 +126,13 @@ const contextualCandidate = (
|
||||
const fileUrl = fileUrlPath(declared);
|
||||
if (fileUrl !== undefined) return fileUrl;
|
||||
if (hasScheme(declared))
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"URL and Node builtin schemes are outside static artifact module resolution.",
|
||||
]);
|
||||
if (!declared.startsWith(".") && !declared.startsWith("/"))
|
||||
return bareModuleCandidate(input, declared);
|
||||
} else if (hasScheme(declared))
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"URL schemes are outside this local artifact path context.",
|
||||
]);
|
||||
const relative = declared.startsWith("/") ? declared.slice(1) : declared;
|
||||
@@ -139,11 +151,11 @@ const bareModuleCandidate = (
|
||||
NODE_BUILTINS.has(packageName) ||
|
||||
declared.startsWith("#")
|
||||
)
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"The bare specifier is a Node builtin, package import map, or invalid package name.",
|
||||
]);
|
||||
if (declared !== packageName)
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"Bare package subpaths remain unresolved unless an exact package-exports subpath model is available.",
|
||||
]);
|
||||
const source = input.files.get(input.sourcePath);
|
||||
@@ -155,7 +167,7 @@ const bareModuleCandidate = (
|
||||
if (directory === "." || directory === "") break;
|
||||
directory = posix.dirname(directory);
|
||||
}
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"No matching bare package was inventoried in an enclosing node_modules directory.",
|
||||
]);
|
||||
};
|
||||
@@ -190,7 +202,7 @@ const htmlCandidate = (
|
||||
): string | ArtifactPathResolution => {
|
||||
const declared = stripQueryAndFragment(input.declaredPath);
|
||||
if (hasScheme(declared))
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"External HTML references are not mapped to local artifact assets.",
|
||||
]);
|
||||
if (declared.startsWith("/")) return declared.slice(1);
|
||||
@@ -198,7 +210,7 @@ const htmlCandidate = (
|
||||
if (base === undefined || base === null || base === "")
|
||||
return posix.join(posix.dirname(input.sourcePath), declared);
|
||||
if (hasScheme(base))
|
||||
return outcome(input, "external", null, [
|
||||
return unresolvedOutcome(input, "external", [
|
||||
"The document base href is external, so its script reference is not a local artifact path.",
|
||||
]);
|
||||
const basePath = base.startsWith("/")
|
||||
@@ -218,7 +230,7 @@ const confineCandidate = (
|
||||
normalized.startsWith("../") ||
|
||||
normalized.startsWith("/")
|
||||
)
|
||||
return outcome(input, "rejected", null, [
|
||||
return unresolvedOutcome(input, "rejected", [
|
||||
"The resolved candidate escapes the canonical artifact root.",
|
||||
]);
|
||||
return normalized === "." ? "" : normalized;
|
||||
@@ -282,11 +294,17 @@ const resolveCandidate = (
|
||||
},
|
||||
packageDepth + 1,
|
||||
);
|
||||
return {
|
||||
resolvedPath: nested.resolved_path,
|
||||
status: nested.resolution_status,
|
||||
limitations: nested.limitations,
|
||||
};
|
||||
return nested.resolution_status === "resolved"
|
||||
? {
|
||||
resolvedPath: nested.resolved_path,
|
||||
status: nested.resolution_status,
|
||||
limitations: nested.limitations,
|
||||
}
|
||||
: {
|
||||
resolvedPath: null,
|
||||
status: nested.resolution_status,
|
||||
limitations: nested.limitations,
|
||||
};
|
||||
};
|
||||
|
||||
const directCandidates = (candidate: string): readonly string[] => [
|
||||
@@ -374,13 +392,27 @@ const stripQueryAndFragment = (value: string): string =>
|
||||
|
||||
const outcome = (
|
||||
input: ResolveArtifactPathInput,
|
||||
status: ArtifactPathResolution["resolution_status"],
|
||||
resolvedPath: string | null,
|
||||
resolution: CandidateResolution,
|
||||
): ArtifactPathResolution =>
|
||||
resolution.status === "resolved"
|
||||
? {
|
||||
declared_path: input.declaredPath,
|
||||
resolution_context: input.context,
|
||||
resolved_path: resolution.resolvedPath,
|
||||
resolution_status: resolution.status,
|
||||
limitations: resolution.limitations,
|
||||
}
|
||||
: {
|
||||
declared_path: input.declaredPath,
|
||||
resolution_context: input.context,
|
||||
resolved_path: null,
|
||||
resolution_status: resolution.status,
|
||||
limitations: resolution.limitations,
|
||||
};
|
||||
|
||||
const unresolvedOutcome = (
|
||||
input: ResolveArtifactPathInput,
|
||||
status: UnresolvedArtifactPathStatus,
|
||||
limitations: readonly string[],
|
||||
): ArtifactPathResolution => ({
|
||||
declared_path: input.declaredPath,
|
||||
resolution_context: input.context,
|
||||
resolved_path: resolvedPath,
|
||||
resolution_status: status,
|
||||
limitations,
|
||||
});
|
||||
): ArtifactPathResolution =>
|
||||
outcome(input, { resolvedPath: null, status, limitations });
|
||||
Reference in new issue
Block a user