/** * SimpleGitBranchManager Tests * * Tests for remoteBranchExists validation used when setting * a project's default branch. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { simpleGit } from 'simple-git'; import { SimpleGitBranchManager } from './simple-git-branch-manager.js'; /** * Create a "remote" bare repo and a clone of it for testing. * The bare repo has branches that the clone can see as remote tracking branches. */ async function createTestRepoWithRemote(): Promise<{ clonePath: string; barePath: string; cleanup: () => Promise; }> { const tmpBase = await mkdtemp(path.join(tmpdir(), 'cw-branch-test-')); const barePath = path.join(tmpBase, 'bare.git'); const workPath = path.join(tmpBase, 'work'); const clonePath = path.join(tmpBase, 'clone'); // Create a bare repo const bareGit = simpleGit(); await bareGit.init([barePath, '--bare']); // Clone it to a working directory, add commits and branches, push await simpleGit().clone(barePath, workPath); const workGit = simpleGit(workPath); await workGit.addConfig('user.email', 'test@example.com'); await workGit.addConfig('user.name', 'Test User'); await writeFile(path.join(workPath, 'README.md'), '# Test\n'); await workGit.add('README.md'); await workGit.commit('Initial commit'); await workGit.push('origin', 'main'); // Create additional branches await workGit.checkoutLocalBranch('develop'); await writeFile(path.join(workPath, 'dev.txt'), 'dev\n'); await workGit.add('dev.txt'); await workGit.commit('Dev commit'); await workGit.push('origin', 'develop'); await workGit.checkoutLocalBranch('feature/auth'); await writeFile(path.join(workPath, 'auth.txt'), 'auth\n'); await workGit.add('auth.txt'); await workGit.commit('Auth commit'); await workGit.push('origin', 'feature/auth'); // Clone from bare to simulate what project registration does await simpleGit().clone(barePath, clonePath); return { clonePath, barePath, cleanup: async () => { await rm(tmpBase, { recursive: true, force: true }); }, }; } describe('SimpleGitBranchManager', () => { let clonePath: string; let cleanup: () => Promise; let branchManager: SimpleGitBranchManager; beforeEach(async () => { const setup = await createTestRepoWithRemote(); clonePath = setup.clonePath; cleanup = setup.cleanup; branchManager = new SimpleGitBranchManager(); }); afterEach(async () => { await cleanup(); }); describe('remoteBranchExists', () => { it('should return true for a branch that exists on the remote', async () => { expect(await branchManager.remoteBranchExists(clonePath, 'main')).toBe(true); expect(await branchManager.remoteBranchExists(clonePath, 'develop')).toBe(true); expect(await branchManager.remoteBranchExists(clonePath, 'feature/auth')).toBe(true); }); it('should return false for a branch that does not exist', async () => { expect(await branchManager.remoteBranchExists(clonePath, 'nonexistent')).toBe(false); expect(await branchManager.remoteBranchExists(clonePath, 'feature/nope')).toBe(false); }); it('should return false for an invalid repo path', async () => { expect(await branchManager.remoteBranchExists('/tmp/no-such-repo', 'main')).toBe(false); }); it('should detect remote branches not checked out locally', async () => { // After clone, only 'main' is checked out locally. // 'develop' exists only as origin/develop. const localExists = await branchManager.branchExists(clonePath, 'develop'); const remoteExists = await branchManager.remoteBranchExists(clonePath, 'develop'); expect(localExists).toBe(false); expect(remoteExists).toBe(true); }); }); });