import { describe, expect, it } from 'vitest' import { decideBaseUpdate, isEfCoreConflict, buildConflictGuidance } from '../update-policy.js' describe('decideBaseUpdate — pure decision for updating a branch from its base', () => { it('returns none when there is nothing to take from the base', () => { expect(decideBaseUpdate({ ahead: 0, behind: 0, strategy: 'merge' })).toBe('none') // ahead-only must NEVER touch the branch (regardless of strategy) expect(decideBaseUpdate({ ahead: 5, behind: 0, strategy: 'merge' })).toBe('none') expect(decideBaseUpdate({ ahead: 5, behind: 0, strategy: 'rebase' })).toBe('none') }) it('fast-forwards when behind with no own commits — ff wins over BOTH strategies', () => { expect(decideBaseUpdate({ ahead: 0, behind: 3, strategy: 'merge' })).toBe('ff') expect(decideBaseUpdate({ ahead: 0, behind: 3, strategy: 'rebase' })).toBe('ff') }) it('falls back to the chosen strategy only when genuinely diverged', () => { expect(decideBaseUpdate({ ahead: 2, behind: 3, strategy: 'merge' })).toBe('merge') expect(decideBaseUpdate({ ahead: 2, behind: 3, strategy: 'rebase' })).toBe('rebase') }) }) describe('isEfCoreConflict — EF Core artifacts in a conflict list', () => { it('detects files under a Migrations/ directory', () => { expect(isEfCoreConflict(['Migrations/20260101_Init.cs'])).toBe(true) expect(isEfCoreConflict(['src/Data/Migrations/20260101_Init.cs'])).toBe(true) }) it('detects model snapshots anywhere, case-insensitively', () => { expect(isEfCoreConflict(['src/Persistence/FooModelSnapshot.cs'])).toBe(true) expect(isEfCoreConflict(['src/Persistence/foomodelsnapshot.CS'])).toBe(true) }) it('does not match a file merely NAMED Migrations (not a directory segment)', () => { expect(isEfCoreConflict(['src/components/Migrations.tsx'])).toBe(false) expect(isEfCoreConflict(['src/MigrationsHelper.cs'])).toBe(false) }) it('returns false for unrelated files and empty lists', () => { expect(isEfCoreConflict(['src/Program.cs', 'web/src/App.tsx'])).toBe(false) expect(isEfCoreConflict([])).toBe(false) }) }) describe('buildConflictGuidance — human guidance after a rolled-back conflict', () => { it('states the abort + untouched guarantee and names the operation and base ref', () => { const g = buildConflictGuidance({ operation: 'merge', baseRef: 'origin/develop', branch: 'feature/x', conflictFiles: ['src/a.ts', 'src/b.ts'], }) expect(g).toContain('aborted') expect(g).toContain('untouched') expect(g).toContain('merge') expect(g).toContain('origin/develop') expect(g).toContain('2 file(s)') }) it('points at /efcore rebase-snapshot when EF Core files conflict', () => { const g = buildConflictGuidance({ operation: 'rebase', baseRef: 'origin/develop', branch: 'feature/x', conflictFiles: ['src/Persistence/Migrations/AppModelSnapshot.cs'], }) expect(g).toContain('/efcore rebase-snapshot') }) it('does NOT mention efcore for non-EF conflicts', () => { const g = buildConflictGuidance({ operation: 'merge', baseRef: 'origin/develop', branch: 'feature/x', conflictFiles: ['src/a.ts'], }) expect(g).not.toContain('/efcore') }) })