@amitgaikwad37/api-contract-drift-detector 0.1.0-beta.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # API Contract Drift Detector
2
+
3
+ ## Overview
4
+
5
+ Detect drift between OpenAPI specs and backend implementations before it reaches production.
6
+
7
+ ## Installation
8
+
9
+ ~~~bash
10
+ npm install @public-sdk/api-contract-drift-detector
11
+ ~~~
12
+
13
+ ## Quick Start
14
+
15
+ ~~~bash
16
+ npx drift-check --help
17
+ ~~~
18
+
19
+ ## Integration Example
20
+
21
+ 1. Add this SDK to your CI workflow or local tooling script.
22
+ 2. Run the command against your project inputs.
23
+ 3. Fail the pipeline on non-zero exit code to enforce quality gates.
24
+
25
+ ~~~bash
26
+ npx drift-check --openapi ./examples/openapi.yaml --backend ./examples/backend.yaml --json
27
+ ~~~
28
+
29
+ ## Typical Output
30
+
31
+ ~~~json
32
+ {
33
+ "command": "drift-check",
34
+ "summary": "2 warnings detected",
35
+ "stats": {
36
+ "totalEndpoints": 18,
37
+ "errors": 0,
38
+ "warnings": 2
39
+ }
40
+ }
41
+ ~~~
42
+
43
+ ## Local Development
44
+
45
+ ~~~bash
46
+ npm ci
47
+ npm run build
48
+ npm test
49
+ ~~~
50
+
51
+ ## License
52
+
53
+ MIT
@@ -0,0 +1,44 @@
1
+ # Implementation Plan - API Contract Drift Detector
2
+
3
+ ## Phase 0: Discovery and Scope (Week 1)
4
+
5
+ - Validate user problem with 5-10 real-world examples.
6
+ - Define MVP boundary and out-of-scope items.
7
+ - Produce architecture sketch and data flow.
8
+
9
+ ## Phase 1: Foundation (Week 1-2)
10
+
11
+ - Initialize project structure and coding standards.
12
+ - Implement configuration loader and CLI parsing.
13
+ - Add logging, error handling, and output formatter.
14
+
15
+ ## Phase 2: Core Engine (Week 2-4)
16
+
17
+ - Implement the primary analysis/generation capability.
18
+ - Add rule engine or model orchestration layer.
19
+ - Build deterministic fixtures for regression safety.
20
+
21
+ ## Phase 3: Integrations (Week 4-5)
22
+
23
+ - Add GitHub Action workflow support.
24
+ - Add JSON/SARIF output when relevant.
25
+ - Add optional webhook or notification adapters.
26
+
27
+ ## Phase 4: Quality and Hardening (Week 5-6)
28
+
29
+ - Add unit, integration, and snapshot tests.
30
+ - Benchmark performance on representative repositories.
31
+ - Improve diagnostics and false-positive handling.
32
+
33
+ ## Phase 5: Documentation and Release (Week 6)
34
+
35
+ - Publish getting-started guide and examples.
36
+ - Add migration/upgrade notes for future versions.
37
+ - Release v0.1.0 and collect feedback.
38
+
39
+ ## Deliverables
40
+
41
+ - Runnable CLI/SDK/Action artifact.
42
+ - CI-ready examples and sample configs.
43
+ - Stable contract for outputs and exit codes.
44
+ - Public roadmap for v0.2+ features.
@@ -0,0 +1,60 @@
1
+ # Requirements - Api Contract Drift Detector
2
+
3
+ ## 1. Product Requirements
4
+
5
+ ### 1.1 Problem Statement
6
+
7
+ Define the core pain point this project solves and how teams currently suffer due to manual, error-prone, or fragmented workflows.
8
+
9
+ ### 1.2 Objectives
10
+
11
+ - Reduce engineering effort for the target workflow.
12
+ - Improve reliability and consistency of delivery.
13
+ - Provide CI-friendly and developer-friendly outputs.
14
+
15
+ ### 1.3 Success Metrics
16
+
17
+ - Adoption: stars, forks, downloads, active users.
18
+ - Efficiency: measurable time saved for key workflows.
19
+ - Quality: reduction in defects or drift incidents.
20
+
21
+ ### 1.4 Target Audience
22
+
23
+ - Small to medium engineering teams
24
+ - API-first companies
25
+ - Enterprise engineering organizations
26
+
27
+ ## 2. Functional Requirements
28
+
29
+ - Provide a primary command/API for the core workflow.
30
+ - Support configuration via file and CLI flags.
31
+ - Emit human-readable and machine-readable results (e.g., JSON).
32
+ - Return deterministic exit codes for CI integration.
33
+ - Provide a dry-run mode where applicable.
34
+
35
+ ## 3. Non-Functional Requirements
36
+
37
+ - Performance: complete typical runs within acceptable CI time budgets.
38
+ - Reliability: deterministic behavior for the same inputs.
39
+ - Security: avoid leaking sensitive data in logs and outputs.
40
+ - Extensibility: plugin/hooks design for custom rules or providers.
41
+ - Usability: concise, actionable messaging and docs.
42
+
43
+ ## 4. Integrations
44
+
45
+ - GitHub Actions support.
46
+ - Optional integration with issue trackers and chat notifications.
47
+ - Optional telemetry (opt-in) for usage insights.
48
+
49
+ ## 5. Constraints and Risks
50
+
51
+ - Fast-changing frameworks and ecosystem compatibility.
52
+ - Risk of false positives in AI-assisted analysis.
53
+ - Need for clear explainability in automated decisions.
54
+
55
+ ## 6. Acceptance Criteria
56
+
57
+ - Core workflow works end-to-end with sample fixtures.
58
+ - CI integration example passes in a reference repository.
59
+ - Documentation includes quickstart, config, and troubleshooting.
60
+ - Baseline tests cover critical paths and edge cases.
@@ -0,0 +1,118 @@
1
+ # v0.1 Engineering Backlog - API Contract Drift Detector
2
+
3
+ ## 1. Release Objective
4
+
5
+ - Product Type: CLI
6
+ - Primary Command/API: drift-check
7
+ - v0.1 Goal: Detect drift between OpenAPI, backend routes, generated SDKs, and docs examples.
8
+
9
+ ## 2. v0.1 Scope
10
+
11
+ ### In Scope
12
+
13
+ - Implement one reliable end-to-end workflow for the primary command/API.
14
+ - Support local CLI usage and CI-ready machine-readable output.
15
+ - Provide deterministic fixtures and baseline test coverage.
16
+
17
+ ### Out of Scope
18
+
19
+ - Multi-tenant cloud control planes and hosted dashboards.
20
+ - Broad ecosystem plugin marketplace.
21
+ - Advanced enterprise SSO/billing lifecycle automation.
22
+
23
+ ## 3. CLI/API Contract
24
+
25
+ ### Inputs
26
+
27
+ openapi.yaml, backend route metadata, sdk surface metadata, markdown docs
28
+
29
+ ### Outputs
30
+
31
+ terminal report, json report, CI exit code
32
+
33
+ ### Exit Code Policy
34
+
35
+ - 0: successful run with no blocking findings.
36
+ - 1: blocking findings, failed policies, or generation validation errors.
37
+ - 2: invalid arguments/configuration.
38
+ - 3: unexpected internal runtime error.
39
+
40
+ ## 4. Proposed Folder Structure
41
+
42
+ ```text
43
+ project/
44
+ src/
45
+ cli/
46
+ core/
47
+ adapters/
48
+ formatters/
49
+ tests/
50
+ fixtures/
51
+ integration/
52
+ unit/
53
+ examples/
54
+ docs/
55
+ requirements.md
56
+ implementation-plan.md
57
+ v0.1-engineering-backlog.md
58
+ ```
59
+
60
+ ## 5. Epics and Tasks
61
+
62
+ ### Epic 1 - Foundations
63
+
64
+ - Setup language/runtime, linting, formatting, and test harness.
65
+ - Implement config loader and schema validation.
66
+ - Implement logging and output formatter (human + JSON).
67
+ - Define typed domain models for inputs and findings.
68
+
69
+ ### Epic 2 - Core Engine
70
+
71
+ - Implement parser/collector pipeline for source inputs.
72
+ - Implement rule/evaluation engine with deterministic ordering.
73
+ - Implement result normalization and deduplication.
74
+ - Add clear diagnostic messages with file/entity pointers.
75
+
76
+ ### Epic 3 - Integrations
77
+
78
+ - Add CI usage examples and default command presets.
79
+ - Add GitHub workflow/action integration sample.
80
+ - Add JSON artifact emission for downstream automation.
81
+
82
+ ### Epic 4 - Quality and Release
83
+
84
+ - Build fixture-based integration tests for happy/edge/error paths.
85
+ - Add performance baseline test for representative input size.
86
+ - Add release notes template and semantic version tagging process.
87
+
88
+ ## 6. Acceptance Tests
89
+
90
+ - Core Acceptance Test: Given mismatched OpenAPI and SDK fixtures, command reports missing endpoints and schema mismatches with non-zero exit.
91
+ - Invalid Config Test: malformed config returns exit code 2 and actionable error.
92
+ - Output Contract Test: json output schema remains stable across fixture runs.
93
+ - Regression Test: known fixture continues to produce expected findings/generation.
94
+
95
+ ## 7. Two-Week Execution Plan
96
+
97
+ ### Week 1
98
+
99
+ - Day 1: Finalize v0.1 interface and fixture list.
100
+ - Day 2-3: Implement foundations and parser/collector layer.
101
+ - Day 4-5: Implement first end-to-end core workflow.
102
+
103
+ ### Week 2
104
+
105
+ - Day 6-7: Add remaining critical rules/features for MVP.
106
+ - Day 8: Add integration examples and CI contract checks.
107
+ - Day 9: Harden tests, snapshots, and error handling.
108
+ - Day 10: Ship v0.1.0 release candidate with docs.
109
+
110
+ ## 8. Definition of Done for v0.1
111
+
112
+ - Command/API is usable in local and CI contexts.
113
+ - Output contract is documented and tested.
114
+ - At least 1 reference example is included and reproducible.
115
+ - Known high-priority edge cases are covered by tests.
116
+ - Release notes include limitations and v0.2 roadmap themes.
117
+
118
+
@@ -0,0 +1,18 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ export default [
5
+ js.configs.recommended,
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ files: ["**/*.ts"],
9
+ languageOptions: {
10
+ parserOptions: {
11
+ project: false
12
+ }
13
+ },
14
+ rules: {
15
+ "no-console": "off"
16
+ }
17
+ }
18
+ ];
@@ -0,0 +1,21 @@
1
+ # API Contract Drift Detector Examples
2
+
3
+ ## CLI Example
4
+
5
+ Run this command from your project root:
6
+
7
+ ~~~bash
8
+ npx drift-check --openapi ./examples/openapi.yaml --backend ./examples/backend.yaml --json
9
+ ~~~
10
+
11
+ ## CI Example (GitHub Actions)
12
+
13
+ ~~~yaml
14
+ - name: Run API Contract Drift Detector
15
+ run: npx drift-check --openapi ./examples/openapi.yaml --backend ./examples/backend.yaml --json
16
+ ~~~
17
+
18
+ ## Notes
19
+
20
+ - Keep example inputs small and deterministic.
21
+ - Commit expected outputs when you want regression visibility in pull requests.
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@amitgaikwad37/api-contract-drift-detector",
3
+ "version": "0.1.0-beta.18",
4
+ "type": "module",
5
+ "description": "Api Contract Drift Detector starter implementation scaffold",
6
+ "bin": {
7
+ "drift-check": "dist/cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc -p tsconfig.json",
11
+ "typecheck": "tsc -p tsconfig.json --noEmit",
12
+ "start": "node dist/cli/index.js",
13
+ "dev": "tsx src/cli/index.ts",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "lint": "eslint ."
17
+ },
18
+ "devDependencies": {
19
+ "@eslint/js": "^9.16.0",
20
+ "@types/node": "^22.10.1",
21
+ "eslint": "^9.16.0",
22
+ "tsx": "^4.19.2",
23
+ "typescript": "^5.7.2",
24
+ "typescript-eslint": "^8.18.0",
25
+ "vitest": "^2.1.8"
26
+ },
27
+ "dependencies": {
28
+ "yaml": "^2.4.1"
29
+ }
30
+ }
@@ -0,0 +1,18 @@
1
+ import type { BackendRoute } from "../types.js";
2
+ import { readFileSync } from "fs";
3
+ import { parse } from "yaml";
4
+
5
+ export function parseBackendRoutes(filePath: string): BackendRoute[] {
6
+ const content = readFileSync(filePath, "utf-8");
7
+ const spec = parse(content) as any;
8
+
9
+ if (!Array.isArray(spec.routes)) {
10
+ throw new Error("Invalid backend metadata: routes must be an array");
11
+ }
12
+
13
+ return spec.routes.map((route: any) => ({
14
+ path: route.path,
15
+ method: (route.method || "GET").toUpperCase(),
16
+ handler: route.handler || "unknown"
17
+ }));
18
+ }
@@ -0,0 +1,39 @@
1
+ import type { OpenAPISpec, Endpoint } from "../types.js";
2
+ import { readFileSync } from "fs";
3
+ import { parse } from "yaml";
4
+
5
+ export function parseOpenAPI(filePath: string): OpenAPISpec {
6
+ const content = readFileSync(filePath, "utf-8");
7
+ const spec = parse(content) as any;
8
+
9
+ if (!spec.openapi && !spec.swagger) {
10
+ throw new Error("Invalid OpenAPI spec: missing openapi or swagger field");
11
+ }
12
+
13
+ const endpoints: Endpoint[] = [];
14
+ const paths = spec.paths || {};
15
+
16
+ for (const [path, pathItem] of Object.entries(paths)) {
17
+ const item = pathItem as any;
18
+ for (const method of ["get", "post", "put", "delete", "patch", "head"]) {
19
+ if (method in item) {
20
+ const operation = item[method];
21
+ endpoints.push({
22
+ path,
23
+ method: method.toUpperCase(),
24
+ summary: operation.summary,
25
+ parameters: operation.parameters || [],
26
+ requestBody: operation.requestBody,
27
+ responses: operation.responses || {}
28
+ });
29
+ }
30
+ }
31
+ }
32
+
33
+ return {
34
+ version: spec.openapi || spec.swagger,
35
+ title: spec.info?.title || "Unknown",
36
+ endpoints,
37
+ schemas: spec.components?.schemas || spec.definitions || {}
38
+ };
39
+ }
@@ -0,0 +1,82 @@
1
+ import { runCore } from "../core/run-core.js";
2
+ import type { RunOptions } from "../types.js";
3
+
4
+ function printHelp(): void {
5
+ console.log("drift-check - Detect drift between OpenAPI, backend, SDK, and documentation");
6
+ console.log("");
7
+ console.log("Usage:");
8
+ console.log(" drift-check --openapi <path> --backend <path> [--json] [--help]");
9
+ console.log("");
10
+ console.log("Options:");
11
+ console.log(" --openapi <path> Path to OpenAPI YAML file (required)");
12
+ console.log(" --backend <path> Path to backend metadata YAML file (required)");
13
+ console.log(" --json Print JSON output");
14
+ console.log(" --help Show this help message");
15
+ }
16
+
17
+ function parseArgs(args: string[]): RunOptions | null {
18
+ const opts: RunOptions = { json: false };
19
+ let i = 0;
20
+
21
+ while (i < args.length) {
22
+ const arg = args[i];
23
+ if (arg === "--json") {
24
+ opts.json = true;
25
+ i++;
26
+ } else if (arg === "--openapi" && i + 1 < args.length) {
27
+ opts.openapi = args[i + 1];
28
+ i += 2;
29
+ } else if (arg === "--backend" && i + 1 < args.length) {
30
+ opts.backend = args[i + 1];
31
+ i += 2;
32
+ } else {
33
+ i++;
34
+ }
35
+ }
36
+
37
+ if (!opts.openapi || !opts.backend) {
38
+ return null;
39
+ }
40
+
41
+ return opts;
42
+ }
43
+
44
+ function main(): void {
45
+ const args = process.argv.slice(2);
46
+
47
+ if (args.length === 0 || args.includes("--help")) {
48
+ printHelp();
49
+ process.exit(args.length === 0 ? 2 : 0);
50
+ }
51
+
52
+ const opts = parseArgs(args);
53
+ if (!opts) {
54
+ console.error("Error: --openapi and --backend are required");
55
+ printHelp();
56
+ process.exit(2);
57
+ }
58
+
59
+ const result = runCore(opts);
60
+
61
+ if (opts.json) {
62
+ console.log(JSON.stringify(result, null, 2));
63
+ } else {
64
+ console.log(`[${result.command}] ${result.summary}`);
65
+ console.log(`\nStats: ${result.stats.totalEndpoints} endpoints, ${result.stats.errors} errors, ${result.stats.warnings} warnings\n`);
66
+
67
+ if (result.findings.length > 0) {
68
+ for (const finding of result.findings) {
69
+ const icon = finding.severity === "error" ? "❌" : "⚠️";
70
+ console.log(`${icon} [${finding.type}] ${finding.message}`);
71
+ }
72
+ } else {
73
+ console.log("✅ No drift detected!");
74
+ }
75
+ }
76
+
77
+ const exitCode = result.stats.errors > 0 ? 1 : 0;
78
+ process.exit(exitCode);
79
+ }
80
+
81
+ main();
82
+
@@ -0,0 +1,45 @@
1
+ import type { Endpoint, BackendRoute, DriftFinding } from "../types.js";
2
+
3
+ export function compareEndpoints(openapi: Endpoint[], backend: BackendRoute[]): DriftFinding[] {
4
+ const findings: DriftFinding[] = [];
5
+ const backendMap = new Map<string, BackendRoute>();
6
+
7
+ // Build backend route map for fast lookup
8
+ for (const route of backend) {
9
+ backendMap.set(`${route.method}:${route.path}`, route);
10
+ }
11
+
12
+ // Check for missing or mismatched endpoints in backend
13
+ for (const endpoint of openapi) {
14
+ const key = `${endpoint.method}:${endpoint.path}`;
15
+ const backendRoute = backendMap.get(key);
16
+
17
+ if (!backendRoute) {
18
+ findings.push({
19
+ type: "missing_in_backend",
20
+ severity: "error",
21
+ endpoint: endpoint.path,
22
+ method: endpoint.method,
23
+ message: `OpenAPI endpoint not found in backend: ${endpoint.method} ${endpoint.path}`,
24
+ source: "backend"
25
+ });
26
+ }
27
+ }
28
+
29
+ // Check for extra endpoints in backend (not in OpenAPI)
30
+ for (const [key, route] of backendMap.entries()) {
31
+ const found = openapi.some(e => `${e.method}:${e.path}` === key);
32
+ if (!found) {
33
+ findings.push({
34
+ type: "extra_in_sdk",
35
+ severity: "warning",
36
+ endpoint: route.path,
37
+ method: route.method,
38
+ message: `Backend route not documented in OpenAPI: ${route.method} ${route.path}`,
39
+ source: "backend"
40
+ });
41
+ }
42
+ }
43
+
44
+ return findings;
45
+ }
@@ -0,0 +1,78 @@
1
+ import type { RunOptions, RunResult, DriftFinding, OpenAPISpec, BackendRoute } from "../types.js";
2
+ import { parseOpenAPI } from "../adapters/openapi-parser.js";
3
+ import { parseBackendRoutes } from "../adapters/backend-parser.js";
4
+ import { compareEndpoints } from "../core/drift-engine.js";
5
+
6
+ export function runCore(options: RunOptions): RunResult {
7
+ try {
8
+ let findings: DriftFinding[] = [];
9
+ let totalEndpoints = 0;
10
+
11
+ // Parse inputs
12
+ let openapi: OpenAPISpec | null = null;
13
+ let backend: BackendRoute[] = [];
14
+
15
+ if (options.openapi) {
16
+ try {
17
+ openapi = parseOpenAPI(options.openapi);
18
+ totalEndpoints = openapi.endpoints.length;
19
+ } catch (e) {
20
+ findings.push({
21
+ type: "missing_in_backend",
22
+ severity: "error",
23
+ endpoint: "(global)",
24
+ message: `Failed to parse OpenAPI: ${e instanceof Error ? e.message : String(e)}`,
25
+ source: "openapi"
26
+ });
27
+ }
28
+ }
29
+
30
+ if (options.backend) {
31
+ try {
32
+ backend = parseBackendRoutes(options.backend);
33
+ } catch (e) {
34
+ findings.push({
35
+ type: "missing_in_backend",
36
+ severity: "error",
37
+ endpoint: "(global)",
38
+ message: `Failed to parse backend metadata: ${e instanceof Error ? e.message : String(e)}`,
39
+ source: "backend"
40
+ });
41
+ }
42
+ }
43
+
44
+ // Run drift detection
45
+ if (openapi && backend.length > 0) {
46
+ const driftResults = compareEndpoints(openapi.endpoints, backend);
47
+ findings.push(...driftResults);
48
+ }
49
+
50
+ const stats = {
51
+ totalEndpoints,
52
+ drifted: findings.filter(f => f.severity === "error").length,
53
+ errors: findings.filter(f => f.severity === "error").length,
54
+ warnings: findings.filter(f => f.severity === "warning").length
55
+ };
56
+
57
+ const hasErrors = stats.errors > 0;
58
+ const summary = hasErrors
59
+ ? `Found ${stats.drifted} drift issues across ${totalEndpoints} endpoints.`
60
+ : `All ${totalEndpoints} endpoints match specifications.`;
61
+
62
+ return {
63
+ project: "api-contract-drift-detector",
64
+ command: "drift-check",
65
+ summary,
66
+ findings,
67
+ stats
68
+ };
69
+ } catch (e) {
70
+ return {
71
+ project: "api-contract-drift-detector",
72
+ command: "drift-check",
73
+ summary: `Internal error: ${e instanceof Error ? e.message : String(e)}`,
74
+ findings: [],
75
+ stats: { totalEndpoints: 0, drifted: 0, errors: 1, warnings: 0 }
76
+ };
77
+ }
78
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { runCore } from "./core/run-core.js";
2
+ export type { RunOptions, RunResult, DriftFinding, Endpoint } from "./types.js";
3
+ export { parseOpenAPI } from "./adapters/openapi-parser.js";
4
+ export { parseBackendRoutes } from "./adapters/backend-parser.js";
5
+ export { compareEndpoints } from "./core/drift-engine.js";
package/src/types.ts ADDED
@@ -0,0 +1,87 @@
1
+ // ============ INPUT MODELS ============
2
+
3
+ export type Endpoint = {
4
+ path: string;
5
+ method: string;
6
+ summary?: string;
7
+ parameters?: Parameter[];
8
+ requestBody?: RequestBody;
9
+ responses: Record<string, Response>;
10
+ };
11
+
12
+ export type Parameter = {
13
+ name: string;
14
+ in: "query" | "path" | "header" | "cookie";
15
+ required: boolean;
16
+ schema: any;
17
+ };
18
+
19
+ export type RequestBody = {
20
+ required: boolean;
21
+ content: Record<string, { schema: any }>;
22
+ };
23
+
24
+ export type Response = {
25
+ description: string;
26
+ content?: Record<string, { schema: any }>;
27
+ };
28
+
29
+ export type OpenAPISpec = {
30
+ version: string;
31
+ title: string;
32
+ endpoints: Endpoint[];
33
+ schemas: Record<string, any>;
34
+ };
35
+
36
+ export type BackendRoute = {
37
+ path: string;
38
+ method: string;
39
+ handler: string;
40
+ };
41
+
42
+ export type SDKMethod = {
43
+ name: string;
44
+ endpoint: string;
45
+ signature: string;
46
+ };
47
+
48
+ export type DocExample = {
49
+ endpoint: string;
50
+ method: string;
51
+ example: string;
52
+ };
53
+
54
+ // ============ FINDINGS ============
55
+
56
+ export type DriftFinding = {
57
+ type: "missing_in_sdk" | "missing_in_backend" | "schema_mismatch" | "doc_stale" | "extra_in_sdk";
58
+ severity: "error" | "warning";
59
+ endpoint: string;
60
+ method?: string;
61
+ message: string;
62
+ details?: string;
63
+ source: "openapi" | "backend" | "sdk" | "docs";
64
+ };
65
+
66
+ // ============ OUTPUT MODELS ============
67
+
68
+ export type RunResult = {
69
+ project: string;
70
+ command: string;
71
+ summary: string;
72
+ findings: DriftFinding[];
73
+ stats: {
74
+ totalEndpoints: number;
75
+ drifted: number;
76
+ errors: number;
77
+ warnings: number;
78
+ };
79
+ };
80
+
81
+ export type RunOptions = {
82
+ json: boolean;
83
+ openapi?: string;
84
+ backend?: string;
85
+ sdk?: string;
86
+ docs?: string;
87
+ };
@@ -0,0 +1,3 @@
1
+ # Fixtures
2
+
3
+ Add deterministic fixtures for acceptance tests.
@@ -0,0 +1,10 @@
1
+ routes:
2
+ - path: /pets
3
+ method: GET
4
+ handler: ListPets
5
+ - path: /pets/{id}
6
+ method: GET
7
+ handler: GetPetById
8
+ - path: /users
9
+ method: GET
10
+ handler: ListUsers
@@ -0,0 +1,46 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ version: 1.0.0
4
+ title: Pet Store API
5
+ paths:
6
+ /pets:
7
+ get:
8
+ summary: List all pets
9
+ responses:
10
+ '200':
11
+ description: A list of pets
12
+ post:
13
+ summary: Create a pet
14
+ requestBody:
15
+ required: true
16
+ content:
17
+ application/json:
18
+ schema:
19
+ type: object
20
+ properties:
21
+ name:
22
+ type: string
23
+ responses:
24
+ '201':
25
+ description: Pet created
26
+ /pets/{id}:
27
+ get:
28
+ summary: Get a pet by ID
29
+ parameters:
30
+ - name: id
31
+ in: path
32
+ required: true
33
+ schema:
34
+ type: string
35
+ responses:
36
+ '200':
37
+ description: A pet
38
+ components:
39
+ schemas:
40
+ Pet:
41
+ type: object
42
+ properties:
43
+ id:
44
+ type: string
45
+ name:
46
+ type: string
@@ -0,0 +1,3 @@
1
+ # Integration Tests
2
+
3
+ Add end-to-end command tests here as project logic is implemented.
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { runCore } from "../../src/core/run-core.js";
3
+ import { resolve } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
7
+ const fixturesDir = resolve(__dirname, "../fixtures");
8
+
9
+ describe("drift-check core", () => {
10
+ it("returns a basic scaffold result", () => {
11
+ // Verify SDK returns correct structure with command, project name, and stats
12
+ const result = runCore({ json: false });
13
+ expect(result.command).toBe("drift-check");
14
+ expect(result.stats).toBeDefined();
15
+ });
16
+
17
+ it("detects drift between OpenAPI and backend", () => {
18
+ // Verify SDK identifies discrepancies between API spec and actual backend implementation
19
+ // Fixture has 3 defined endpoints; should detect missing/extra implementations
20
+ const result = runCore({
21
+ json: false,
22
+ openapi: `${fixturesDir}/openapi.yaml`,
23
+ backend: `${fixturesDir}/backend.yaml`
24
+ });
25
+
26
+ expect(result.stats.totalEndpoints).toBe(3); // GET /pets, POST /pets, GET /pets/{id}
27
+ expect(result.findings.length).toBeGreaterThan(0);
28
+ expect(result.stats.errors).toBeGreaterThan(0);
29
+ });
30
+
31
+ it("includes missing endpoint in findings", () => {
32
+ // Verify SDK detects POST /pets endpoint in spec but missing in backend implementation
33
+ const result = runCore({
34
+ json: false,
35
+ openapi: `${fixturesDir}/openapi.yaml`,
36
+ backend: `${fixturesDir}/backend.yaml`
37
+ });
38
+
39
+ const missingPost = result.findings.some(
40
+ f => f.type === "missing_in_backend" && f.method === "POST"
41
+ );
42
+ expect(missingPost).toBe(true);
43
+ });
44
+
45
+ it("includes extra endpoint in findings", () => {
46
+ // Verify SDK detects /users endpoint in backend that's not in OpenAPI spec (potential security risk)
47
+ const result = runCore({
48
+ json: false,
49
+ openapi: `${fixturesDir}/openapi.yaml`,
50
+ backend: `${fixturesDir}/backend.yaml`
51
+ });
52
+
53
+ const extraUsers = result.findings.some(
54
+ f => f.endpoint === "/users" && f.severity === "warning"
55
+ );
56
+ expect(extraUsers).toBe(true);
57
+ });
58
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "include": [
4
+ "src/**/*.ts"
5
+ ]
6
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ include: ["tests/**/*.test.ts"]
7
+ }
8
+ });