diff --git a/test/git-status.browser.test.ts b/test/git-status.browser.test.ts index f62621c4..615917cd 100644 --- a/test/git-status.browser.test.ts +++ b/test/git-status.browser.test.ts @@ -264,6 +264,10 @@ describe('Git status indicator in a real browser', () => { await page.evaluate(() => (window as any).app.saveAppSettings()); await page.waitForTimeout(300); await page.evaluate(() => (window as any).app.closeAppSettings()); + // gitStatusTree is per-device: it must never reach the strict PUT /api/settings (a 400 there is + // what an unstripped key looks like), and it must round-trip through the saved settings. + expect(settingsPutStatuses.every((st) => st === 200)).toBe(true); + expect(await page.evaluate(() => (window as any).app.isGitStatusTree())).toBe(true); rmSync(join(repo, 'deep'), { recursive: true }); rmSync(join(repo, 'docs'), { recursive: true }); }, 30000); diff --git a/test/routes/git-status-routes.test.ts b/test/routes/git-status-routes.test.ts index fb0c7564..dc6449f6 100644 --- a/test/routes/git-status-routes.test.ts +++ b/test/routes/git-status-routes.test.ts @@ -215,4 +215,42 @@ describe('GET /api/sessions/:id/git-diff', () => { const res = await app.inject({ method: 'GET', url: url({ repo: root, path: 'a.txt', kind: 'unstaged' }) }); expect(res.statusCode).toBe(404); }); + + it('diffs a staged rename against its old name, and a merge conflict as git’s combined diff (real git)', async () => { + // beforeEach left a.txt/b.txt modified; start this case from a clean tree. + git(dir, 'checkout', '-q', '--', '.'); + git(dir, 'reset', '-q', '--hard'); + git(dir, 'clean', '-fdq'); + writeFileSync(join(dir, 'old.txt'), 'a\nb\nc\nd\ne\nf\ng\n'); + git(dir, 'add', 'old.txt'); + git(dir, 'commit', '-q', '-m', 'old'); + // A real conflict on c.txt. + writeFileSync(join(dir, 'c.txt'), 'base\n'); + git(dir, 'add', 'c.txt'); + git(dir, 'commit', '-q', '-m', 'c'); + git(dir, 'checkout', '-q', '-b', 'other'); + writeFileSync(join(dir, 'c.txt'), 'theirs\n'); + git(dir, 'commit', '-q', '-am', 'theirs'); + git(dir, 'checkout', '-q', 'main'); + writeFileSync(join(dir, 'c.txt'), 'ours\n'); + git(dir, 'commit', '-q', '-am', 'ours'); + try { + git(dir, 'merge', 'other'); + } catch { + /* the conflict is the point */ + } + // A staged rename, made once the merge has stopped on the conflict. + git(dir, 'mv', 'old.txt', 'new-name.txt'); + writeFileSync(join(dir, 'new-name.txt'), 'a\nb\nc\nd\ne\nf\nCHANGED\n'); + git(dir, 'add', 'new-name.txt'); + const { app } = await setup(); + const root = realpathSync(dir); + const rename = await app.inject({ method: 'GET', url: url({ repo: root, path: 'new-name.txt', kind: 'staged' }) }); + expect(rename.statusCode).toBe(200); + expect(rename.json().data.diff).toContain('rename from old.txt'); + expect(rename.json().data.diff).toContain('+CHANGED'); + const conflict = await app.inject({ method: 'GET', url: url({ repo: root, path: 'c.txt', kind: 'conflicted' }) }); + expect(conflict.statusCode).toBe(200); + expect(conflict.json().data.diff).toMatch(/<<<<<<<|\+\+<<<<<<