Files
puaros/packages/guardian/tests/e2e/JSONOutput.e2e.test.ts
imfozilbek 5a43fbf116 test: add comprehensive E2E test suite for v0.7.8
- Add 62 new E2E tests (21 + 22 + 19)
- AnalyzeProject.e2e.test.ts: full pipeline testing
- CLI.e2e.test.ts: CLI smoke tests with process spawning
- JSONOutput.e2e.test.ts: JSON structure validation
- 100% test pass rate achieved (519/519 tests)
- Update ROADMAP.md and CHANGELOG.md
- Bump version to 0.7.8
2025-11-25 17:20:56 +05:00

413 lines
18 KiB
TypeScript

import { describe, it, expect } from "vitest"
import { analyzeProject } from "../../src/api"
import path from "path"
import type {
AnalyzeProjectResponse,
HardcodeViolation,
CircularDependencyViolation,
NamingConventionViolation,
FrameworkLeakViolation,
EntityExposureViolation,
DependencyDirectionViolation,
RepositoryPatternViolation,
AggregateBoundaryViolation,
} from "../../src/api"
describe("JSON Output Format E2E", () => {
const EXAMPLES_DIR = path.join(__dirname, "../../examples")
describe("Response Structure", () => {
it("should return valid JSON structure", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
expect(result).toBeDefined()
expect(typeof result).toBe("object")
const json = JSON.stringify(result)
expect(() => JSON.parse(json)).not.toThrow()
})
it("should include all required top-level fields", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result: AnalyzeProjectResponse = await analyzeProject({ rootDir })
expect(result).toHaveProperty("hardcodeViolations")
expect(result).toHaveProperty("violations")
expect(result).toHaveProperty("circularDependencyViolations")
expect(result).toHaveProperty("namingViolations")
expect(result).toHaveProperty("frameworkLeakViolations")
expect(result).toHaveProperty("entityExposureViolations")
expect(result).toHaveProperty("dependencyDirectionViolations")
expect(result).toHaveProperty("repositoryPatternViolations")
expect(result).toHaveProperty("aggregateBoundaryViolations")
expect(result).toHaveProperty("metrics")
expect(result).toHaveProperty("dependencyGraph")
})
it("should have correct types for all fields", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
expect(Array.isArray(result.hardcodeViolations)).toBe(true)
expect(Array.isArray(result.violations)).toBe(true)
expect(Array.isArray(result.circularDependencyViolations)).toBe(true)
expect(Array.isArray(result.namingViolations)).toBe(true)
expect(Array.isArray(result.frameworkLeakViolations)).toBe(true)
expect(Array.isArray(result.entityExposureViolations)).toBe(true)
expect(Array.isArray(result.dependencyDirectionViolations)).toBe(true)
expect(Array.isArray(result.repositoryPatternViolations)).toBe(true)
expect(Array.isArray(result.aggregateBoundaryViolations)).toBe(true)
expect(typeof result.metrics).toBe("object")
expect(typeof result.dependencyGraph).toBe("object")
})
})
describe("Metrics Structure", () => {
it("should include all metric fields", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
const { metrics } = result
expect(metrics).toHaveProperty("totalFiles")
expect(metrics).toHaveProperty("totalFunctions")
expect(metrics).toHaveProperty("totalImports")
expect(metrics).toHaveProperty("layerDistribution")
expect(typeof metrics.totalFiles).toBe("number")
expect(typeof metrics.totalFunctions).toBe("number")
expect(typeof metrics.totalImports).toBe("number")
expect(typeof metrics.layerDistribution).toBe("object")
})
it("should have non-negative metric values", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
const { metrics } = result
expect(metrics.totalFiles).toBeGreaterThanOrEqual(0)
expect(metrics.totalFunctions).toBeGreaterThanOrEqual(0)
expect(metrics.totalImports).toBeGreaterThanOrEqual(0)
})
})
describe("Hardcode Violation Structure", () => {
it("should have correct structure for hardcode violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture/hardcoded")
const result = await analyzeProject({ rootDir })
if (result.hardcodeViolations.length > 0) {
const violation: HardcodeViolation = result.hardcodeViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("line")
expect(violation).toHaveProperty("column")
expect(violation).toHaveProperty("type")
expect(violation).toHaveProperty("value")
expect(violation).toHaveProperty("context")
expect(violation).toHaveProperty("suggestion")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.line).toBe("number")
expect(typeof violation.column).toBe("number")
expect(typeof violation.type).toBe("string")
expect(typeof violation.context).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Circular Dependency Violation Structure", () => {
it("should have correct structure for circular dependency violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture/circular")
const result = await analyzeProject({ rootDir })
if (result.circularDependencyViolations.length > 0) {
const violation: CircularDependencyViolation =
result.circularDependencyViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("cycle")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(Array.isArray(violation.cycle)).toBe(true)
expect(violation.cycle.length).toBeGreaterThanOrEqual(2)
expect(typeof violation.severity).toBe("string")
expect(violation.severity).toBe("critical")
}
})
})
describe("Naming Convention Violation Structure", () => {
it("should have correct structure for naming violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture/naming")
const result = await analyzeProject({ rootDir })
if (result.namingViolations.length > 0) {
const violation: NamingConventionViolation = result.namingViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("fileName")
expect(violation).toHaveProperty("expected")
expect(violation).toHaveProperty("actual")
expect(violation).toHaveProperty("layer")
expect(violation).toHaveProperty("message")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.fileName).toBe("string")
expect(typeof violation.expected).toBe("string")
expect(typeof violation.actual).toBe("string")
expect(typeof violation.layer).toBe("string")
expect(typeof violation.message).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Framework Leak Violation Structure", () => {
it("should have correct structure for framework leak violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture/framework-leaks")
const result = await analyzeProject({ rootDir })
if (result.frameworkLeakViolations.length > 0) {
const violation: FrameworkLeakViolation = result.frameworkLeakViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("packageName")
expect(violation).toHaveProperty("category")
expect(violation).toHaveProperty("categoryDescription")
expect(violation).toHaveProperty("layer")
expect(violation).toHaveProperty("message")
expect(violation).toHaveProperty("suggestion")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.packageName).toBe("string")
expect(typeof violation.category).toBe("string")
expect(typeof violation.categoryDescription).toBe("string")
expect(typeof violation.layer).toBe("string")
expect(typeof violation.message).toBe("string")
expect(typeof violation.suggestion).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Entity Exposure Violation Structure", () => {
it("should have correct structure for entity exposure violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture/entity-exposure")
const result = await analyzeProject({ rootDir })
if (result.entityExposureViolations.length > 0) {
const violation: EntityExposureViolation = result.entityExposureViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("entityName")
expect(violation).toHaveProperty("returnType")
expect(violation).toHaveProperty("suggestion")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.entityName).toBe("string")
expect(typeof violation.returnType).toBe("string")
expect(typeof violation.suggestion).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Dependency Direction Violation Structure", () => {
it("should have correct structure for dependency direction violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture/dependency-direction")
const result = await analyzeProject({ rootDir })
if (result.dependencyDirectionViolations.length > 0) {
const violation: DependencyDirectionViolation =
result.dependencyDirectionViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("fromLayer")
expect(violation).toHaveProperty("toLayer")
expect(violation).toHaveProperty("importPath")
expect(violation).toHaveProperty("suggestion")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.fromLayer).toBe("string")
expect(typeof violation.toLayer).toBe("string")
expect(typeof violation.importPath).toBe("string")
expect(typeof violation.suggestion).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Repository Pattern Violation Structure", () => {
it("should have correct structure for repository pattern violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "repository-pattern")
const result = await analyzeProject({ rootDir })
const badViolations = result.repositoryPatternViolations.filter((v) =>
v.file.includes("bad"),
)
if (badViolations.length > 0) {
const violation: RepositoryPatternViolation = badViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("line")
expect(violation).toHaveProperty("violationType")
expect(violation).toHaveProperty("details")
expect(violation).toHaveProperty("suggestion")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.line).toBe("number")
expect(typeof violation.violationType).toBe("string")
expect(typeof violation.details).toBe("string")
expect(typeof violation.suggestion).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Aggregate Boundary Violation Structure", () => {
it("should have correct structure for aggregate boundary violations", async () => {
const rootDir = path.join(EXAMPLES_DIR, "aggregate-boundary/bad")
const result = await analyzeProject({ rootDir })
if (result.aggregateBoundaryViolations.length > 0) {
const violation: AggregateBoundaryViolation = result.aggregateBoundaryViolations[0]
expect(violation).toHaveProperty("file")
expect(violation).toHaveProperty("fromAggregate")
expect(violation).toHaveProperty("toAggregate")
expect(violation).toHaveProperty("entityName")
expect(violation).toHaveProperty("importPath")
expect(violation).toHaveProperty("suggestion")
expect(violation).toHaveProperty("severity")
expect(typeof violation.file).toBe("string")
expect(typeof violation.fromAggregate).toBe("string")
expect(typeof violation.toAggregate).toBe("string")
expect(typeof violation.entityName).toBe("string")
expect(typeof violation.importPath).toBe("string")
expect(typeof violation.suggestion).toBe("string")
expect(typeof violation.severity).toBe("string")
}
})
})
describe("Dependency Graph Structure", () => {
it("should have dependency graph object", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
const { dependencyGraph } = result
expect(dependencyGraph).toBeDefined()
expect(typeof dependencyGraph).toBe("object")
})
it("should have getAllNodes method on dependency graph", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
const { dependencyGraph } = result
expect(typeof dependencyGraph.getAllNodes).toBe("function")
const nodes = dependencyGraph.getAllNodes()
expect(Array.isArray(nodes)).toBe(true)
})
})
describe("JSON Serialization", () => {
it("should serialize metrics without data loss", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
const json = JSON.stringify(result.metrics)
const parsed = JSON.parse(json)
expect(parsed.totalFiles).toBe(result.metrics.totalFiles)
expect(parsed.totalFunctions).toBe(result.metrics.totalFunctions)
expect(parsed.totalImports).toBe(result.metrics.totalImports)
})
it("should serialize violations without data loss", async () => {
const rootDir = path.join(EXAMPLES_DIR, "good-architecture")
const result = await analyzeProject({ rootDir })
const json = JSON.stringify({
hardcodeViolations: result.hardcodeViolations,
violations: result.violations,
})
const parsed = JSON.parse(json)
expect(Array.isArray(parsed.violations)).toBe(true)
expect(Array.isArray(parsed.hardcodeViolations)).toBe(true)
})
it("should serialize violation arrays for large results", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture")
const result = await analyzeProject({ rootDir })
const json = JSON.stringify({
hardcodeViolations: result.hardcodeViolations,
violations: result.violations,
namingViolations: result.namingViolations,
})
expect(json.length).toBeGreaterThan(0)
expect(() => JSON.parse(json)).not.toThrow()
})
})
describe("Severity Levels", () => {
it("should only contain valid severity levels", async () => {
const rootDir = path.join(EXAMPLES_DIR, "bad-architecture")
const result = await analyzeProject({ rootDir })
const validSeverities = ["critical", "high", "medium", "low"]
const allViolations = [
...result.hardcodeViolations,
...result.violations,
...result.circularDependencyViolations,
...result.namingViolations,
...result.frameworkLeakViolations,
...result.entityExposureViolations,
...result.dependencyDirectionViolations,
...result.repositoryPatternViolations,
...result.aggregateBoundaryViolations,
]
allViolations.forEach((violation) => {
if ("severity" in violation) {
expect(validSeverities).toContain(violation.severity)
}
})
})
})
})