import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; // homedir/platform are read at call time, so they can be stubbed per case. vi.mock('node:os', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, homedir: () => '/home/tester', platform: () => mockPlatform, }; }); let mockPlatform: NodeJS.Platform = 'linux'; const { getShellProfilePath, detectShell } = await import('../../src/platform/detect.js'); describe('getShellProfilePath', () => { const originalShell = process.env['SHELL']; const originalZdotdir = process.env['ZDOTDIR']; beforeEach(() => { mockPlatform = 'linux'; delete process.env['ZDOTDIR']; }); afterEach(() => { if (originalShell === undefined) delete process.env['SHELL']; else process.env['SHELL'] = originalShell; if (originalZdotdir === undefined) delete process.env['ZDOTDIR']; else process.env['ZDOTDIR'] = originalZdotdir; }); // The regression this guards: setupPath() in stages/finalize.ts appends the // PATH export to whatever this returns. A line written to ~/.bashrc is // unreachable to `bash -lc`, systemd units and agent seats, because Debian's // default .bashrc returns early for non-interactive shells — so an install // reported success and left `mosaic: command not found`. Same for .zshrc, // which zsh only reads for interactive shells. it('never targets an interactive-only rc file', () => { for (const shell of ['/bin/bash', '/usr/bin/zsh']) { process.env['SHELL'] = shell; const profile = getShellProfilePath(); expect(profile).not.toMatch(/\.bashrc$/); expect(profile).not.toMatch(/\.zshrc$/); } }); it('uses ~/.profile for bash', () => { process.env['SHELL'] = '/bin/bash'; expect(getShellProfilePath()).toBe('/home/tester/.profile'); }); it('uses ~/.zshenv for zsh', () => { process.env['SHELL'] = '/usr/bin/zsh'; expect(getShellProfilePath()).toBe('/home/tester/.zshenv'); }); it('honours ZDOTDIR for zsh', () => { process.env['SHELL'] = '/usr/bin/zsh'; process.env['ZDOTDIR'] = '/custom/zdot'; expect(getShellProfilePath()).toBe('/custom/zdot/.zshenv'); }); it('falls back to ~/.profile for an unknown shell', () => { process.env['SHELL'] = '/bin/somethingelse'; expect(detectShell()).toBe('unknown'); expect(getShellProfilePath()).toBe('/home/tester/.profile'); }); it('still routes fish to its own config', () => { process.env['SHELL'] = '/usr/bin/fish'; expect(getShellProfilePath()).toBe('/home/tester/.config/fish/config.fish'); }); });