🎯 refactor: implement comprehensive BDD test suite with modular architecture
✨ feat: add feature-based test organization per ADR 0024 🐛 fix: resolve compilation errors in suite_feature.go 📝 docs: add comprehensive BDD framework documentation ♻️ refactor: split monolithic tests into modular features 🧪 test: implement synchronization helpers and context management ⚡ perf: add parallel test execution capability 🔧 chore: add feature-specific test scripts and validation 📚 docs: move BDD_TAGS.md to features/ for better organization Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -1,31 +1,320 @@
|
|||||||
Pending BDD Tests Implementation Plan
|
# BDD Implementation Plan - Iterative Approach
|
||||||
|
|
||||||
Implementation Plan:
|
Based on ADR 0024: BDD Test Organization and Isolation Strategy
|
||||||
|
|
||||||
**Configuration & Validation** (LOW priority):
|
## Phase 1: Refactor Current Tests (1-2 weeks)
|
||||||
- `iSetRetentionFactorTo()` - Dynamic configuration
|
|
||||||
- `iTryToStartTheServer()` - Server validation
|
|
||||||
- `iShouldReceiveConfigurationValidationError()` - Error handling
|
|
||||||
- `theErrorShouldMention()` - Error message validation
|
|
||||||
|
|
||||||
**Monitoring & Metrics** (LOW priority):
|
### Objective: Split monolithic feature files into modular, isolated components
|
||||||
- `iShouldSeeMetricIncrement()` - Already implemented ✅
|
|
||||||
- `iShouldSeeMetricDecrease()` - Already implemented ✅
|
|
||||||
- `iShouldSeeHistogramUpdate()` - Already implemented ✅
|
|
||||||
|
|
||||||
**Performance & Scalability** (LOW priority):
|
### Tasks:
|
||||||
- `iHaveJWTSecrets()` - Bulk secret management
|
1. **Split feature files by business domain**
|
||||||
- `ofThemAreExpired()` - Expiration tracking
|
- Create `features/auth/` directory
|
||||||
- `itShouldCompleteWithinMilliseconds()` - Performance validation
|
- Create `features/config/` directory
|
||||||
- `andNotImpactServerPerformance()` - Performance monitoring
|
- Create `features/greet/` directory
|
||||||
|
- Create `features/health/` directory
|
||||||
|
- Create `features/jwt/` directory
|
||||||
|
|
||||||
**Advanced Features** (LOW priority):
|
2. **Implement feature-specific isolation**
|
||||||
- Various edge case and advanced scenarios
|
- Add config file patterns: `features/{domain}/{domain}-test-config.yaml`
|
||||||
|
- Implement database naming: `dance_lessons_coach_{domain}_test`
|
||||||
|
- Assign unique ports per feature group
|
||||||
|
|
||||||
Next Steps:
|
3. **Create feature-specific test scripts**
|
||||||
|
- Implement `scripts/test-feature.sh` with feature parameter
|
||||||
|
- Add environment setup/teardown logic
|
||||||
|
- Implement resource cleanup routines
|
||||||
|
|
||||||
1. Add configuration validation and monitoring
|
### Deliverables:
|
||||||
2. Implement step definitions for pending scenarios
|
- ✅ Modular feature directory structure
|
||||||
3. Run full test suite to verify all scenarios pass
|
- ✅ Feature-specific configuration files
|
||||||
|
- ✅ Basic isolation mechanisms
|
||||||
|
- ✅ Feature-level test scripts
|
||||||
|
|
||||||
Estimated Time: 2-3 days
|
## Phase 2: Enhance Test Infrastructure (2-3 weeks)
|
||||||
|
|
||||||
|
### Objective: Add synchronization and lifecycle management
|
||||||
|
|
||||||
|
### Tasks:
|
||||||
|
1. **Implement synchronization helpers**
|
||||||
|
- Add `waitForServerReady()` with timeout
|
||||||
|
- Add `waitForConfigReload()` with event-based detection
|
||||||
|
- Add `waitForCondition()` helper function
|
||||||
|
|
||||||
|
2. **Add Godog context management**
|
||||||
|
- Create feature-specific context structs
|
||||||
|
- Implement `InitializeFeatureSuite()`
|
||||||
|
- Implement `CleanupFeatureSuite()`
|
||||||
|
|
||||||
|
3. **Add tag-based test selection**
|
||||||
|
- Implement `@smoke`, `@auth`, `@config` tags
|
||||||
|
- Add tag filtering to test scripts
|
||||||
|
- Document tag usage in README
|
||||||
|
|
||||||
|
### Deliverables:
|
||||||
|
- ✅ Robust synchronization mechanisms
|
||||||
|
- ✅ Proper context lifecycle management
|
||||||
|
- ✅ Tag-based test execution
|
||||||
|
- ✅ Improved test reliability
|
||||||
|
|
||||||
|
## Phase 3: Parallel Testing (Optional - 1 week)
|
||||||
|
|
||||||
|
### Objective: Enable safe parallel test execution
|
||||||
|
|
||||||
|
### Tasks:
|
||||||
|
1. **Implement port management**
|
||||||
|
- Add port allocation system
|
||||||
|
- Implement port conflict detection
|
||||||
|
- Add parallel execution flags
|
||||||
|
|
||||||
|
2. **Add resource monitoring**
|
||||||
|
- Implement resource usage tracking
|
||||||
|
- Add timeout detection
|
||||||
|
- Implement cleanup on failure
|
||||||
|
|
||||||
|
3. **Update CI/CD pipeline**
|
||||||
|
- Add parallel test execution
|
||||||
|
- Implement resource limits
|
||||||
|
- Add test isolation validation
|
||||||
|
|
||||||
|
### Deliverables:
|
||||||
|
- ✅ Parallel test execution capability
|
||||||
|
- ✅ Resource monitoring and limits
|
||||||
|
- ✅ Updated CI/CD configuration
|
||||||
|
|
||||||
|
## Implementation Timeline
|
||||||
|
|
||||||
|
### Week 1-2: Phase 1 - Test Refactoring
|
||||||
|
- Day 1-2: Create feature directory structure
|
||||||
|
- Day 3-4: Implement feature-specific configs
|
||||||
|
- Day 5-7: Create test scripts and isolation
|
||||||
|
- Day 8-10: Test and validate refactoring
|
||||||
|
|
||||||
|
### Week 3-5: Phase 2 - Infrastructure Enhancement
|
||||||
|
- Day 11-12: Add synchronization helpers
|
||||||
|
- Day 13-14: Implement context management
|
||||||
|
- Day 15-17: Add tag-based selection
|
||||||
|
- Day 18-21: Test and validate infrastructure
|
||||||
|
|
||||||
|
### Week 6: Phase 3 - Parallel Testing (Optional)
|
||||||
|
- Day 22-24: Implement port management
|
||||||
|
- Day 25-26: Add resource monitoring
|
||||||
|
- Day 27-28: Update CI/CD pipeline
|
||||||
|
- Day 29-30: Test and validate parallel execution
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
### Phase 1 Success:
|
||||||
|
- ✅ All tests pass in new structure
|
||||||
|
- ✅ Feature isolation working correctly
|
||||||
|
- ✅ Test scripts functional
|
||||||
|
- ✅ No regression in test coverage
|
||||||
|
|
||||||
|
### Phase 2 Success:
|
||||||
|
- ✅ Synchronization working reliably
|
||||||
|
- ✅ Context management implemented
|
||||||
|
- ✅ Tag filtering operational
|
||||||
|
- ✅ Test reliability >95%
|
||||||
|
|
||||||
|
### Phase 3 Success:
|
||||||
|
- ✅ Parallel tests execute safely
|
||||||
|
- ✅ Resource usage within limits
|
||||||
|
- ✅ CI/CD pipeline updated
|
||||||
|
- ✅ Test execution time reduced
|
||||||
|
|
||||||
|
## Risk Mitigation
|
||||||
|
|
||||||
|
### Phase 1 Risks:
|
||||||
|
- **Test failures during refactoring**: Maintain old structure until new is validated
|
||||||
|
- **Isolation issues**: Implement gradual rollout with validation
|
||||||
|
|
||||||
|
### Phase 2 Risks:
|
||||||
|
- **Synchronization complexity**: Start with simple timeouts, enhance gradually
|
||||||
|
- **Context management bugs**: Add comprehensive logging and debugging
|
||||||
|
|
||||||
|
### Phase 3 Risks:
|
||||||
|
- **Resource conflicts**: Implement strict resource limits and monitoring
|
||||||
|
- **CI/CD instability**: Test parallel execution locally before pipeline update
|
||||||
|
|
||||||
|
## Monitoring and Validation
|
||||||
|
|
||||||
|
### Phase 1 Validation:
|
||||||
|
```bash
|
||||||
|
# Test each feature independently
|
||||||
|
./scripts/test-feature.sh auth
|
||||||
|
./scripts/test-feature.sh config
|
||||||
|
./scripts/test-feature.sh greet
|
||||||
|
|
||||||
|
# Verify isolation
|
||||||
|
./scripts/validate-isolation.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2 Validation:
|
||||||
|
```bash
|
||||||
|
# Test synchronization
|
||||||
|
./scripts/test-synchronization.sh
|
||||||
|
|
||||||
|
# Test tag filtering
|
||||||
|
godog --tags=@smoke features/
|
||||||
|
|
||||||
|
# Test context management
|
||||||
|
./scripts/test-context-lifecycle.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3 Validation:
|
||||||
|
```bash
|
||||||
|
# Test parallel execution
|
||||||
|
./scripts/test-all-features-parallel.sh
|
||||||
|
|
||||||
|
# Monitor resource usage
|
||||||
|
./scripts/monitor-test-resources.sh
|
||||||
|
|
||||||
|
# Validate CI/CD changes
|
||||||
|
./scripts/validate-ci-cd.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
### Phase 1 Rollback:
|
||||||
|
```bash
|
||||||
|
# Revert to original structure
|
||||||
|
git checkout HEAD~1 -- features/
|
||||||
|
|
||||||
|
# Restore original test scripts
|
||||||
|
git checkout HEAD~1 -- scripts/test-*.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2 Rollback:
|
||||||
|
```bash
|
||||||
|
# Remove synchronization helpers
|
||||||
|
git checkout HEAD~1 -- pkg/bdd/helpers/
|
||||||
|
|
||||||
|
# Restore original context management
|
||||||
|
git checkout HEAD~1 -- pkg/bdd/context/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3 Rollback:
|
||||||
|
```bash
|
||||||
|
# Disable parallel execution
|
||||||
|
sed -i 's/parallel=true/parallel=false/' scripts/test-all-features-parallel.sh
|
||||||
|
|
||||||
|
# Revert CI/CD changes
|
||||||
|
git checkout HEAD~1 -- .github/workflows/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation Updates
|
||||||
|
|
||||||
|
### Phase 1 Documentation:
|
||||||
|
- ✅ Update README with new test structure
|
||||||
|
- ✅ Document feature organization conventions
|
||||||
|
- ✅ Add test execution instructions
|
||||||
|
|
||||||
|
### Phase 2 Documentation:
|
||||||
|
- ✅ Document synchronization patterns
|
||||||
|
- ✅ Add context management guide
|
||||||
|
- ✅ Document tag usage and filtering
|
||||||
|
|
||||||
|
### Phase 3 Documentation:
|
||||||
|
- ✅ Add parallel testing guide
|
||||||
|
- ✅ Document resource limits
|
||||||
|
- ✅ Update CI/CD documentation
|
||||||
|
|
||||||
|
## Team Communication
|
||||||
|
|
||||||
|
### Phase 1:
|
||||||
|
- Team meeting to explain new structure
|
||||||
|
- Hands-on workshop for test refactoring
|
||||||
|
- Daily standups to track progress
|
||||||
|
|
||||||
|
### Phase 2:
|
||||||
|
- Technical deep dive on synchronization
|
||||||
|
- Code review sessions for context management
|
||||||
|
- Pair programming for complex scenarios
|
||||||
|
|
||||||
|
### Phase 3:
|
||||||
|
- Performance testing workshop
|
||||||
|
- CI/CD pipeline review
|
||||||
|
- Resource monitoring training
|
||||||
|
|
||||||
|
## Continuous Improvement
|
||||||
|
|
||||||
|
### Post-Phase 1:
|
||||||
|
- Gather feedback on new structure
|
||||||
|
- Identify pain points in isolation
|
||||||
|
- Optimize test execution times
|
||||||
|
|
||||||
|
### Post-Phase 2:
|
||||||
|
- Monitor test reliability metrics
|
||||||
|
- Identify flaky tests for fixing
|
||||||
|
- Optimize synchronization patterns
|
||||||
|
|
||||||
|
### Post-Phase 3:
|
||||||
|
- Monitor parallel execution performance
|
||||||
|
- Identify resource bottlenecks
|
||||||
|
- Optimize CI/CD pipeline timing
|
||||||
|
|
||||||
|
## Metrics Tracking
|
||||||
|
|
||||||
|
### Test Reliability:
|
||||||
|
```
|
||||||
|
# Track pass rate over time
|
||||||
|
./scripts/track-test-reliability.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Execution Time:
|
||||||
|
```
|
||||||
|
# Monitor execution times
|
||||||
|
./scripts/monitor-execution-time.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resource Usage:
|
||||||
|
```
|
||||||
|
# Track resource consumption
|
||||||
|
./scripts/monitor-resource-usage.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### Post-Phase 3:
|
||||||
|
- Test impact analysis
|
||||||
|
- Flaky test detection
|
||||||
|
- Performance benchmarking
|
||||||
|
- Test coverage visualization
|
||||||
|
|
||||||
|
### Long-term:
|
||||||
|
- AI-assisted test generation
|
||||||
|
- Automated test optimization
|
||||||
|
- Predictive test failure analysis
|
||||||
|
- Intelligent test prioritization
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
### Phase 1: Test Refactoring
|
||||||
|
- [ ] Create feature directories
|
||||||
|
- [ ] Split feature files
|
||||||
|
- [ ] Implement config isolation
|
||||||
|
- [ ] Add database isolation
|
||||||
|
- [ ] Create test scripts
|
||||||
|
- [ ] Test and validate
|
||||||
|
|
||||||
|
### Phase 2: Infrastructure Enhancement
|
||||||
|
- [ ] Add synchronization helpers
|
||||||
|
- [ ] Implement context management
|
||||||
|
- [ ] Add tag filtering
|
||||||
|
- [ ] Test and validate
|
||||||
|
|
||||||
|
### Phase 3: Parallel Testing
|
||||||
|
- [ ] Implement port management
|
||||||
|
- [ ] Add resource monitoring
|
||||||
|
- [ ] Update CI/CD pipeline
|
||||||
|
- [ ] Test and validate
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Each phase builds on the previous one
|
||||||
|
- Phase 3 is optional and can be deferred
|
||||||
|
- Focus on reliability before performance
|
||||||
|
- Maintain backward compatibility where possible
|
||||||
|
- Document all changes thoroughly
|
||||||
|
- Gather team feedback at each phase
|
||||||
|
- Monitor metrics continuously
|
||||||
|
- Celebrate milestones and successes
|
||||||
|
|||||||
29
features/auth/auth_test.go
Normal file
29
features/auth/auth_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"dance-lessons-coach/pkg/bdd"
|
||||||
|
"github.com/cucumber/godog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthBDD(t *testing.T) {
|
||||||
|
// Set FEATURE environment variable for feature-specific configuration
|
||||||
|
os.Setenv("FEATURE", "auth")
|
||||||
|
|
||||||
|
suite := godog.TestSuite{
|
||||||
|
Name: "dance-lessons-coach BDD Tests - Auth Feature",
|
||||||
|
TestSuiteInitializer: bdd.InitializeTestSuite,
|
||||||
|
ScenarioInitializer: bdd.InitializeScenario,
|
||||||
|
Options: &godog.Options{
|
||||||
|
Format: "progress",
|
||||||
|
Paths: []string{"."},
|
||||||
|
TestingT: t,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if suite.Run() != 0 {
|
||||||
|
t.Fatal("non-zero status returned, failed to run auth BDD tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package features
|
package features
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"dance-lessons-coach/pkg/bdd"
|
"dance-lessons-coach/pkg/bdd"
|
||||||
@@ -8,13 +9,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestBDD(t *testing.T) {
|
func TestBDD(t *testing.T) {
|
||||||
|
// Get feature name from environment variable or default to all features
|
||||||
|
feature := os.Getenv("FEATURE")
|
||||||
|
|
||||||
|
var paths []string
|
||||||
|
var suiteName string
|
||||||
|
|
||||||
|
if feature == "" {
|
||||||
|
// Run all features
|
||||||
|
suiteName = "dance-lessons-coach BDD Tests - All Features"
|
||||||
|
paths = []string{
|
||||||
|
"features/auth",
|
||||||
|
"features/config",
|
||||||
|
"features/greet",
|
||||||
|
"features/health",
|
||||||
|
"features/jwt",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Run specific feature
|
||||||
|
suiteName = "dance-lessons-coach BDD Tests - " + feature + " Feature"
|
||||||
|
paths = []string{"features/" + feature}
|
||||||
|
}
|
||||||
|
|
||||||
suite := godog.TestSuite{
|
suite := godog.TestSuite{
|
||||||
Name: "dance-lessons-coach BDD Tests",
|
Name: suiteName,
|
||||||
TestSuiteInitializer: bdd.InitializeTestSuite,
|
TestSuiteInitializer: bdd.InitializeTestSuite,
|
||||||
ScenarioInitializer: bdd.InitializeScenario,
|
ScenarioInitializer: bdd.InitializeScenario,
|
||||||
Options: &godog.Options{
|
Options: &godog.Options{
|
||||||
Format: "progress",
|
Format: "progress",
|
||||||
Paths: []string{"."},
|
Paths: paths,
|
||||||
TestingT: t,
|
TestingT: t,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
29
features/config/config_test.go
Normal file
29
features/config/config_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"dance-lessons-coach/pkg/bdd"
|
||||||
|
"github.com/cucumber/godog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigBDD(t *testing.T) {
|
||||||
|
// Set FEATURE environment variable for feature-specific configuration
|
||||||
|
os.Setenv("FEATURE", "config")
|
||||||
|
|
||||||
|
suite := godog.TestSuite{
|
||||||
|
Name: "dance-lessons-coach BDD Tests - Config Feature",
|
||||||
|
TestSuiteInitializer: bdd.InitializeTestSuite,
|
||||||
|
ScenarioInitializer: bdd.InitializeScenario,
|
||||||
|
Options: &godog.Options{
|
||||||
|
Format: "progress",
|
||||||
|
Paths: []string{"."},
|
||||||
|
TestingT: t,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if suite.Run() != 0 {
|
||||||
|
t.Fatal("non-zero status returned, failed to run config BDD tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
# features/greet.feature
|
# features/greet.feature
|
||||||
|
@greet @smoke
|
||||||
Feature: Greet Service
|
Feature: Greet Service
|
||||||
The greet service should return appropriate greetings
|
The greet service should return appropriate greetings
|
||||||
|
|
||||||
|
@basic
|
||||||
Scenario: Default greeting
|
Scenario: Default greeting
|
||||||
Given the server is running
|
Given the server is running
|
||||||
When I request the default greeting
|
When I request the default greeting
|
||||||
Then the response should be "{\"message\":\"Hello world!\"}"
|
Then the response should be "{\"message\":\"Hello world!\"}"
|
||||||
|
|
||||||
|
@basic
|
||||||
Scenario: Personalized greeting
|
Scenario: Personalized greeting
|
||||||
Given the server is running
|
Given the server is running
|
||||||
When I request a greeting for "John"
|
When I request a greeting for "John"
|
||||||
Then the response should be "{\"message\":\"Hello John!\"}"
|
Then the response should be "{\"message\":\"Hello John!\"}"
|
||||||
|
|
||||||
|
@v2 @api
|
||||||
Scenario: v2 greeting with JSON POST request
|
Scenario: v2 greeting with JSON POST request
|
||||||
Given the server is running with v2 enabled
|
Given the server is running with v2 enabled
|
||||||
When I send a POST request to v2 greet with name "John"
|
When I send a POST request to v2 greet with name "John"
|
||||||
29
features/greet/greet_test.go
Normal file
29
features/greet/greet_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package greet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"dance-lessons-coach/pkg/bdd"
|
||||||
|
"github.com/cucumber/godog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGreetBDD(t *testing.T) {
|
||||||
|
// Set FEATURE environment variable for feature-specific configuration
|
||||||
|
os.Setenv("FEATURE", "greet")
|
||||||
|
|
||||||
|
suite := godog.TestSuite{
|
||||||
|
Name: "dance-lessons-coach BDD Tests - Greet Feature",
|
||||||
|
TestSuiteInitializer: bdd.InitializeTestSuite,
|
||||||
|
ScenarioInitializer: bdd.InitializeScenario,
|
||||||
|
Options: &godog.Options{
|
||||||
|
Format: "progress",
|
||||||
|
Paths: []string{"."},
|
||||||
|
TestingT: t,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if suite.Run() != 0 {
|
||||||
|
t.Fatal("non-zero status returned, failed to run greet BDD tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
# features/health.feature
|
# features/health.feature
|
||||||
|
@health @smoke @critical
|
||||||
Feature: Health Endpoint
|
Feature: Health Endpoint
|
||||||
The health endpoint should indicate server status
|
The health endpoint should indicate server status
|
||||||
|
|
||||||
|
@basic @critical
|
||||||
Scenario: Health check returns healthy status
|
Scenario: Health check returns healthy status
|
||||||
Given the server is running
|
Given the server is running
|
||||||
When I request the health endpoint
|
When I request the health endpoint
|
||||||
29
features/health/health_test.go
Normal file
29
features/health/health_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"dance-lessons-coach/pkg/bdd"
|
||||||
|
"github.com/cucumber/godog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHealthBDD(t *testing.T) {
|
||||||
|
// Set FEATURE environment variable for feature-specific configuration
|
||||||
|
os.Setenv("FEATURE", "health")
|
||||||
|
|
||||||
|
suite := godog.TestSuite{
|
||||||
|
Name: "dance-lessons-coach BDD Tests - Health Feature",
|
||||||
|
TestSuiteInitializer: bdd.InitializeTestSuite,
|
||||||
|
ScenarioInitializer: bdd.InitializeScenario,
|
||||||
|
Options: &godog.Options{
|
||||||
|
Format: "progress",
|
||||||
|
Paths: []string{"."},
|
||||||
|
TestingT: t,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if suite.Run() != 0 {
|
||||||
|
t.Fatal("non-zero status returned, failed to run health BDD tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
29
features/jwt/jwt_test.go
Normal file
29
features/jwt/jwt_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"dance-lessons-coach/pkg/bdd"
|
||||||
|
"github.com/cucumber/godog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJWTBDD(t *testing.T) {
|
||||||
|
// Set FEATURE environment variable for feature-specific configuration
|
||||||
|
os.Setenv("FEATURE", "jwt")
|
||||||
|
|
||||||
|
suite := godog.TestSuite{
|
||||||
|
Name: "dance-lessons-coach BDD Tests - JWT Feature",
|
||||||
|
TestSuiteInitializer: bdd.InitializeTestSuite,
|
||||||
|
ScenarioInitializer: bdd.InitializeScenario,
|
||||||
|
Options: &godog.Options{
|
||||||
|
Format: "progress",
|
||||||
|
Paths: []string{"."},
|
||||||
|
TestingT: t,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if suite.Run() != 0 {
|
||||||
|
t.Fatal("non-zero status returned, failed to run jwt BDD tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +1,327 @@
|
|||||||
# BDD Testing with Godog
|
# BDD Testing Framework
|
||||||
|
|
||||||
This package implements Behavior-Driven Development (BDD) testing using the Godog framework.
|
This directory contains the Behavior-Driven Development (BDD) testing framework for the dance-lessons-coach project, implementing the architecture described in ADR 0024.
|
||||||
|
|
||||||
## Important Requirements for Step Definitions
|
## 🗺️ Architecture Overview
|
||||||
|
|
||||||
### Step Pattern Matching
|
The BDD framework follows a modular, isolated test suite architecture with these key components:
|
||||||
|
|
||||||
Godog has **very specific requirements** for step pattern matching. To avoid "undefined" warnings:
|
### 📁 Directory Structure
|
||||||
|
|
||||||
1. **Use the exact regex pattern** that Godog suggests in its error messages
|
```
|
||||||
2. **Use the exact parameter names** that Godog suggests (`arg1, arg2`, etc.)
|
pkg/bdd/
|
||||||
3. **Match the feature file syntax exactly** including quotes and JSON formatting
|
├── README.md # This file
|
||||||
|
├── context/ # Feature-specific test contexts
|
||||||
### Example
|
│ ├── auth_context.go # Authentication test context
|
||||||
|
│ └── config_context.go # Configuration test context
|
||||||
**Feature file step:**
|
├── helpers/ # Test synchronization helpers
|
||||||
```gherkin
|
│ └── synchronization.go # Wait functions and utilities
|
||||||
Then the response should be "{\"message\":\"Hello world!\"}"
|
├── parallel/ # Parallel test execution
|
||||||
|
│ ├── port_manager.go # Port allocation system
|
||||||
|
│ └── resource_monitor.go # Resource tracking
|
||||||
|
├── steps/ # Step definitions
|
||||||
|
│ ├── auth_steps.go # Authentication steps
|
||||||
|
│ ├── config_steps.go # Configuration steps
|
||||||
|
│ ├── greet_steps.go # Greeting steps
|
||||||
|
│ ├── health_steps.go # Health check steps
|
||||||
|
│ ├── jwt_retention_steps.go # JWT retention steps
|
||||||
|
│ └── steps.go # Main step registration
|
||||||
|
├── suite.go # Test suite initialization
|
||||||
|
├── suite_feature.go # Feature-specific suite support
|
||||||
|
└── testserver/ # Test server implementation
|
||||||
|
├── client.go # HTTP test client
|
||||||
|
└── server.go # Test server with config
|
||||||
```
|
```
|
||||||
|
|
||||||
**Correct step definition:**
|
## 🎯 Core Components
|
||||||
|
|
||||||
|
### 1. Test Server
|
||||||
|
|
||||||
|
**Location:** `pkg/bdd/testserver/`
|
||||||
|
|
||||||
|
The test server provides a real HTTP server instance for black-box testing:
|
||||||
|
|
||||||
|
- **Hybrid Testing**: Runs in-process (not external process)
|
||||||
|
- **Configuration**: Loads feature-specific configs from `features/*/*-test-config.yaml`
|
||||||
|
- **Database**: Manages PostgreSQL connections with proper isolation
|
||||||
|
- **Port Management**: Uses feature-specific ports (9192-9196)
|
||||||
|
|
||||||
|
**Key Functions:**
|
||||||
|
- `NewServer()` - Creates test server instance
|
||||||
|
- `Start()` - Starts server with feature-specific configuration
|
||||||
|
- `initDBConnection()` - Initializes database connection
|
||||||
|
- `createTestConfig()` - Loads feature-specific configuration
|
||||||
|
|
||||||
|
### 2. Step Definitions
|
||||||
|
|
||||||
|
**Location:** `pkg/bdd/steps/`
|
||||||
|
|
||||||
|
Step definitions implement the Gherkin scenarios using Godog:
|
||||||
|
|
||||||
|
- **Domain-Specific**: Organized by feature area (auth, config, greet, etc.)
|
||||||
|
- **Reusable**: Common patterns in `common_steps.go`
|
||||||
|
- **Exact Matching**: Uses Godog's exact regex patterns
|
||||||
|
|
||||||
|
**Example:**
|
||||||
```go
|
```go
|
||||||
ctx.Step(`^the response should be "{\"([^"]*)\":\"([^"]*)\"}"$`, func(arg1, arg2 string) error {
|
// greet_steps.go
|
||||||
// Implementation here
|
func (gs *GreetSteps) iRequestAGreetingFor(name string) error {
|
||||||
return nil
|
return gs.client.Request("GET", fmt.Sprintf("/api/v1/greet/%s", name), nil)
|
||||||
})
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Incorrect patterns that cause "undefined" warnings:**
|
### 3. Synchronization Helpers
|
||||||
```go
|
|
||||||
// Wrong: Different regex pattern
|
|
||||||
ctx.Step(`^the response should be "{\"message\":\"([^"]*)\"}"$`, func(message string) error {
|
|
||||||
// ...
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wrong: Different parameter names
|
**Location:** `pkg/bdd/helpers/`
|
||||||
ctx.Step(`^the response should be "{\"([^"]*)\":\"([^"]*)\"}"$`, func(key, value string) error {
|
|
||||||
// ...
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Current Implementation
|
Helpers provide robust waiting mechanisms for async operations:
|
||||||
|
|
||||||
### Step Definition Strategy
|
- **Timeout Support**: All functions include timeout parameters
|
||||||
|
- **Polling**: Uses context-based polling with configurable intervals
|
||||||
|
- **Common Patterns**: Covers server readiness, config reload, API availability
|
||||||
|
|
||||||
1. **First eliminate "undefined" warnings** by using Godog's exact suggested patterns
|
**Available Helpers:**
|
||||||
2. **Return `godog.ErrPending`** initially to confirm pattern matching works
|
- `waitForServerReady()` - Waits for server to be ready
|
||||||
3. **Then implement actual validation** logic
|
- `waitForConfigReload()` - Detects configuration changes
|
||||||
|
- `waitForCondition()` - Generic condition waiting
|
||||||
|
- `waitForV2APIEnabled()` - Checks v2 API availability
|
||||||
|
|
||||||
### Files
|
### 4. Parallel Testing
|
||||||
|
|
||||||
- `suite.go`: Test suite initialization and server management
|
**Location:** `pkg/bdd/parallel/`
|
||||||
- `testserver/`: Test server and client implementation
|
|
||||||
- `steps/`: Step definitions for each feature
|
|
||||||
|
|
||||||
## Debugging "Undefined" Steps
|
Parallel execution infrastructure for CI/CD optimization:
|
||||||
|
|
||||||
If you see "undefined" warnings:
|
- **Port Management**: `PortManager` allocates unique ports
|
||||||
|
- **Resource Monitoring**: Tracks memory, goroutines, CPU usage
|
||||||
|
- **Controlled Parallelism**: `ParallelTestRunner` limits concurrency
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Thread-safe port allocation
|
||||||
|
- Resource limit enforcement
|
||||||
|
- Timeout detection
|
||||||
|
- Comprehensive monitoring
|
||||||
|
|
||||||
|
### 5. Feature Contexts
|
||||||
|
|
||||||
|
**Location:** `pkg/bdd/context/`
|
||||||
|
|
||||||
|
Feature-specific test contexts for better organization:
|
||||||
|
|
||||||
|
- **AuthContext**: User management and authentication
|
||||||
|
- **ConfigContext**: Configuration file handling
|
||||||
|
- **Extensible**: Easy to add new feature contexts
|
||||||
|
|
||||||
|
## 🚀 Test Execution
|
||||||
|
|
||||||
|
### Running All Tests
|
||||||
|
|
||||||
1. Run the tests to see Godog's suggested pattern:
|
|
||||||
```bash
|
```bash
|
||||||
go test ./features/... -v
|
# Default: Run all features sequentially
|
||||||
|
go test ./features/...
|
||||||
|
|
||||||
|
# With environment variables
|
||||||
|
DLC_DATABASE_HOST=localhost DLC_DATABASE_PORT=5432 \
|
||||||
|
DLC_DATABASE_USER=postgres DLC_DATABASE_PASSWORD=postgres \
|
||||||
|
DLC_DATABASE_NAME=dance_lessons_coach_bdd_test \
|
||||||
|
DLC_DATABASE_SSL_MODE=disable \
|
||||||
|
go test ./features/...
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Copy the **exact regex pattern** from the error message
|
### Feature-Specific Testing
|
||||||
3. Copy the **exact parameter names** (`arg1, arg2`, etc.)
|
|
||||||
4. Update your step definition to match exactly
|
|
||||||
|
|
||||||
## Common Mistakes
|
```bash
|
||||||
|
# Test specific feature
|
||||||
|
./scripts/test-feature.sh greet
|
||||||
|
|
||||||
The "undefined" warnings are **not a Godog bug** - they occur when step definitions don't match Godog's expected patterns exactly:
|
# Test with specific tags
|
||||||
|
./scripts/test-by-tag.sh @smoke greet
|
||||||
|
```
|
||||||
|
|
||||||
- Using different regex patterns than what Godog suggests
|
### Parallel Testing
|
||||||
- Using descriptive parameter names instead of `arg1, arg2`
|
|
||||||
- Not escaping quotes properly in JSON patterns
|
|
||||||
- Trying to be "clever" with regex optimization
|
|
||||||
|
|
||||||
**Solution**: Always use the exact pattern and parameter names that Godog suggests in its error messages.
|
```bash
|
||||||
|
# Run all features in parallel
|
||||||
|
./scripts/test-all-features-parallel.sh
|
||||||
|
|
||||||
## Best Practices
|
# Run specific features in parallel
|
||||||
|
# (Requires PostgreSQL container running)
|
||||||
|
```
|
||||||
|
|
||||||
1. **Follow Godog's suggestions exactly** - Copy-paste the pattern and parameter names
|
### Tag-Based Testing
|
||||||
2. **Test pattern matching first** - Use `godog.ErrPending` to verify patterns work
|
|
||||||
3. **Then implement logic** - Replace `godog.ErrPending` with actual validation
|
|
||||||
4. **Don't over-optimize regex** - Use the patterns Godog provides, even if they seem verbose
|
|
||||||
5. **One pattern per step type** - Use generic patterns to cover similar steps
|
|
||||||
|
|
||||||
## Why This Matters
|
```bash
|
||||||
|
# List available tags
|
||||||
|
./scripts/run-bdd-tests.sh list-tags
|
||||||
|
|
||||||
Godog's step matching is **very specific by design**:
|
# Run smoke tests
|
||||||
- It needs to reliably match feature file steps to code
|
./scripts/run-bdd-tests.sh run @smoke
|
||||||
- It provides exact patterns to ensure consistency
|
|
||||||
- Following its suggestions guarantees your steps will be recognized
|
|
||||||
|
|
||||||
**Remember**: The "undefined" warnings are Godog telling you exactly how to fix your step definitions!
|
# Run critical tests for auth
|
||||||
|
./scripts/run-bdd-tests.sh run @critical @auth
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Test Organization
|
||||||
|
|
||||||
|
### Feature Structure
|
||||||
|
|
||||||
|
Each feature follows this structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
features/{feature}/
|
||||||
|
├── {feature}.feature # Gherkin scenarios
|
||||||
|
├── {feature}-test-config.yaml # Feature-specific config
|
||||||
|
└── {feature}_test.go # Go test runner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
Feature-specific YAML files define test environment:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# features/greet/greet-test-config.yaml
|
||||||
|
server:
|
||||||
|
host: "127.0.0.1"
|
||||||
|
port: 9194
|
||||||
|
|
||||||
|
database:
|
||||||
|
host: "localhost"
|
||||||
|
port: 5432
|
||||||
|
name: "dance_lessons_coach_greet_test"
|
||||||
|
|
||||||
|
api:
|
||||||
|
v2_enabled: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tagging System
|
||||||
|
|
||||||
|
Comprehensive tagging for selective test execution:
|
||||||
|
|
||||||
|
- **Feature Tags**: `@auth`, `@config`, `@greet`, `@health`, `@jwt`
|
||||||
|
- **Priority Tags**: `@smoke`, `@critical`, `@basic`, `@advanced`
|
||||||
|
- **Component Tags**: `@api`, `@v2`, `@database`, `@security`
|
||||||
|
|
||||||
|
See `features/BDD_TAGS.md` for complete documentation.
|
||||||
|
|
||||||
|
## 🔧 Database Management
|
||||||
|
|
||||||
|
### Database Creation
|
||||||
|
|
||||||
|
The framework handles database creation automatically:
|
||||||
|
|
||||||
|
1. **PostgreSQL Container**: Uses Docker (`dance-lessons-coach-postgres`)
|
||||||
|
2. **Feature Databases**: Creates `dance_lessons_coach_{feature}_test` per feature
|
||||||
|
3. **Cleanup**: Automatically drops databases after tests
|
||||||
|
|
||||||
|
**Database Creation Flow:**
|
||||||
|
1. Check if database exists
|
||||||
|
2. Create if missing (`createdb` command)
|
||||||
|
3. Run tests with isolated database
|
||||||
|
4. Cleanup (`dropdb` command)
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Database settings come from:
|
||||||
|
- Environment variables (`DLC_DATABASE_*`)
|
||||||
|
- Feature-specific config files
|
||||||
|
- Default values for development
|
||||||
|
|
||||||
|
## 🧪 Best Practices
|
||||||
|
|
||||||
|
### Step Definition Patterns
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ DO: Use Godog's exact regex patterns
|
||||||
|
ctx.Step(`^I request a greeting for "([^"]*)"$`, sc.iRequestAGreetingFor)
|
||||||
|
|
||||||
|
// ❌ DON'T: Use different patterns
|
||||||
|
ctx.Step(`^I request greeting "(.*)"$`, sc.iRequestAGreetingFor)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Isolation
|
||||||
|
|
||||||
|
- Each feature has unique port and database
|
||||||
|
- No shared state between features
|
||||||
|
- Cleanup after each test run
|
||||||
|
- Feature-specific configuration
|
||||||
|
|
||||||
|
### Synchronization
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ DO: Use helpers for async operations
|
||||||
|
helpers.waitForServerReady(client, 30*time.Second)
|
||||||
|
|
||||||
|
// ❌ DON'T: Use fixed sleep times
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Context Management
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ DO: Use feature-specific contexts
|
||||||
|
switch featureName {
|
||||||
|
case "auth":
|
||||||
|
authCtx = context.NewAuthContext(client)
|
||||||
|
context.InitializeAuthContext(ctx, client)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📈 Performance Optimization
|
||||||
|
|
||||||
|
### Parallel Execution
|
||||||
|
|
||||||
|
- Use `scripts/test-all-features-parallel.sh` for CI/CD
|
||||||
|
- Limit parallelism based on system resources
|
||||||
|
- Monitor resource usage with `ResourceMonitor`
|
||||||
|
|
||||||
|
### Selective Testing
|
||||||
|
|
||||||
|
- Run only relevant tests with tag filtering
|
||||||
|
- Use `@smoke` for quick validation
|
||||||
|
- Use `@critical` for essential path testing
|
||||||
|
|
||||||
|
### Resource Management
|
||||||
|
|
||||||
|
- Set appropriate timeouts
|
||||||
|
- Limit maximum goroutines
|
||||||
|
- Monitor memory usage
|
||||||
|
- Cleanup resources promptly
|
||||||
|
|
||||||
|
## 🔧 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
| Issue | Cause | Solution |
|
||||||
|
|-------|-------|----------|
|
||||||
|
| Undefined steps | Step pattern mismatch | Use Godog's exact suggested patterns |
|
||||||
|
| Port conflicts | Multiple servers | Check port allocation in config files |
|
||||||
|
| Database connection | PostgreSQL not running | Start with `docker compose up -d postgres` |
|
||||||
|
| Test isolation | Shared state | Verify unique ports/databases per feature |
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verbose output
|
||||||
|
go test ./features/... -v
|
||||||
|
|
||||||
|
# Check specific feature
|
||||||
|
cd features/greet && go test -v .
|
||||||
|
|
||||||
|
# List available tags
|
||||||
|
./scripts/run-bdd-tests.sh list-tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
- **ADR 0024**: BDD Test Organization and Isolation Strategy
|
||||||
|
- **BDD_TAGS.md**: Complete tag reference
|
||||||
|
- **Godog Documentation**: https://github.com/cucumber/godog
|
||||||
|
|
||||||
|
## 🎯 Future Enhancements
|
||||||
|
|
||||||
|
- **Test Impact Analysis**: Track which tests are affected by code changes
|
||||||
|
- **Flaky Test Detection**: Automatically identify and quarantine flaky tests
|
||||||
|
- **Performance Benchmarking**: Monitor test execution times
|
||||||
|
- **AI-Assisted Testing**: Automated test generation and optimization
|
||||||
|
|
||||||
|
This BDD framework provides a robust foundation for behavior-driven development in the dance-lessons-coach project, ensuring test reliability, maintainability, and scalability.
|
||||||
@@ -2,6 +2,7 @@ package context
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"dance-lessons-coach/pkg/bdd/testserver"
|
"dance-lessons-coach/pkg/bdd/testserver"
|
||||||
|
|
||||||
"github.com/cucumber/godog"
|
"github.com/cucumber/godog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package context
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"dance-lessons-coach/pkg/bdd/testserver"
|
"dance-lessons-coach/pkg/bdd/testserver"
|
||||||
|
|
||||||
"github.com/cucumber/godog"
|
"github.com/cucumber/godog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dance-lessons-coach/pkg/bdd/testserver"
|
"dance-lessons-coach/pkg/bdd/testserver"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,19 @@ type ConfigSteps struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigSteps(client *testserver.Client) *ConfigSteps {
|
func NewConfigSteps(client *testserver.Client) *ConfigSteps {
|
||||||
|
// Get feature-specific config path
|
||||||
|
feature := os.Getenv("FEATURE")
|
||||||
|
var configFilePath string
|
||||||
|
|
||||||
|
if feature != "" {
|
||||||
|
configFilePath = fmt.Sprintf("features/%s/%s-test-config.yaml", feature, feature)
|
||||||
|
} else {
|
||||||
|
configFilePath = "test-config.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
return &ConfigSteps{
|
return &ConfigSteps{
|
||||||
client: client,
|
client: client,
|
||||||
configFilePath: "test-config.yaml",
|
configFilePath: configFilePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -27,8 +28,37 @@ type Server struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() *Server {
|
func NewServer() *Server {
|
||||||
|
// Get feature-specific port from configuration
|
||||||
|
feature := os.Getenv("FEATURE")
|
||||||
|
port := 9191 // Default port
|
||||||
|
|
||||||
|
if feature != "" {
|
||||||
|
// Try to read port from feature-specific config
|
||||||
|
configPath := fmt.Sprintf("features/%s/%s-test-config.yaml", feature, feature)
|
||||||
|
if _, statErr := os.Stat(configPath); statErr == nil {
|
||||||
|
// Read config file to get port
|
||||||
|
content, err := os.ReadFile(configPath)
|
||||||
|
if err == nil {
|
||||||
|
// Simple YAML parsing to extract port
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, "port:") {
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
portStr := strings.TrimSpace(parts[1])
|
||||||
|
if p, err := strconv.Atoi(portStr); err == nil {
|
||||||
|
port = p
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
port: 9191,
|
port: port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +101,16 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
// monitorConfigFile monitors the test config file for changes and reloads configuration
|
// monitorConfigFile monitors the test config file for changes and reloads configuration
|
||||||
func (s *Server) monitorConfigFile() {
|
func (s *Server) monitorConfigFile() {
|
||||||
testConfigPath := "test-config.yaml"
|
// Get feature-specific config path
|
||||||
|
feature := os.Getenv("FEATURE")
|
||||||
|
var testConfigPath string
|
||||||
|
|
||||||
|
if feature != "" {
|
||||||
|
testConfigPath = fmt.Sprintf("features/%s/%s-test-config.yaml", feature, feature)
|
||||||
|
} else {
|
||||||
|
testConfigPath = "test-config.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
lastModTime := time.Time{}
|
lastModTime := time.Time{}
|
||||||
fileExists := false
|
fileExists := false
|
||||||
|
|
||||||
@@ -151,7 +190,39 @@ func (s *Server) ReloadConfig() error {
|
|||||||
|
|
||||||
// initDBConnection initializes a direct database connection for cleanup operations
|
// initDBConnection initializes a direct database connection for cleanup operations
|
||||||
func (s *Server) initDBConnection() error {
|
func (s *Server) initDBConnection() error {
|
||||||
cfg := createTestConfig(s.port)
|
// Get feature-specific configuration
|
||||||
|
feature := os.Getenv("FEATURE")
|
||||||
|
var cfg *config.Config
|
||||||
|
|
||||||
|
if feature != "" {
|
||||||
|
// Try to load feature-specific config
|
||||||
|
configPath := fmt.Sprintf("features/%s/%s-test-config.yaml", feature, feature)
|
||||||
|
if _, err := os.Stat(configPath); err == nil {
|
||||||
|
v := viper.New()
|
||||||
|
v.SetConfigFile(configPath)
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
|
||||||
|
if readErr := v.ReadInConfig(); readErr == nil {
|
||||||
|
var featureCfg config.Config
|
||||||
|
if unmarshalErr := v.Unmarshal(&featureCfg); unmarshalErr == nil {
|
||||||
|
// Set default values if not configured
|
||||||
|
if featureCfg.Auth.JWTSecret == "" {
|
||||||
|
featureCfg.Auth.JWTSecret = "default-secret-key-please-change-in-production"
|
||||||
|
}
|
||||||
|
if featureCfg.Auth.AdminMasterPassword == "" {
|
||||||
|
featureCfg.Auth.AdminMasterPassword = "admin123"
|
||||||
|
}
|
||||||
|
cfg = &featureCfg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to default config if feature-specific not available
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = createTestConfig(s.port)
|
||||||
|
}
|
||||||
|
|
||||||
dsn := fmt.Sprintf(
|
dsn := fmt.Sprintf(
|
||||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||||
cfg.Database.Host,
|
cfg.Database.Host,
|
||||||
@@ -162,10 +233,10 @@ func (s *Server) initDBConnection() error {
|
|||||||
cfg.Database.SSLMode,
|
cfg.Database.SSLMode,
|
||||||
)
|
)
|
||||||
|
|
||||||
var err error
|
var dbErr error
|
||||||
s.db, err = sql.Open("postgres", dsn)
|
s.db, dbErr = sql.Open("postgres", dsn)
|
||||||
if err != nil {
|
if dbErr != nil {
|
||||||
return fmt.Errorf("failed to open database connection: %w", err)
|
return fmt.Errorf("failed to open database connection: %w", dbErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the connection
|
// Test the connection
|
||||||
@@ -329,16 +400,31 @@ func (s *Server) GetBaseURL() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createTestConfig(port int) *config.Config {
|
func createTestConfig(port int) *config.Config {
|
||||||
// Check if there's a test config file (used by config hot reloading tests)
|
// Check for feature-specific config file first
|
||||||
// If it exists, use it. Otherwise, use default config.
|
// This supports the new modular BDD test structure
|
||||||
testConfigPath := "test-config.yaml"
|
feature := os.Getenv("FEATURE")
|
||||||
if _, err := os.Stat(testConfigPath); err == nil {
|
var configPaths []string
|
||||||
// Test config file exists, use it
|
|
||||||
|
if feature != "" {
|
||||||
|
// Feature-specific config takes precedence
|
||||||
|
configPaths = []string{
|
||||||
|
fmt.Sprintf("features/%s/%s-test-config.yaml", feature, feature),
|
||||||
|
"test-config.yaml", // Fallback to legacy config
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When running all features, use legacy config
|
||||||
|
configPaths = []string{"test-config.yaml"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try each config path in order
|
||||||
|
for _, configPath := range configPaths {
|
||||||
|
if _, err := os.Stat(configPath); err == nil {
|
||||||
|
// Config file exists, use it
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
v.SetConfigFile(testConfigPath)
|
v.SetConfigFile(configPath)
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
|
|
||||||
// Read the test config file
|
// Read the config file
|
||||||
if err := v.ReadInConfig(); err == nil {
|
if err := v.ReadInConfig(); err == nil {
|
||||||
var cfg config.Config
|
var cfg config.Config
|
||||||
if err := v.Unmarshal(&cfg); err == nil {
|
if err := v.Unmarshal(&cfg); err == nil {
|
||||||
@@ -353,10 +439,12 @@ func createTestConfig(port int) *config.Config {
|
|||||||
cfg.Auth.AdminMasterPassword = "admin123"
|
cfg.Auth.AdminMasterPassword = "admin123"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("config", configPath).Msg("Using test config file")
|
||||||
return &cfg
|
return &cfg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// No test config file, use default config
|
// No test config file, use default config
|
||||||
cfg, err := config.LoadConfig()
|
cfg, err := config.LoadConfig()
|
||||||
|
|||||||
64
scripts/test-by-tag.sh
Executable file
64
scripts/test-by-tag.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Tag-Based Test Runner Script
|
||||||
|
# Runs BDD tests with specific tags
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Check if tag is provided
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "❌ Usage: $0 <tag> [feature]"
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 @smoke # Run all smoke tests"
|
||||||
|
echo " $0 @critical auth # Run critical auth tests"
|
||||||
|
echo " $0 @v2 greet # Run v2 greet tests"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAG=$1
|
||||||
|
FEATURE=""
|
||||||
|
|
||||||
|
# Check if feature is also provided
|
||||||
|
if [ $# -ge 2 ]; then
|
||||||
|
FEATURE=$2
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
|
||||||
|
cd $SCRIPTS_DIR/..
|
||||||
|
|
||||||
|
echo "🧪 Running tests with tag: $TAG"
|
||||||
|
|
||||||
|
if [ -n "$FEATURE" ]; then
|
||||||
|
echo "📁 Feature: $FEATURE"
|
||||||
|
|
||||||
|
# Set feature-specific environment variables
|
||||||
|
DATABASE="dance_lessons_coach_${FEATURE}_test"
|
||||||
|
CONFIG="features/${FEATURE}/${FEATURE}-test-config.yaml"
|
||||||
|
|
||||||
|
export DLC_DATABASE_HOST="localhost"
|
||||||
|
export DLC_DATABASE_PORT="5432"
|
||||||
|
export DLC_DATABASE_USER="postgres"
|
||||||
|
export DLC_DATABASE_PASSWORD="postgres"
|
||||||
|
export DLC_DATABASE_NAME="${DATABASE}"
|
||||||
|
export DLC_DATABASE_SSL_MODE="disable"
|
||||||
|
export DLC_CONFIG_FILE="${CONFIG}"
|
||||||
|
|
||||||
|
# Run feature-specific tests with tag filtering
|
||||||
|
echo "🚀 Running tagged tests for ${FEATURE} feature..."
|
||||||
|
cd "features/${FEATURE}"
|
||||||
|
FEATURE=${FEATURE} go test -v -tags="$TAG" .
|
||||||
|
else
|
||||||
|
echo "🚀 Running tagged tests for all features..."
|
||||||
|
|
||||||
|
# Run all tests with tag filtering
|
||||||
|
# Note: Godog tag filtering is done through the godog command line
|
||||||
|
# For Go test integration, we need to use a different approach
|
||||||
|
echo "⚠️ Tag filtering for all features requires godog command directly"
|
||||||
|
echo "📝 Running: godog --tags=$TAG features/"
|
||||||
|
|
||||||
|
# This would require setting up the test server manually
|
||||||
|
# For now, we'll show how it would work
|
||||||
|
echo "⏳ This functionality would require additional implementation"
|
||||||
|
echo "💡 Consider using: godog --tags=$TAG features/"
|
||||||
|
echo " after starting the test server manually"
|
||||||
|
fi
|
||||||
168
scripts/test-feature.sh
Executable file
168
scripts/test-feature.sh
Executable file
@@ -0,0 +1,168 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Feature-Specific Test Runner Script
|
||||||
|
# Runs BDD tests for a specific feature with proper isolation
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Check if feature name is provided
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "❌ Usage: $0 <feature-name>"
|
||||||
|
echo "Available features: auth, config, greet, health, jwt"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FEATURE=$1
|
||||||
|
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
|
||||||
|
cd $SCRIPTS_DIR/..
|
||||||
|
|
||||||
|
# Validate feature name
|
||||||
|
case $FEATURE in
|
||||||
|
auth|config|greet|health|jwt)
|
||||||
|
echo "🧪 Setting up ${FEATURE} feature tests..."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "❌ Invalid feature: $FEATURE"
|
||||||
|
echo "Available features: auth, config, greet, health, jwt"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Feature-specific configuration
|
||||||
|
DATABASE="dance_lessons_coach_${FEATURE}_test"
|
||||||
|
CONFIG="features/${FEATURE}/${FEATURE}-test-config.yaml"
|
||||||
|
PORT=$(grep "port:" "$CONFIG" | awk '{print $2}')
|
||||||
|
|
||||||
|
# Setup function
|
||||||
|
setup_feature_environment() {
|
||||||
|
echo "🧪 Setting up ${FEATURE} feature tests..."
|
||||||
|
|
||||||
|
# Check if we're in CI environment
|
||||||
|
if [ -n "$GITHUB_ACTIONS" ] || [ -n "$GITEA_ACTIONS" ]; then
|
||||||
|
# CI environment - PostgreSQL is already running as a service
|
||||||
|
echo "🏗️ CI environment detected"
|
||||||
|
|
||||||
|
# Create database if it doesn't exist
|
||||||
|
if ! psql -h postgres -p 5432 -U postgres -lqt | cut -d \| -f 1 | grep -qw "${DATABASE}"; then
|
||||||
|
echo "📦 Creating ${FEATURE} test database..."
|
||||||
|
createdb -h postgres -p 5432 -U postgres "${DATABASE}"
|
||||||
|
echo "✅ ${FEATURE} test database created successfully!"
|
||||||
|
else
|
||||||
|
echo "✅ ${FEATURE} test database already exists"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Local environment - use docker compose
|
||||||
|
echo "💻 Local environment detected"
|
||||||
|
|
||||||
|
# Check if PostgreSQL container is running, start it if not
|
||||||
|
if ! docker ps --format '{{.Names}}' | grep -q "^dance-lessons-coach-postgres$"; then
|
||||||
|
echo "🐋 Starting PostgreSQL container..."
|
||||||
|
docker compose up -d postgres
|
||||||
|
|
||||||
|
# Wait for PostgreSQL to be ready
|
||||||
|
echo "⏳ Waiting for PostgreSQL to be ready..."
|
||||||
|
max_attempts=30
|
||||||
|
attempt=0
|
||||||
|
while [ $attempt -lt $max_attempts ]; do
|
||||||
|
if docker exec dance-lessons-coach-postgres pg_isready -U postgres 2>/dev/null; then
|
||||||
|
echo "✅ PostgreSQL is ready!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $attempt -eq $max_attempts ]; then
|
||||||
|
echo "❌ PostgreSQL failed to start"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ PostgreSQL container is already running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create feature-specific database
|
||||||
|
if docker exec dance-lessons-coach-postgres psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "${DATABASE}"; then
|
||||||
|
echo "✅ ${FEATURE} test database already exists"
|
||||||
|
else
|
||||||
|
echo "📦 Creating ${FEATURE} test database..."
|
||||||
|
if docker exec dance-lessons-coach-postgres createdb -U postgres "${DATABASE}"; then
|
||||||
|
echo "✅ ${FEATURE} test database created successfully!"
|
||||||
|
else
|
||||||
|
echo "❌ Failed to create ${FEATURE} test database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run tests function
|
||||||
|
run_feature_tests() {
|
||||||
|
echo "🚀 Running ${FEATURE} feature tests..."
|
||||||
|
|
||||||
|
# Set feature-specific environment variables
|
||||||
|
export DLC_DATABASE_HOST="localhost"
|
||||||
|
export DLC_DATABASE_PORT="5432"
|
||||||
|
export DLC_DATABASE_USER="postgres"
|
||||||
|
export DLC_DATABASE_PASSWORD="postgres"
|
||||||
|
export DLC_DATABASE_NAME="${DATABASE}"
|
||||||
|
export DLC_DATABASE_SSL_MODE="disable"
|
||||||
|
export DLC_CONFIG_FILE="${CONFIG}"
|
||||||
|
|
||||||
|
# Run tests with proper coverage measurement
|
||||||
|
set +e
|
||||||
|
test_output=$(go test ./features/${FEATURE}/... -v -cover -coverpkg=./... -coverprofile=coverage-${FEATURE}.out 2>&1)
|
||||||
|
test_exit_code=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "$test_output"
|
||||||
|
|
||||||
|
# Check for undefined steps
|
||||||
|
if echo "$test_output" | grep -q "undefined"; then
|
||||||
|
echo "❌ FAILED: Found undefined steps in ${FEATURE} tests"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for pending steps
|
||||||
|
if echo "$test_output" | grep -q "pending"; then
|
||||||
|
echo "❌ FAILED: Found pending steps in ${FEATURE} tests"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for skipped steps
|
||||||
|
if echo "$test_output" | grep -q "skipped"; then
|
||||||
|
echo "❌ FAILED: Found skipped steps in ${FEATURE} tests"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if tests passed
|
||||||
|
if [ $test_exit_code -eq 0 ]; then
|
||||||
|
echo "✅ All ${FEATURE} feature tests passed successfully!"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "❌ ${FEATURE} feature tests failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup function
|
||||||
|
cleanup_feature_environment() {
|
||||||
|
echo "🧹 Cleaning up ${FEATURE} feature tests..."
|
||||||
|
|
||||||
|
# Check if we're in CI environment
|
||||||
|
if [ -n "$GITHUB_ACTIONS" ] || [ -n "$GITEA_ACTIONS" ]; then
|
||||||
|
# CI environment - drop database
|
||||||
|
echo "🗑️ Dropping ${FEATURE} test database..."
|
||||||
|
dropdb -h postgres -p 5432 -U postgres "${DATABASE}" 2>/dev/null || true
|
||||||
|
echo "✅ ${FEATURE} test database cleaned up"
|
||||||
|
else
|
||||||
|
# Local environment - drop database
|
||||||
|
echo "🗑️ Dropping ${FEATURE} test database..."
|
||||||
|
docker exec dance-lessons-coach-postgres dropdb -U postgres "${DATABASE}" 2>/dev/null || true
|
||||||
|
echo "✅ ${FEATURE} test database cleaned up"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
setup_feature_environment
|
||||||
|
run_feature_tests
|
||||||
|
cleanup_feature_environment
|
||||||
110
scripts/validate-isolation.sh
Executable file
110
scripts/validate-isolation.sh
Executable file
@@ -0,0 +1,110 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Isolation Validation Script
|
||||||
|
# Validates that feature isolation is working correctly
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔍 Validating BDD test isolation..."
|
||||||
|
|
||||||
|
# Check feature directories exist
|
||||||
|
echo "📁 Checking feature directory structure..."
|
||||||
|
for feature in auth config greet health jwt; do
|
||||||
|
if [ ! -d "features/${feature}" ]; then
|
||||||
|
echo "❌ Missing features/${feature} directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for feature files
|
||||||
|
if [ -z "$(find features/${feature} -name "*.feature" -type f)" ]; then
|
||||||
|
echo "❌ No feature files found in features/${feature}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for config files
|
||||||
|
if [ ! -f "features/${feature}/${feature}-test-config.yaml" ]; then
|
||||||
|
echo "❌ Missing config file for ${feature} feature"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ ${feature} feature structure validated"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for unique ports
|
||||||
|
echo "🔌 Checking for unique port assignments..."
|
||||||
|
port_auth=$(grep "port:" "features/auth/auth-test-config.yaml" | awk '{print $2}')
|
||||||
|
port_config=$(grep "port:" "features/config/config-test-config.yaml" | awk '{print $2}')
|
||||||
|
port_greet=$(grep "port:" "features/greet/greet-test-config.yaml" | awk '{print $2}')
|
||||||
|
port_health=$(grep "port:" "features/health/health-test-config.yaml" | awk '{print $2}')
|
||||||
|
port_jwt=$(grep "port:" "features/jwt/jwt-test-config.yaml" | awk '{print $2}')
|
||||||
|
|
||||||
|
# Check for port conflicts
|
||||||
|
if [ "$port_auth" = "$port_config" ] || [ "$port_auth" = "$port_greet" ] || [ "$port_auth" = "$port_health" ] || [ "$port_auth" = "$port_jwt" ]; then
|
||||||
|
echo "❌ Port conflict detected with auth port $port_auth"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$port_config" = "$port_greet" ] || [ "$port_config" = "$port_health" ] || [ "$port_config" = "$port_jwt" ]; then
|
||||||
|
echo "❌ Port conflict detected with config port $port_config"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$port_greet" = "$port_health" ] || [ "$port_greet" = "$port_jwt" ]; then
|
||||||
|
echo "❌ Port conflict detected with greet port $port_greet"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$port_health" = "$port_jwt" ]; then
|
||||||
|
echo "❌ Port conflict detected with health port $port_health"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ All features have unique ports"
|
||||||
|
|
||||||
|
# Check for unique database names
|
||||||
|
echo "🗃️ Checking for unique database names..."
|
||||||
|
db_auth="dance_lessons_coach_auth_test"
|
||||||
|
db_config="dance_lessons_coach_config_test"
|
||||||
|
db_greet="dance_lessons_coach_greet_test"
|
||||||
|
db_health="dance_lessons_coach_health_test"
|
||||||
|
db_jwt="dance_lessons_coach_jwt_test"
|
||||||
|
|
||||||
|
# Check for database name conflicts
|
||||||
|
if [ "$db_auth" = "$db_config" ] || [ "$db_auth" = "$db_greet" ] || [ "$db_auth" = "$db_health" ] || [ "$db_auth" = "$db_jwt" ]; then
|
||||||
|
echo "❌ Database conflict detected with auth database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$db_config" = "$db_greet" ] || [ "$db_config" = "$db_health" ] || [ "$db_config" = "$db_jwt" ]; then
|
||||||
|
echo "❌ Database conflict detected with config database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$db_greet" = "$db_health" ] || [ "$db_greet" = "$db_jwt" ]; then
|
||||||
|
echo "❌ Database conflict detected with greet database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$db_health" = "$db_jwt" ]; then
|
||||||
|
echo "❌ Database conflict detected with health database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ All features have unique database names"
|
||||||
|
|
||||||
|
# Test that each feature can be run independently
|
||||||
|
echo "🧪 Testing feature independence..."
|
||||||
|
for feature in auth config greet health jwt; do
|
||||||
|
echo "Testing ${feature} feature..."
|
||||||
|
|
||||||
|
# Try to run the feature test script with setup only
|
||||||
|
if ! bash scripts/test-feature.sh $feature 2>&1 | grep -q "Setting up ${feature} feature tests"; then
|
||||||
|
echo "❌ Failed to setup ${feature} feature tests"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ ${feature} feature can be set up independently"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ All isolation validations passed!"
|
||||||
|
echo "🎉 BDD test isolation is working correctly"
|
||||||
Reference in New Issue
Block a user