/** * GSD Tools Tests - New Milestone Clear Phases (#1588) * * Verifies that `phases clear` removes all phase subdirectories from * .planning/phases/, leaving the directory itself intact. */ const { test, describe, beforeEach, afterEach } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); const path = require('path'); const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs'); describe('phases clear command', () => { let tmpDir; beforeEach(() => { tmpDir = createTempProject(); }); afterEach(() => { cleanup(tmpDir); }); test('clears all phase subdirectories from .planning/phases/', () => { const phasesDir = path.join(tmpDir, '.planning', 'phases'); // Simulate phases left over from a previous milestone const phase1 = path.join(phasesDir, '01-foundation'); const phase2 = path.join(phasesDir, '02-api'); const phase3 = path.join(phasesDir, '03-ui'); fs.mkdirSync(phase1, { recursive: true }); fs.mkdirSync(phase2, { recursive: true }); fs.mkdirSync(phase3, { recursive: true }); fs.writeFileSync(path.join(phase1, '01-01-PLAN.md'), '# Plan'); fs.writeFileSync(path.join(phase2, '02-01-SUMMARY.md'), '# Summary'); const result = runGsdTools('phases clear --confirm', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.cleared, 3, 'should report 3 directories cleared'); // phases/ directory itself must still exist assert.ok(fs.existsSync(phasesDir), '.planning/phases/ directory should still exist'); // all subdirectories must be gone const remaining = fs.readdirSync(phasesDir, { withFileTypes: true }) .filter(e => e.isDirectory()); assert.strictEqual(remaining.length, 0, 'no phase subdirectories should remain'); }); test('succeeds with cleared=0 when phases directory is already empty', () => { const phasesDir = path.join(tmpDir, '.planning', 'phases'); // createTempProject creates the directory but leaves it empty const result = runGsdTools('phases clear --confirm', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.cleared, 0, 'should report 0 cleared when already empty'); assert.ok(fs.existsSync(phasesDir), '.planning/phases/ directory should still exist'); }); test('succeeds with cleared=0 when phases directory does not exist', () => { // Remove the phases directory entirely fs.rmSync(path.join(tmpDir, '.planning', 'phases'), { recursive: true, force: true }); const result = runGsdTools('phases clear --confirm', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.cleared, 0, 'should report 0 cleared when directory absent'); }); test('does not remove files (only directories) at the phases root', () => { const phasesDir = path.join(tmpDir, '.planning', 'phases'); // Put a stray file directly in phases/ (edge case) fs.writeFileSync(path.join(phasesDir, 'README.md'), '# Phases'); const phase1 = path.join(phasesDir, '01-foundation'); fs.mkdirSync(phase1, { recursive: true }); fs.writeFileSync(path.join(phase1, '01-01-PLAN.md'), '# Plan'); const result = runGsdTools('phases clear --confirm', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.cleared, 1, 'should report 1 directory cleared (not the file)'); // File must survive assert.ok( fs.existsSync(path.join(phasesDir, 'README.md')), 'files at phases root should be preserved' ); }); test('clears nested phase content (recursive delete)', () => { const phasesDir = path.join(tmpDir, '.planning', 'phases'); const phase1 = path.join(phasesDir, '01-foundation'); const nested = path.join(phase1, 'subdir'); fs.mkdirSync(nested, { recursive: true }); fs.writeFileSync(path.join(nested, 'deep-file.md'), '# Deep'); const result = runGsdTools('phases clear --confirm', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); assert.ok(!fs.existsSync(phase1), 'phase directory including nested content should be removed'); }); });