Issue: #237 Testing Approach: Test-Driven Development
This guide helps you implement the Bastion Default feature using TDD. All tests are written and will guide your implementation.
Status: Tests written, implementation needed Tests Total: ~90 tests across 3 levels Estimated Time: 3 weeks
# See all failing tests
pytest tests/ -v
# Run specific test level
pytest tests/unit/test_bastion_default_behavior.py -v # Unit
pytest tests/integration/test_bastion_default_integration.py -v # Integration
pytest tests/e2e/test_bastion_default_e2e.py -v # E2E- Pick a test (start with unit tests)
- Run it - should fail (Red)
- Implement minimal code to pass
- Run it - should pass (Green)
- Refactor - improve code quality
- Repeat with next test
File: tests/unit/test_bastion_default_behavior.py
What to implement:
- Bastion detection logic
- User prompt handling
- VM provisioning with bastion
- Flag override behavior
- Config persistence
- Error handling
- Security validation
Run:
pytest tests/unit/test_bastion_default_behavior.py -vFile: tests/integration/test_bastion_default_integration.py
What to test:
- Module interactions
- Complete workflows
- Config persistence across operations
- CLI integration
- Error recovery
Run:
pytest tests/integration/test_bastion_default_integration.py -vFile: tests/e2e/test_bastion_default_e2e.py
What to validate:
- Acceptance criteria (AC1-AC5)
- Complete user workflows
- Error scenarios
- Performance
- Security compliance
Run:
pytest tests/e2e/test_bastion_default_e2e.py -vGoal: Implement bastion auto-detection
Files to modify:
src/azlin/modules/bastion_detector.py
Tests to pass:
pytest tests/unit/test_bastion_default_behavior.py::TestBastionAutoDetection -vKey functions:
detect_bastion_for_vm()- Main detection logiclist_bastions()- Query Azure API- Error handling for Azure failures
Success criteria:
- 15 detection tests pass
- Graceful error handling
- Logging implemented
Goal: Implement user interaction prompts
Files to modify:
src/azlin/vm_provisioning.pysrc/azlin/vm_connector.py
Tests to pass:
pytest tests/unit/test_bastion_default_behavior.py::TestUserPromptBehavior -vKey features:
click.confirm()for user prompts- Default values (yes for bastion)
- Cost information display
- User choice handling
Success criteria:
- 5 prompt tests pass
- User can accept/decline
- Cost info displayed
Goal: Integrate bastion with VM provisioning
Files to modify:
src/azlin/vm_provisioning.pysrc/azlin/vm_connector.py
Tests to pass:
pytest tests/unit/test_bastion_default_behavior.py::TestVMProvisioningWithBastion -v
pytest tests/unit/test_bastion_default_behavior.py::TestBastionFlagOverride -vKey features:
- Skip public IP when using bastion
- Create public IP when declining bastion
- Flag override behavior (--no-bastion)
- Backward compatibility (--use-bastion)
Success criteria:
- 9 provisioning tests pass
- Flag behavior correct
- Backward compatible
Goal: Save and load bastion preferences
Files to modify:
src/azlin/modules/bastion_config.py(already exists)
Tests to pass:
pytest tests/unit/test_bastion_default_behavior.py::TestBastionConfigPersistence -v
pytest tests/integration/test_bastion_default_integration.py::TestBastionConfigPersistence -vKey features:
- Save VM-to-Bastion mappings
- Load preferences on next connection
- Secure file permissions (0600)
- Config merge and update
Success criteria:
- 6 config tests pass
- Config persists correctly
- Secure permissions enforced
Goal: Test module interactions
Files to verify:
- All modules working together
- Complete workflows
Tests to pass:
pytest tests/integration/test_bastion_default_integration.py -vKey scenarios:
- Detection + provisioning flow
- Connection with saved config
- CLI integration
- Error recovery
- Multi-VM scenarios
Success criteria:
- 20 integration tests pass
- No module conflicts
- Smooth workflows
Goal: Validate acceptance criteria
Tests to pass:
pytest tests/e2e/test_bastion_default_e2e.py -vKey validation:
- AC1: Auto-detect bastion ✓
- AC2: Use automatically with confirmation ✓
- AC3: Prompt to create (default yes) ✓
- AC4: Allow decline for public IP ✓
- AC5: Backward compatibility ✓
Success criteria:
- 18 E2E tests pass
- All ACs validated
- User workflows smooth
- Performance acceptable
Goal: Pass all security tests
Tests to pass:
pytest tests/ -v -k securitySecurity checklist:
- No credentials in config ✓
- Config file permissions 0600 ✓
- Input validation (injection prevention) ✓
- Error message sanitization ✓
- Path traversal prevention ✓
Success criteria:
- All security tests pass
- No vulnerabilities found
- Security review approved
# Generate coverage report
pytest --cov=azlin.modules.bastion_detector \
--cov=azlin.vm_provisioning \
--cov=azlin.vm_connector \
--cov-report=html \
--cov-report=term-missing
# View HTML report
open htmlcov/index.htmlMinimum: 90% for critical paths
Critical paths:
- Bastion detection: 95%+
- User prompts: 90%+
- VM provisioning: 95%+
- Flag handling: 100%
- Config persistence: 90%+
- Error handling: 85%+
def test_detection_scenario(self):
# Arrange - Mock Azure response
mock_bastions = [{"name": "test-bastion", "resourceGroup": "test-rg"}]
# Act - Call detection
with patch.object(BastionDetector, 'list_bastions', return_value=mock_bastions):
result = BastionDetector.detect_bastion_for_vm("vm", "test-rg")
# Assert - Verify result
assert result is not None
assert result["name"] == "test-bastion"def test_prompt_scenario(self):
# Arrange - Mock user input
with patch('click.confirm', return_value=True) as mock_confirm:
# Act - Trigger prompt
result = mock_confirm("Use bastion?", default=True)
# Assert - Verify prompt and result
assert result is True
mock_confirm.assert_called_once()def test_provisioning_scenario(self):
# Arrange - Mock Azure and detection
with patch('subprocess.run') as mock_run:
mock_run.return_value.stdout = '{"privateIpAddress": "10.0.0.4"}'
# Act - Provision VM
# Implementation calls subprocess
# Assert - Verify correct command
cmd = mock_run.call_args[0][0]
assert "--public-ip-address" not in cmd # No public IPCause: Function/method not implemented Fix: Implement the missing method
Cause: Function returns None instead of expected value Fix: Return correct value from function
Cause: Code path not executing expected logic Fix: Check conditional logic, ensure code reaches mock
Cause: Incorrect implementation logic Fix: Review test expectations, fix logic
# Run all tests
pytest tests/ -v
# Run single test
pytest tests/unit/test_bastion_default_behavior.py::TestBastionAutoDetection::test_detect_bastion_in_resource_group_found -v
# Run tests matching pattern
pytest tests/ -k "test_bastion" -v
# Run with coverage
pytest tests/ --cov=azlin --cov-report=html
# Run only failed tests from last run
pytest --lf -v
# Run in parallel (faster)
pytest tests/ -n auto
# Watch mode (re-run on file change)
pytest-watch tests/ -- -vAfter implementation, verify all ACs:
-
AC1: Bastion auto-detected in resource group
- Test:
test_ac1_auto_detect_bastion_in_resource_group - Command:
pytest tests/e2e/test_bastion_default_e2e.py::TestAcceptanceCriteria::test_ac1_auto_detect_bastion_in_resource_group -v
- Test:
-
AC2: Used automatically with confirmation
- Test:
test_ac2_use_bastion_automatically_with_confirmation - Verify: Prompt shows, default yes, bastion used
- Test:
-
AC3: Prompt to create (default yes)
- Test:
test_ac3_prompt_to_create_bastion_default_yes - Verify: Default is yes, cost shown
- Test:
-
AC4: Can decline for public IP
- Test:
test_ac4_allow_decline_bastion_use_public_ip - Verify: User can say no, public IP created
- Test:
-
AC5: Backward compatible with --use-bastion
- Test:
test_ac5_backward_compatibility_use_bastion_flag - Verify: Flag works, no breaking changes
- Test:
Expected Performance:
- Bastion detection: <5 seconds
- VM provisioning: <10 minutes (includes bastion if needed)
- Tunnel creation: <30 seconds
- Config load/save: <100ms
Test Performance:
# Check test execution time
pytest tests/unit/ --durations=10
pytest tests/integration/ --durations=5
pytest tests/e2e/ --durations=3- Full spec:
docs/BASTION_DEFAULT_TEST_SPEC.md - Security:
docs/BASTION_SECURITY_REQUIREMENTS.md
- Unit:
tests/unit/test_bastion_default_behavior.py - Integration:
tests/integration/test_bastion_default_integration.py - E2E:
tests/e2e/test_bastion_default_e2e.py
- Tests not found: Check pytest discovery (files must start with
test_) - Imports fail: Check PYTHONPATH includes
src/ - Mocks not working: Verify patch paths match actual import paths
- Fixtures missing: Check fixture scope and names
Feature is done when:
- ✅ All 90 tests pass (100%)
- ✅ Code coverage >90%
- ✅ All 5 ACs validated
- ✅ Security tests pass
- ✅ Performance acceptable
- ✅ Documentation complete
- ✅ Code reviewed
- Start with Phase 1: Bastion detection
- Run tests frequently:
pytest -v - Follow TDD cycle: Red → Green → Refactor
- Check coverage: Maintain >90%
- Ask for help: If stuck on a test
Happy TDD! Let the tests guide your implementation.