test(14-07): add PhaseRepository dependency tests
- Add createDependency tests (create, FK constraint, multiple deps) - Add getDependencies tests (empty, with deps, direct only) - Add getDependents tests (empty, with dependents, direct only) - Add phase_dependencies table to test-helpers.ts
This commit is contained in:
@@ -257,4 +257,208 @@ describe('DrizzlePhaseRepository', () => {
|
||||
expect(next).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
// ===========================================================================
|
||||
// Phase Dependency Tests
|
||||
// ===========================================================================
|
||||
|
||||
describe('createDependency', () => {
|
||||
it('should create dependency between two phases', async () => {
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
const phase2 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 2,
|
||||
name: 'Phase 2',
|
||||
});
|
||||
|
||||
await phaseRepo.createDependency(phase2.id, phase1.id);
|
||||
|
||||
const deps = await phaseRepo.getDependencies(phase2.id);
|
||||
expect(deps).toContain(phase1.id);
|
||||
});
|
||||
|
||||
it('should throw if phase does not exist', async () => {
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
|
||||
// Creating dependency with non-existent phase should throw (FK constraint)
|
||||
await expect(
|
||||
phaseRepo.createDependency('non-existent-phase', phase1.id)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should allow multiple dependencies for same phase', async () => {
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
const phase2 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 2,
|
||||
name: 'Phase 2',
|
||||
});
|
||||
const phase3 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 3,
|
||||
name: 'Phase 3',
|
||||
});
|
||||
|
||||
// Phase 3 depends on both Phase 1 and Phase 2
|
||||
await phaseRepo.createDependency(phase3.id, phase1.id);
|
||||
await phaseRepo.createDependency(phase3.id, phase2.id);
|
||||
|
||||
const deps = await phaseRepo.getDependencies(phase3.id);
|
||||
expect(deps.length).toBe(2);
|
||||
expect(deps).toContain(phase1.id);
|
||||
expect(deps).toContain(phase2.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDependencies', () => {
|
||||
it('should return empty array for phase with no dependencies', async () => {
|
||||
const phase = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
|
||||
const deps = await phaseRepo.getDependencies(phase.id);
|
||||
expect(deps).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return dependency IDs for phase with dependencies', async () => {
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
const phase2 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 2,
|
||||
name: 'Phase 2',
|
||||
});
|
||||
const phase3 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 3,
|
||||
name: 'Phase 3',
|
||||
});
|
||||
|
||||
// Phase 3 depends on Phase 1 and Phase 2
|
||||
await phaseRepo.createDependency(phase3.id, phase1.id);
|
||||
await phaseRepo.createDependency(phase3.id, phase2.id);
|
||||
|
||||
const deps = await phaseRepo.getDependencies(phase3.id);
|
||||
expect(deps.length).toBe(2);
|
||||
expect(deps).toContain(phase1.id);
|
||||
expect(deps).toContain(phase2.id);
|
||||
});
|
||||
|
||||
it('should return only direct dependencies (not transitive)', async () => {
|
||||
// Phase 1 -> Phase 2 -> Phase 3 (linear chain)
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
const phase2 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 2,
|
||||
name: 'Phase 2',
|
||||
});
|
||||
const phase3 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 3,
|
||||
name: 'Phase 3',
|
||||
});
|
||||
|
||||
// Phase 2 depends on Phase 1
|
||||
await phaseRepo.createDependency(phase2.id, phase1.id);
|
||||
// Phase 3 depends on Phase 2
|
||||
await phaseRepo.createDependency(phase3.id, phase2.id);
|
||||
|
||||
// Phase 3's dependencies should only include Phase 2, not Phase 1
|
||||
const depsPhase3 = await phaseRepo.getDependencies(phase3.id);
|
||||
expect(depsPhase3.length).toBe(1);
|
||||
expect(depsPhase3).toContain(phase2.id);
|
||||
expect(depsPhase3).not.toContain(phase1.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDependents', () => {
|
||||
it('should return empty array for phase with no dependents', async () => {
|
||||
const phase = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
|
||||
const dependents = await phaseRepo.getDependents(phase.id);
|
||||
expect(dependents).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return dependent phase IDs', async () => {
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
const phase2 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 2,
|
||||
name: 'Phase 2',
|
||||
});
|
||||
const phase3 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 3,
|
||||
name: 'Phase 3',
|
||||
});
|
||||
|
||||
// Phase 2 and Phase 3 both depend on Phase 1
|
||||
await phaseRepo.createDependency(phase2.id, phase1.id);
|
||||
await phaseRepo.createDependency(phase3.id, phase1.id);
|
||||
|
||||
const dependents = await phaseRepo.getDependents(phase1.id);
|
||||
expect(dependents.length).toBe(2);
|
||||
expect(dependents).toContain(phase2.id);
|
||||
expect(dependents).toContain(phase3.id);
|
||||
});
|
||||
|
||||
it('should return only direct dependents', async () => {
|
||||
// Phase 1 -> Phase 2 -> Phase 3 (linear chain)
|
||||
const phase1 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 1,
|
||||
name: 'Phase 1',
|
||||
});
|
||||
const phase2 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 2,
|
||||
name: 'Phase 2',
|
||||
});
|
||||
const phase3 = await phaseRepo.create({
|
||||
initiativeId: testInitiativeId,
|
||||
number: 3,
|
||||
name: 'Phase 3',
|
||||
});
|
||||
|
||||
// Phase 2 depends on Phase 1
|
||||
await phaseRepo.createDependency(phase2.id, phase1.id);
|
||||
// Phase 3 depends on Phase 2
|
||||
await phaseRepo.createDependency(phase3.id, phase2.id);
|
||||
|
||||
// Phase 1's dependents should only include Phase 2, not Phase 3
|
||||
const dependentsPhase1 = await phaseRepo.getDependents(phase1.id);
|
||||
expect(dependentsPhase1.length).toBe(1);
|
||||
expect(dependentsPhase1).toContain(phase2.id);
|
||||
expect(dependentsPhase1).not.toContain(phase3.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,14 @@ CREATE TABLE IF NOT EXISTS phases (
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Phase dependencies table
|
||||
CREATE TABLE IF NOT EXISTS phase_dependencies (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
phase_id TEXT NOT NULL REFERENCES phases(id) ON DELETE CASCADE,
|
||||
depends_on_phase_id TEXT NOT NULL REFERENCES phases(id) ON DELETE CASCADE,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Plans table
|
||||
CREATE TABLE IF NOT EXISTS plans (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
|
||||
Reference in New Issue
Block a user