Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale 2a8e97c7a5 fix: skip additional Windows-specific tests
- fish-installer: skip uninstall permission test (chmod on directory)
- powershell-installer: skip "skip configuration when script line exists"
  test (Windows has dual profile paths so the second profile gets configured)
2026-01-09 16:18:16 -08:00
Tabish Bidiwale e7eac663ee fix: skip Windows-specific permission tests that rely on chmod()
fs.chmod() on directories doesn't restrict write access on Windows since
Windows uses ACLs that Node.js doesn't control. Additionally, admin users
and CI runners can bypass read-only attributes. Skip these tests on Windows
and use platform-specific invalid paths in cross-platform tests.

Fixes #401 (bash/pwsh completion commit breaking Windows e2e tests).
2026-01-09 16:04:12 -08:00
4 changed files with 34 additions and 10 deletions
@@ -140,7 +140,11 @@ describe('BashInstaller', () => {
it('should handle installation errors gracefully', async () => {
// Create installer with non-existent/invalid home directory
const invalidInstaller = new BashInstaller('/root/invalid/nonexistent/path');
// Use a path that will fail on both Unix and Windows
const invalidPath = process.platform === 'win32'
? 'Z:\\nonexistent\\invalid\\path' // Non-existent drive letter on Windows
: '/root/invalid/nonexistent/path'; // Permission-denied path on Unix
const invalidInstaller = new BashInstaller(invalidPath);
const result = await invalidInstaller.install(testScript);
@@ -374,7 +378,11 @@ describe('BashInstaller', () => {
it('should handle write permission errors gracefully', async () => {
// Create installer with path that can't be written
const invalidInstaller = new BashInstaller('/root/invalid/path');
// Use a path that will fail on both Unix and Windows
const invalidPath = process.platform === 'win32'
? 'Z:\\nonexistent\\invalid\\path' // Non-existent drive letter on Windows
: '/root/invalid/path'; // Permission-denied path on Unix
const invalidInstaller = new BashInstaller(invalidPath);
const result = await invalidInstaller.configureBashrc(completionsDir);
@@ -192,7 +192,9 @@ complete -c openspec -a 'validate' -d 'Validate specs'
await fs.rm(spacedHomeDir, { recursive: true, force: true });
});
it('should return failure on permission error', async () => {
// Skip on Windows: fs.chmod() on directories doesn't restrict write access on Windows
// Windows uses ACLs which Node.js chmod doesn't control
it.skipIf(process.platform === 'win32')('should return failure on permission error', async () => {
// Create a read-only directory to simulate permission error
const restrictedDir = path.join(testHomeDir, '.config', 'fish', 'completions');
await fs.mkdir(restrictedDir, { recursive: true });
@@ -284,7 +286,9 @@ complete -c openspec -a 'init'
expect(result.message).toBe('Completion script uninstalled successfully');
});
it('should return failure on permission error', async () => {
// Skip on Windows: fs.chmod() on directories doesn't restrict write access on Windows
// Windows uses ACLs which Node.js chmod doesn't control
it.skipIf(process.platform === 'win32')('should return failure on permission error', async () => {
await installer.install(mockCompletionScript);
const targetPath = path.join(testHomeDir, '.config', 'fish', 'completions', 'openspec.fish');
const parentDir = path.dirname(targetPath);
@@ -171,7 +171,9 @@ describe('PowerShellInstaller', () => {
expect(content).toContain('Write-Host "Hello"');
});
it('should skip configuration when script line already exists', async () => {
// Skip on Windows: Windows has dual profile paths (PowerShell Core + Windows PowerShell 5.1),
// so even if one profile is already configured, the second one will be configured and return true
it.skipIf(process.platform === 'win32')('should skip configuration when script line already exists', async () => {
delete process.env.OPENSPEC_NO_AUTO_CONFIG;
const profilePath = installer.getProfilePath();
await fs.mkdir(path.dirname(profilePath), { recursive: true });
@@ -237,7 +239,9 @@ describe('PowerShellInstaller', () => {
expect(content).toContain('# OPENSPEC:END');
});
it('should return false on write permission error', async () => {
// Skip on Windows: fs.chmod() doesn't reliably restrict write access on Windows
// (admin users can bypass read-only attribute, and CI runners often have elevated privileges)
it.skipIf(process.platform === 'win32')('should return false on write permission error', async () => {
delete process.env.OPENSPEC_NO_AUTO_CONFIG;
const profilePath = installer.getProfilePath();
await fs.mkdir(path.dirname(profilePath), { recursive: true });
@@ -451,7 +455,9 @@ Register-ArgumentCompleter -CommandName openspec -ScriptBlock $openspecCompleter
// Note: OPENSPEC_NO_AUTO_CONFIG support was removed from PowerShell installer
// Profile is now always auto-configured if possible
it('should provide instructions when profile cannot be configured', async () => {
// Skip on Windows: fs.chmod() doesn't reliably restrict write access on Windows
// (admin users can bypass read-only attribute, and CI runners often have elevated privileges)
it.skipIf(process.platform === 'win32')('should provide instructions when profile cannot be configured', async () => {
delete process.env.OPENSPEC_NO_AUTO_CONFIG;
// Make profile directory read-only to prevent configuration
const profilePath = installer.getProfilePath();
@@ -496,7 +502,9 @@ Register-ArgumentCompleter -CommandName openspec -ScriptBlock $openspecCompleter
await fs.rm(spacedHomeDir, { recursive: true, force: true });
});
it('should return failure on permission error', async () => {
// Skip on Windows: fs.chmod() on directories doesn't restrict write access on Windows
// Windows uses ACLs which Node.js chmod doesn't control
it.skipIf(process.platform === 'win32')('should return failure on permission error', async () => {
const targetPath = installer.getInstallationPath();
const targetDir = path.dirname(targetPath);
await fs.mkdir(targetDir, { recursive: true });
@@ -614,7 +622,9 @@ Register-ArgumentCompleter -CommandName openspec -ScriptBlock $openspecCompleter
expect(profileContentAfter).not.toContain('# OPENSPEC:START');
});
it('should return failure on permission error', async () => {
// Skip on Windows: fs.chmod() on directories doesn't restrict write access on Windows
// Windows uses ACLs which Node.js chmod doesn't control
it.skipIf(process.platform === 'win32')('should return failure on permission error', async () => {
delete process.env.OPENSPEC_NO_AUTO_CONFIG;
await installer.install(mockCompletionScript);
const targetPath = installer.getInstallationPath();
+3 -1
View File
@@ -196,7 +196,9 @@ describe('FileSystemUtils', () => {
expect(canWrite).toBe(true);
});
it('should return false for non-existent file in read-only directory', async () => {
// Skip on Windows: fs.chmod() on directories doesn't restrict write access on Windows
// Windows uses ACLs which Node.js chmod doesn't control
it.skipIf(process.platform === 'win32')('should return false for non-existent file in read-only directory', async () => {
const readOnlyDir = path.join(testDir, 'readonly-dir');
await fs.mkdir(readOnlyDir);
await fs.chmod(readOnlyDir, 0o555); // Read-only + execute