BMAD-METHOD/.patch/477/TEST-SPECIFICATIONS.md

26 KiB

Test Specifications - Issue #477

Overview

This document specifies the complete test strategy for issue #477 fix: "Installer asks configuration questions during update instead of using existing settings"

Tests are organized by category and follow TDD principles. Tests should be created BEFORE implementation.


Test Infrastructure Setup

Test Framework

  • Framework: Jest (Node.js standard)
  • Location: test/ directory
  • Configuration: jest.config.js (or similar)

Test Fixtures Directory

test/
├── fixtures/
│   ├── manifests/
│   │   ├── valid-manifest.yaml
│   │   ├── minimal-manifest.yaml
│   │   ├── full-manifest.yaml
│   │   ├── old-version-manifest.yaml
│   │   ├── corrupted-manifest.yaml
│   │   ├── missing-required-field.yaml
│   │   └── missing-optional-field.yaml
│   ├── projects/
│   │   ├── fresh-install/
│   │   ├── with-manifest/
│   │   └── with-old-manifest/
│   └── configs/
│       └── sample-configs.js
├── unit/
├── integration/
└── scenarios/

Unit Tests

Test Suite 1: Configuration Loader (File: test/unit/config-loader.test.js)

Test 1.1: Load Valid Manifest

describe('ManifestConfigLoader', () => {
  describe('loadManifest', () => {
    it('should load a valid manifest file', async () => {
      // Given: Valid manifest file exists
      // When: loadManifest called
      // Then: Config loaded successfully
      //   AND all fields accessible
      //   AND no errors thrown
    });
  });
});

Acceptance Criteria:

  • File read successfully
  • YAML parsed without errors
  • All fields accessible via getConfig()
  • No exceptions thrown

Test 1.2: Handle Missing Manifest

it('should return empty config for missing manifest', async () => {
  // Given: Manifest file doesn't exist
  // When: loadManifest called with non-existent path
  // Then: Returns empty config object
  //   AND hasConfig() returns false for all keys
  //   AND no error thrown (graceful failure)
});

Acceptance Criteria:

  • No FileNotFound exception
  • Empty config returned
  • Graceful handling

Test 1.3: Handle Corrupted Manifest

it('should throw error for corrupted YAML', async () => {
  // Given: Corrupted YAML file
  // When: loadManifest called
  // Then: Error thrown with helpful message
  //   AND Error indicates YAML parse failure
});

Acceptance Criteria:

  • Error thrown with clear message
  • Message indicates YAML parsing issue
  • Helpful context provided

Test 1.4: Cache Configuration

it('should cache loaded configuration', async () => {
  // Given: Manifest loaded
  // When: getConfig called multiple times
  // Then: File read only once (verified via spy)
  //   AND Same cached object returned
});

Acceptance Criteria:

  • File system accessed only once
  • Subsequent calls return cached data
  • Performance verified (< 1ms second access)

Test 1.5: Get Specific Configuration Value

it('should return specific config value by key', async () => {
  // Given: Manifest with known values
  // When: getConfig('version') called
  // Then: Correct version string returned
  //   AND Type is string
});

Acceptance Criteria:

  • Correct value returned
  • Correct data type
  • Key access working

Test 1.6: Get Configuration with Default

it('should return default when config key missing', async () => {
  // Given: Config without 'ides_setup' field
  // When: getConfig('ides_setup', ['default-ide'])
  // Then: Returns default value
  //   AND Default is array ['default-ide']
});

Acceptance Criteria:

  • Default returned when missing
  • Default has correct type
  • Original config unchanged

Test Suite 2: Manifest Validation (File: test/unit/manifest-validation.test.js)

Test 2.1: Validate Complete Manifest

describe('Manifest Validation', () => {
  describe('validateManifest', () => {
    it('should validate complete valid manifest', () => {
      // Given: Valid manifest with all fields
      // When: validateManifest called
      // Then: Returns { isValid: true, errors: [] }
    });
  });
});

Acceptance Criteria:

  • isValid === true
  • errors array empty
  • All fields validated

Test 2.2: Reject Missing Required Fields

it('should reject manifest missing "version"', () => {
  // Given: Manifest without version field
  // When: validateManifest called
  // Then: Returns isValid: false
  //   AND errors includes "version required"
});

Acceptance Criteria:

  • isValid === false
  • Error message clear
  • Field name identified

Test 2.3: Reject Invalid Version Format

it('should reject invalid semver version', () => {
  // Given: Manifest with version "not-semver"
  // When: validateManifest called
  // Then: Returns isValid: false
  //   AND errors includes version format issue
});

Acceptance Criteria:

  • Invalid semver rejected
  • Error message clear
  • Expected format shown

Test 2.4: Reject Invalid Date Format

it('should reject invalid ISO date', () => {
  // Given: installed_at = "2025-13-45"
  // When: validateManifest called
  // Then: Returns isValid: false
  //   AND errors indicates date issue
});

Acceptance Criteria:

  • Invalid date rejected
  • Error message clear
  • ISO 8601 requirement noted

Test 2.5: Accept Optional Fields Missing

it('should allow missing optional fields', () => {
  // Given: Manifest without ides_setup
  // When: validateManifest called with only required fields
  // Then: Returns isValid: true
  //   AND no error for missing optional field
});

Acceptance Criteria:

  • Optional fields truly optional
  • isValid === true
  • No false errors

Test 2.6: Validate Array Fields

it('should validate ides_setup is array of strings', () => {
  // Given: ides_setup with non-string elements
  // When: validateManifest called
  // Then: Returns isValid: false if type wrong
});

Acceptance Criteria:

  • Array type enforced
  • String elements required
  • Invalid structures rejected

Test 2.7: Type Validation for All Fields

it('should validate field types', () => {
  // Given: install_type = 123 (number, not string)
  // When: validateManifest called
  // Then: Returns isValid: false
  //   AND error message clear
});

Acceptance Criteria:

  • Type validation working
  • All field types checked
  • Error messages clear

Test Suite 3: Update Mode Detection (File: test/unit/install-mode-detection.test.js)

Test 3.1: Detect Fresh Install

describe('Installer', () => {
  describe('detectInstallMode', () => {
    it('should detect fresh install when no manifest', () => {
      // Given: Project directory without manifest
      // When: detectInstallMode called
      // Then: Returns 'fresh'
    });
  });
});

Acceptance Criteria:

  • Returns exactly 'fresh'
  • No errors thrown
  • Verified with spy (no file read attempted)

Test 3.2: Detect Update Install

it('should detect update when version differs', () => {
  // Given: Manifest v4.36.2, Current v4.39.2
  // When: detectInstallMode called
  // Then: Returns 'update'
});

Acceptance Criteria:

  • Returns exactly 'update'
  • Version comparison working
  • Both versions parsed correctly

Test 3.3: Detect Reinstall

it('should detect reinstall when same version', () => {
  // Given: Manifest v4.36.2, Current v4.36.2
  // When: detectInstallMode called
  // Then: Returns 'reinstall'
});

Acceptance Criteria:

  • Returns exactly 'reinstall'
  • Version comparison accurate
  • Same version detected correctly

Test 3.4: Detect Invalid Manifest

it('should detect invalid manifest', () => {
  // Given: Corrupted manifest file
  // When: detectInstallMode called
  // Then: Returns 'invalid'
});

Acceptance Criteria:

  • Returns exactly 'invalid'
  • No crash on corruption
  • Graceful error handling

Test 3.5: Version Comparison Edge Cases

it('should handle version comparison edge cases', () => {
  // Test cases:
  // - v4.36.2 → v4.36.3 (patch bump) = update
  // - v4.36.2 → v5.0.0 (major bump) = update
  // - v4.36.2 → v4.36.2 (same) = reinstall
  // - v4.36.2 → v4.36.2-beta = different format
});

Acceptance Criteria:

  • Correct detection for all version patterns
  • Semver rules followed
  • Pre-release versions handled

Test 3.6: Logging in Detection

it('should log detection results', () => {
  // Given: detectInstallMode called
  // When: Execution completes
  // Then: Debug log shows:
  //   - Mode detected
  //   - Version info
  //   - Decision logic
});

Acceptance Criteria:

  • Logs helpful debug info
  • No console errors
  • Aids troubleshooting

Test Suite 4: Question Skipping (File: test/unit/prompt-skipping.test.js)

Test 4.1: Skip Question When Update with Config

describe('Question Skipping', () => {
  describe('when isUpdate=true and config exists', () => {
    it('should skip question and return config value', () => {
      // Given: isUpdate=true, config has value
      // When: Prompt function called
      // Then: Returns config value immediately
      //   AND prompt NOT displayed
      //   AND prompt library never called
    });
  });
});

Acceptance Criteria:

  • Config value returned
  • Prompt library not invoked
  • Verified via spy

Test 4.2: Ask Question When Fresh Install

it('should ask question on fresh install', () => {
  // Given: isUpdate=false, no prior config
  // When: Prompt function called
  // Then: Prompt displayed
  //   AND User input collected
  //   AND User value returned
});

Acceptance Criteria:

  • Prompt shown to user
  • Input collected normally
  • Fresh install unaffected

Test 4.3: Ask Question When Config Missing

it('should ask question if config missing on update', () => {
  // Given: isUpdate=true BUT config missing that field
  // When: Prompt function called
  // Then: Prompt displayed as fallback
  //   AND User input collected
  //   AND New value stored
});

Acceptance Criteria:

  • Fallback to prompt working
  • No silent failures
  • User can provide value

Test 4.4: Log Skipped Questions

it('should log when question is skipped', () => {
  // Given: isUpdate=true, config exists
  // When: Prompt function called
  // Then: Debug log shows:
  //   - Question skipped
  //   - Value used
  //   - Source (previous config)
});

Acceptance Criteria:

  • Debug logs useful
  • Aids troubleshooting
  • Shows what was skipped

Test 4.5: Multiple Questions Skipped

it('should skip all applicable questions on update', () => {
  // Given: All required config fields present
  // When: All prompt functions called with isUpdate=true
  // Then: All return config values
  //   AND No prompts displayed
  //   AND All log entries created
});

Acceptance Criteria:

  • Multiple questions skipped
  • Consistent behavior
  • No partial skipping

Integration Tests

Test Suite 5: Configuration Loading Integration (File: test/integration/install-config-loading.test.js)

Test 5.1: Load Config During Install Command

describe('Install Command Configuration Loading', () => {
  it('should load config after install mode detection', () => {
    // Given: Project with existing manifest
    // When: Install command initialization completes
    // Then: Config loaded and available to handlers
    //   AND No errors during loading
    //   AND Manifest validated
  });
});

Acceptance Criteria:

  • Config loaded without errors
  • Available to all handlers
  • Validation passed

Test 5.2: Config Available to All Setup Functions

it('should pass config to all setup functions', () => {
  // Given: Update detected, config loaded
  // When: Setup functions called
  // Then: Each function receives config object
  //   AND Can access values
  //   AND Each function can skip questions
});

Acceptance Criteria:

  • Config threaded through pipeline
  • All functions receive it
  • Accessible and usable

Test Suite 6: Question Skipping Integration (File: test/integration/questions-skipped-on-update.test.js)

Test 6.1: No Prompts During Update

describe('Update Install Flow', () => {
  it('should not show any config questions on update', () => {
    // Given: Update installation (manifest exists, version bump)
    // When: Install command runs
    // Then: No prompts displayed
    //   AND Process completes quickly
    //   AND Manifest read and used
  });
});

Acceptance Criteria:

  • Zero prompts shown
  • Fast execution
  • Manifest values used

Test 6.2: All Prompts During Fresh Install

it('should show all config questions on fresh install', () => {
  // Given: Fresh installation (no manifest)
  // When: Install command runs
  // Then: All questions displayed
  //   AND User can answer
  //   AND Responses stored
});

Acceptance Criteria:

  • All expected prompts shown
  • Normal flow maintained
  • No regression

Test 6.3: Graceful Fallback on Invalid Config

it('should ask questions if config invalid on update', () => {
  // Given: Update with invalid/corrupted manifest
  // When: Install command runs
  // Then: Validation fails
  //   AND Falls back to fresh install flow
  //   AND Asks all questions
  //   AND User warned about issue
});

Acceptance Criteria:

  • Graceful fallback
  • User warned
  • Questions asked
  • No data loss

Test Suite 7: Invalid Manifest Fallback (File: test/integration/invalid-manifest-fallback.test.js)

Test 7.1: Fallback on Corrupted File

describe('Invalid Manifest Handling', () => {
  it('should fallback on corrupted manifest file', () => {
    // Given: Corrupted YAML in manifest
    // When: Install command runs
    // Then: Parse error caught
    //   AND Not thrown
    //   AND Fallback to fresh install
    //   AND User notified
  });
});

Acceptance Criteria:

  • No crash on corruption
  • Error caught gracefully
  • User feedback provided

Test 7.2: Fallback on Missing Required Fields

it('should fallback on missing required field', () => {
  // Given: Manifest missing 'version'
  // When: Install command runs
  // Then: Validation fails
  //   AND Treated as fresh install
  //   AND Questions asked
  //   AND Log shows reason
});

Acceptance Criteria:

  • Missing fields detected
  • Fresh install behavior
  • Helpful logging

Test 7.3: No Manifest Corruption

it('should never corrupt existing manifest on error', () => {
  // Given: Error during install with existing manifest
  // When: Install command errors
  // Then: Original manifest unchanged
  //   AND File not modified
  //   AND Backup not needed
});

Acceptance Criteria:

  • Original preserved
  • Read-only during detection
  • Safe error handling

Test Suite 8: Backward Compatibility (File: test/integration/backward-compatibility.test.js)

Test 8.1: Handle Old Manifest Format

describe('Backward Compatibility', () => {
  it('should handle manifest from v4.30.0', () => {
    // Given: Manifest from old version with different format
    // When: Install command runs on update
    // Then: Old format understood
    //   AND Migrated gracefully
    //   AND Questions skipped if possible
    //   AND Asked if field missing
  });
});

Acceptance Criteria:

  • Old format read without error
  • Fields mapped correctly
  • No data loss

Test 8.2: Missing Optional Fields Handled

it('should handle manifest without ides_setup', () => {
  // Given: Manifest predating ides_setup field
  // When: Install command runs
  // Then: Default value applied
  //   AND No error thrown
  //   AND Process continues normally
});

Acceptance Criteria:

  • Default applied
  • No crashes
  • Correct type

Test 8.3: Missing expansion_packs Field

it('should handle manifest without expansion_packs', () => {
  // Given: Old manifest without expansion_packs
  // When: Install command runs
  // Then: Empty array assumed
  //   AND No error
  //   AND Normal flow continues
});

Acceptance Criteria:

  • Safe default
  • No errors
  • Backward compatible

Test 8.4: Version Comparison Backward Compat

it('should handle pre-release version formats', () => {
  // Given: Old manifest with "4.36.2-beta1"
  // When: detectInstallMode runs
  // Then: Correctly parsed
  //   AND Compared to current version
  //   AND Correct mode detected
});

Acceptance Criteria:

  • Pre-release handled
  • Comparison accurate
  • Mode correct

End-to-End Scenarios

Scenario 1: Fresh Installation (File: test/scenarios/e2e-fresh-install.test.js)

Setup: Empty project directory

Execution Steps:

  1. User runs npx bmad-method install
  2. System detects no manifest
  3. All configuration questions displayed
  4. User answers questions
  5. Installation proceeds
  6. Manifest created with answers

Expected Results:

  • All questions displayed
  • Manifest created
  • Manifest valid
  • All settings saved
  • Installation completes

Test Code Structure:

it('should complete fresh install with all prompts', async () => {
  // Setup: Create temp directory
  // Execute: Run install command
  // Verify: Questions shown
  // Verify: Manifest created
  // Verify: Contents correct
});

Scenario 2: Update Installation (File: test/scenarios/e2e-update-install.test.js)

Setup:

  • Project with manifest (v4.36.2)
  • Same settings as original install
  • Version bumped to v4.39.2

Execution Steps:

  1. User runs npx bmad-method install
  2. System detects manifest exists
  3. System detects version bump (update)
  4. Loads previous configuration
  5. NO questions displayed
  6. Installation proceeds with cached config
  7. Manifest updated with new version

Expected Results:

  • No prompts shown
  • Config loaded from manifest
  • Installation uses cached config
  • Version updated in manifest
  • Settings preserved
  • Fast execution (< 30 seconds)

Test Code Structure:

it('should skip questions on update and preserve config', async () => {
  // Setup: Create manifest v4.36.2
  // Execute: Run install with v4.39.2
  // Verify: No prompts shown
  // Verify: Old config used
  // Verify: Version updated
  // Verify: Settings preserved
});

Scenario 3: Reinstall with Same Version (File: test/scenarios/e2e-reinstall.test.js)

Setup:

  • Project with manifest (v4.36.2)
  • Same version being installed again

Execution Steps:

  1. User runs npx bmad-method install
  2. System detects manifest exists
  3. System detects same version (reinstall)
  4. Loads configuration
  5. Skips questions
  6. Reinstalls with same config
  7. Manifest timestamp updated

Expected Results:

  • No prompts shown
  • Config reused
  • Installation completes
  • Settings unchanged
  • Version unchanged (same)
  • Timestamp updated

Test Code Structure:

it('should skip questions on reinstall', async () => {
  // Setup: Create manifest v4.36.2
  // Execute: Run install with v4.36.2
  // Verify: No prompts shown
  // Verify: Settings reused
  // Verify: Version unchanged
});

Scenario 4: Invalid Manifest Recovery (File: test/scenarios/e2e-invalid-manifest.test.js)

Setup:

  • Project with corrupted manifest
  • Invalid YAML or missing required fields

Execution Steps:

  1. User runs npx bmad-method install
  2. System detects manifest exists
  3. System validates manifest
  4. Validation fails
  5. Falls back to fresh install flow
  6. Questions displayed
  7. User answers
  8. New valid manifest created

Expected Results:

  • Manifest error detected
  • Graceful fallback
  • User warned with message
  • All questions asked
  • New manifest created
  • No data corruption
  • Clear error message in logs

Test Code Structure:

it('should recover from invalid manifest', async () => {
  // Setup: Create corrupted manifest
  // Execute: Run install command
  // Verify: Error detected
  // Verify: Questions asked
  // Verify: New manifest created
});

Scenario 5: IDE Configuration Preservation (File: test/scenarios/e2e-ide-preservation.test.js)

Setup:

  • Manifest with IDE configurations:
    ides_setup:
      - claude-code
      - cline
    

Execution Steps:

  1. User runs update with existing manifest
  2. System loads manifest
  3. Skips IDE configuration questions
  4. Uses previous IDE selections
  5. Installation applies same IDEs

Expected Results:

  • IDE list loaded from manifest
  • Same IDEs configured
  • No prompts about IDEs
  • Configurations preserved
  • All IDE files intact

Test Code Structure:

it('should preserve IDE configurations on update', async () => {
  // Setup: Manifest with 2 IDEs
  // Execute: Run update install
  // Verify: IDEs loaded from manifest
  // Verify: Same IDEs installed
  // Verify: No prompts for IDEs
});

Scenario 6: Expansion Packs Preservation (File: test/scenarios/e2e-expansion-packs.test.js)

Setup:

  • Manifest with expansion packs:
    expansion_packs:
      - bmad-infrastructure-devops
      - custom-pack-1
    

Execution Steps:

  1. User runs update with existing manifest
  2. System loads manifest
  3. Skips expansion pack questions
  4. Uses previous selections
  5. Installation applies same packs

Expected Results:

  • Pack list loaded from manifest
  • Same packs configured
  • No prompts about packs
  • Configurations preserved
  • All pack files intact

Test Code Structure:

it('should preserve expansion packs on update', async () => {
  // Setup: Manifest with 2 packs
  // Execute: Run update install
  // Verify: Packs loaded from manifest
  // Verify: Same packs installed
  // Verify: No prompts for packs
});

Manual Testing Scenarios

Manual Test 1: Real Fresh Install

Steps:
1. Create new project directory
2. Run: npx bmad-method install
3. Observe: All questions asked
4. Answer: All questions with sample data
5. Verify: Installation completes
6. Verify: install-manifest.yaml created with answers

Success Criteria:
- All questions displayed
- Manifest created and valid
- Answers stored correctly
- Installation successful

Manual Test 2: Real Update Install

Steps:
1. Using project from Manual Test 1
2. Update to new version
3. Run: npx bmad-method install
4. Observe: NO questions asked
5. Verify: Old settings used
6. Verify: Version updated in manifest

Success Criteria:
- No configuration questions
- Old settings preserved
- Fast execution (< 30 sec)
- Version updated
- All files intact

Manual Test 3: Verify Settings Preserved

Steps:
1. Using project from Manual Test 2
2. Check install-manifest.yaml
3. Verify:
   - Original answers still there
   - Only version/timestamp updated
   - IDEs unchanged
   - Expansion packs unchanged

Success Criteria:
- Settings completely preserved
- Only version/timestamp changed
- File structure intact
- No settings reset

Manual Test 4: Large Manifest Test

Steps:
1. Create manifest with many fields
2. Add multiple IDEs (5+)
3. Add multiple packs (5+)
4. Run update install
5. Verify all fields preserved

Success Criteria:
- All fields handled
- No truncation
- Large file size OK
- Fast loading

Manual Test 5: Corrupted Manifest Recovery

Steps:
1. Create valid installation
2. Manually corrupt manifest (invalid YAML)
3. Run install command
4. Observe: Error detected
5. Observe: Questions asked
6. Answer questions
7. Verify: New manifest created

Success Criteria:
- Error detected
- Graceful recovery
- Questions asked
- New manifest valid
- No crash

Manual Test 6: Upgrade Scenario

Steps:
1. Install old version (v4.30.0 format)
2. Upgrade to current version
3. Run install command
4. Verify: Old manifest understood
5. Verify: No questions asked
6. Verify: Settings preserved

Success Criteria:
- Old format handled
- Backward compatible
- Questions skipped
- Settings preserved

Manual Test 7: Performance Test

Steps:
1. Install with all options
2. Run update install 10 times
3. Measure total time
4. Measure per-install time

Success Criteria:
- Total: < 5 minutes
- Per install: < 30 seconds
- Consistent timing
- No slowdown over multiple runs

Manual Test 8: CLI Flags (Future)

Steps:
1. Run: bmad install --reconfigure
   Verify: Questions asked despite manifest
2. Run: bmad install --skip-questions
   Verify: No questions, all defaults used
3. Run: bmad install --manifest-path ./custom/path
   Verify: Custom manifest path used

Success Criteria:
- Flags work correctly
- Behavior matches flag intent
- Error handling if flag conflicts

Test Execution Strategy

Phase 1: Unit Tests (Run First)

npm test -- test/unit/ --verbose

Expected: All pass Time: ~2-3 minutes Success: 100% coverage of new functions

Phase 2: Integration Tests

npm test -- test/integration/ --verbose

Expected: All pass Time: ~3-5 minutes Success: All workflows tested

Phase 3: End-to-End Scenarios

npm test -- test/scenarios/ --verbose

Expected: All pass Time: ~5-10 minutes Success: Real-world workflows work

Phase 4: Manual Tests

  • Performed by developer
  • Using actual CLI commands
  • Real project directories
  • Time: ~1-2 hours

Phase 5: Regression Tests (Continuous)

  • Run with each commit
  • Verify no breaking changes
  • Ensure backward compatibility

Test Coverage Goals

Category Target Current
Unit Tests 95% TBD
Integration Tests 90% TBD
Edge Cases 100% TBD
Error Paths 100% TBD
Backward Compat 100% TBD

Success Criteria

All tests passing AND:

  • No prompts during update
  • Settings preserved from manifest
  • Fresh install unaffected
  • Old manifests handled gracefully
  • Performance acceptable
  • Error messages helpful
  • No data corruption
  • Backward compatible