mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
test: improve test coverage for domain files from 46-58% to 92-100%
- Add 31 tests for SourceFile.ts (46% → 100%) - Add 31 tests for ProjectPath.ts (50% → 100%) - Add 18 tests for ValueObject.ts (25% → 100%) - Add 32 tests for RepositoryViolation.ts (58% → 92.68%) - Total test count: 345 → 457 tests (all passing) - Overall coverage: 95.4% statements, 86.25% branches, 96.68% functions - Update version to 0.7.7 - Update ROADMAP.md and CHANGELOG.md
This commit is contained in:
329
packages/guardian/tests/unit/domain/SourceFile.test.ts
Normal file
329
packages/guardian/tests/unit/domain/SourceFile.test.ts
Normal file
@@ -0,0 +1,329 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { SourceFile } from "../../../src/domain/entities/SourceFile"
|
||||
import { ProjectPath } from "../../../src/domain/value-objects/ProjectPath"
|
||||
import { LAYERS } from "../../../src/shared/constants/rules"
|
||||
|
||||
describe("SourceFile", () => {
|
||||
describe("constructor", () => {
|
||||
it("should create a SourceFile instance with all properties", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const content = "class User {}"
|
||||
const imports = ["./BaseEntity"]
|
||||
const exports = ["User"]
|
||||
const id = "test-id"
|
||||
|
||||
const sourceFile = new SourceFile(path, content, imports, exports, id)
|
||||
|
||||
expect(sourceFile.path).toBe(path)
|
||||
expect(sourceFile.content).toBe(content)
|
||||
expect(sourceFile.imports).toEqual(imports)
|
||||
expect(sourceFile.exports).toEqual(exports)
|
||||
expect(sourceFile.id).toBe(id)
|
||||
})
|
||||
|
||||
it("should create a SourceFile with empty imports and exports by default", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const content = "class User {}"
|
||||
|
||||
const sourceFile = new SourceFile(path, content)
|
||||
|
||||
expect(sourceFile.imports).toEqual([])
|
||||
expect(sourceFile.exports).toEqual([])
|
||||
})
|
||||
|
||||
it("should generate an id if not provided", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const content = "class User {}"
|
||||
|
||||
const sourceFile = new SourceFile(path, content)
|
||||
|
||||
expect(sourceFile.id).toBeDefined()
|
||||
expect(typeof sourceFile.id).toBe("string")
|
||||
expect(sourceFile.id.length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("layer detection", () => {
|
||||
it("should detect domain layer from path", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/entities/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBe(LAYERS.DOMAIN)
|
||||
})
|
||||
|
||||
it("should detect application layer from path", () => {
|
||||
const path = ProjectPath.create(
|
||||
"/project/src/application/use-cases/CreateUser.ts",
|
||||
"/project",
|
||||
)
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBe(LAYERS.APPLICATION)
|
||||
})
|
||||
|
||||
it("should detect infrastructure layer from path", () => {
|
||||
const path = ProjectPath.create(
|
||||
"/project/src/infrastructure/database/UserRepository.ts",
|
||||
"/project",
|
||||
)
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBe(LAYERS.INFRASTRUCTURE)
|
||||
})
|
||||
|
||||
it("should detect shared layer from path", () => {
|
||||
const path = ProjectPath.create("/project/src/shared/utils/helpers.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBe(LAYERS.SHARED)
|
||||
})
|
||||
|
||||
it("should return undefined for unknown layer", () => {
|
||||
const path = ProjectPath.create("/project/src/unknown/Test.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should handle uppercase layer names in path", () => {
|
||||
const path = ProjectPath.create("/project/src/DOMAIN/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBe(LAYERS.DOMAIN)
|
||||
})
|
||||
|
||||
it("should handle mixed case layer names in path", () => {
|
||||
const path = ProjectPath.create("/project/src/Application/UseCase.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.layer).toBe(LAYERS.APPLICATION)
|
||||
})
|
||||
})
|
||||
|
||||
describe("path getter", () => {
|
||||
it("should return the project path", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.path).toBe(path)
|
||||
})
|
||||
})
|
||||
|
||||
describe("content getter", () => {
|
||||
it("should return the file content", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const content = "class User { constructor(public name: string) {} }"
|
||||
const sourceFile = new SourceFile(path, content)
|
||||
|
||||
expect(sourceFile.content).toBe(content)
|
||||
})
|
||||
})
|
||||
|
||||
describe("imports getter", () => {
|
||||
it("should return a copy of imports array", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const imports = ["./BaseEntity", "./ValueObject"]
|
||||
const sourceFile = new SourceFile(path, "", imports)
|
||||
|
||||
const returnedImports = sourceFile.imports
|
||||
|
||||
expect(returnedImports).toEqual(imports)
|
||||
expect(returnedImports).not.toBe(imports)
|
||||
})
|
||||
|
||||
it("should not allow mutations of internal imports array", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const imports = ["./BaseEntity"]
|
||||
const sourceFile = new SourceFile(path, "", imports)
|
||||
|
||||
const returnedImports = sourceFile.imports
|
||||
returnedImports.push("./NewImport")
|
||||
|
||||
expect(sourceFile.imports).toEqual(["./BaseEntity"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("exports getter", () => {
|
||||
it("should return a copy of exports array", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const exports = ["User", "UserProps"]
|
||||
const sourceFile = new SourceFile(path, "", [], exports)
|
||||
|
||||
const returnedExports = sourceFile.exports
|
||||
|
||||
expect(returnedExports).toEqual(exports)
|
||||
expect(returnedExports).not.toBe(exports)
|
||||
})
|
||||
|
||||
it("should not allow mutations of internal exports array", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const exports = ["User"]
|
||||
const sourceFile = new SourceFile(path, "", [], exports)
|
||||
|
||||
const returnedExports = sourceFile.exports
|
||||
returnedExports.push("NewExport")
|
||||
|
||||
expect(sourceFile.exports).toEqual(["User"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("addImport", () => {
|
||||
it("should add a new import to the list", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
sourceFile.addImport("./BaseEntity")
|
||||
|
||||
expect(sourceFile.imports).toEqual(["./BaseEntity"])
|
||||
})
|
||||
|
||||
it("should not add duplicate imports", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "", ["./BaseEntity"])
|
||||
|
||||
sourceFile.addImport("./BaseEntity")
|
||||
|
||||
expect(sourceFile.imports).toEqual(["./BaseEntity"])
|
||||
})
|
||||
|
||||
it("should update updatedAt timestamp when adding new import", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
const originalUpdatedAt = sourceFile.updatedAt
|
||||
|
||||
setTimeout(() => {
|
||||
sourceFile.addImport("./BaseEntity")
|
||||
|
||||
expect(sourceFile.updatedAt.getTime()).toBeGreaterThanOrEqual(
|
||||
originalUpdatedAt.getTime(),
|
||||
)
|
||||
}, 10)
|
||||
})
|
||||
|
||||
it("should not update timestamp when adding duplicate import", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "", ["./BaseEntity"])
|
||||
|
||||
const originalUpdatedAt = sourceFile.updatedAt
|
||||
|
||||
setTimeout(() => {
|
||||
sourceFile.addImport("./BaseEntity")
|
||||
|
||||
expect(sourceFile.updatedAt).toBe(originalUpdatedAt)
|
||||
}, 10)
|
||||
})
|
||||
|
||||
it("should add multiple different imports", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
sourceFile.addImport("./BaseEntity")
|
||||
sourceFile.addImport("./ValueObject")
|
||||
sourceFile.addImport("./DomainEvent")
|
||||
|
||||
expect(sourceFile.imports).toEqual(["./BaseEntity", "./ValueObject", "./DomainEvent"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("addExport", () => {
|
||||
it("should add a new export to the list", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
sourceFile.addExport("User")
|
||||
|
||||
expect(sourceFile.exports).toEqual(["User"])
|
||||
})
|
||||
|
||||
it("should not add duplicate exports", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "", [], ["User"])
|
||||
|
||||
sourceFile.addExport("User")
|
||||
|
||||
expect(sourceFile.exports).toEqual(["User"])
|
||||
})
|
||||
|
||||
it("should update updatedAt timestamp when adding new export", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
const originalUpdatedAt = sourceFile.updatedAt
|
||||
|
||||
setTimeout(() => {
|
||||
sourceFile.addExport("User")
|
||||
|
||||
expect(sourceFile.updatedAt.getTime()).toBeGreaterThanOrEqual(
|
||||
originalUpdatedAt.getTime(),
|
||||
)
|
||||
}, 10)
|
||||
})
|
||||
|
||||
it("should not update timestamp when adding duplicate export", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "", [], ["User"])
|
||||
|
||||
const originalUpdatedAt = sourceFile.updatedAt
|
||||
|
||||
setTimeout(() => {
|
||||
sourceFile.addExport("User")
|
||||
|
||||
expect(sourceFile.updatedAt).toBe(originalUpdatedAt)
|
||||
}, 10)
|
||||
})
|
||||
|
||||
it("should add multiple different exports", () => {
|
||||
const path = ProjectPath.create("/project/src/domain/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
sourceFile.addExport("User")
|
||||
sourceFile.addExport("UserProps")
|
||||
sourceFile.addExport("UserFactory")
|
||||
|
||||
expect(sourceFile.exports).toEqual(["User", "UserProps", "UserFactory"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("importsFrom", () => {
|
||||
it("should return true if imports contain the specified layer", () => {
|
||||
const path = ProjectPath.create("/project/src/application/User.ts", "/project")
|
||||
const imports = ["../../domain/entities/User", "../use-cases/CreateUser"]
|
||||
const sourceFile = new SourceFile(path, "", imports)
|
||||
|
||||
expect(sourceFile.importsFrom("domain")).toBe(true)
|
||||
})
|
||||
|
||||
it("should return false if imports do not contain the specified layer", () => {
|
||||
const path = ProjectPath.create("/project/src/application/User.ts", "/project")
|
||||
const imports = ["../use-cases/CreateUser", "../dtos/UserDto"]
|
||||
const sourceFile = new SourceFile(path, "", imports)
|
||||
|
||||
expect(sourceFile.importsFrom("domain")).toBe(false)
|
||||
})
|
||||
|
||||
it("should be case-insensitive", () => {
|
||||
const path = ProjectPath.create("/project/src/application/User.ts", "/project")
|
||||
const imports = ["../../DOMAIN/entities/User"]
|
||||
const sourceFile = new SourceFile(path, "", imports)
|
||||
|
||||
expect(sourceFile.importsFrom("domain")).toBe(true)
|
||||
})
|
||||
|
||||
it("should return false for empty imports", () => {
|
||||
const path = ProjectPath.create("/project/src/application/User.ts", "/project")
|
||||
const sourceFile = new SourceFile(path, "")
|
||||
|
||||
expect(sourceFile.importsFrom("domain")).toBe(false)
|
||||
})
|
||||
|
||||
it("should handle partial matches in import paths", () => {
|
||||
const path = ProjectPath.create("/project/src/application/User.ts", "/project")
|
||||
const imports = ["../../infrastructure/database/UserRepository"]
|
||||
const sourceFile = new SourceFile(path, "", imports)
|
||||
|
||||
expect(sourceFile.importsFrom("infrastructure")).toBe(true)
|
||||
expect(sourceFile.importsFrom("domain")).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user