✨ feat: implement schema-per-scenario isolation for BDD tests
- Add BDD_SCHEMA_ISOLATION env var to enable schema-per-scenario mode - Generate unique schema names using SHA256 hash of feature+scenario - Implement SetupScenarioSchema() and TeardownScenarioSchema() methods - Handle search_path configuration for schema isolation - Use CASCADE drop to clean up all scenario-created DB objects - Add isSchemaIsolationEnabled() helper to suite.go - Update cleanup flow: skip table clearing when schema isolation is active - Add ADR 0025 documenting isolation strategies and decision rationale Activation: Set BDD_SCHEMA_ISOLATION=true to enable Debug: Set BDD_ENABLE_CLEANUP_LOGS=true for verbose isolation logging Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -20,6 +20,11 @@ func isCleanupLoggingEnabled() bool {
|
||||
return os.Getenv("BDD_ENABLE_CLEANUP_LOGS") == "true"
|
||||
}
|
||||
|
||||
// isSchemaIsolationEnabled returns true if BDD_SCHEMA_ISOLATION environment variable is set to "true"
|
||||
func isSchemaIsolationEnabled() bool {
|
||||
return os.Getenv("BDD_SCHEMA_ISOLATION") == "true"
|
||||
}
|
||||
|
||||
func InitializeTestSuite(ctx *godog.TestSuiteContext) {
|
||||
ctx.BeforeSuite(func() {
|
||||
// Small delay to ensure any previous server instances are fully cleaned up
|
||||
@@ -37,8 +42,24 @@ func InitializeTestSuite(ctx *godog.TestSuiteContext) {
|
||||
|
||||
sc := ctx.ScenarioContext()
|
||||
sc.BeforeScenario(func(s *godog.Scenario) {
|
||||
// Get feature name from context or environment
|
||||
feature := os.Getenv("FEATURE")
|
||||
if feature == "" {
|
||||
// Try to extract feature from scenario tags or path
|
||||
feature = "unknown"
|
||||
}
|
||||
|
||||
if isCleanupLoggingEnabled() {
|
||||
log.Info().Str("scenario", s.Name).Msg("CLEANUP: Scenario starting")
|
||||
log.Info().Str("feature", feature).Str("scenario", s.Name).Msg("CLEANUP: Scenario starting")
|
||||
}
|
||||
|
||||
// Setup schema isolation if enabled
|
||||
if sharedServer != nil {
|
||||
if err := sharedServer.SetupScenarioSchema(feature, s.Name); err != nil {
|
||||
if isCleanupLoggingEnabled() {
|
||||
log.Warn().Err(err).Msg("ISOLATION: Failed to setup scenario schema")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -48,17 +69,27 @@ func InitializeTestSuite(ctx *godog.TestSuiteContext) {
|
||||
}
|
||||
|
||||
if sharedServer != nil {
|
||||
// Teardown schema isolation if enabled
|
||||
if teardownErr := sharedServer.TeardownScenarioSchema(); teardownErr != nil {
|
||||
if isCleanupLoggingEnabled() {
|
||||
log.Warn().Err(teardownErr).Msg("ISOLATION: Failed to teardown scenario schema")
|
||||
}
|
||||
}
|
||||
|
||||
// Reset JWT secrets after every scenario to prevent pollution
|
||||
// Note: This is still needed for in-memory state even with schema isolation
|
||||
if resetErr := sharedServer.ResetJWTSecrets(); resetErr != nil {
|
||||
if isCleanupLoggingEnabled() {
|
||||
log.Warn().Err(resetErr).Msg("CLEANUP: Failed to reset JWT secrets after scenario")
|
||||
}
|
||||
}
|
||||
|
||||
// Clean database after every scenario
|
||||
if cleanupErr := sharedServer.CleanupDatabase(); cleanupErr != nil {
|
||||
if isCleanupLoggingEnabled() {
|
||||
log.Warn().Err(cleanupErr).Msg("CLEANUP: Failed to cleanup database after scenario")
|
||||
// Clean database after every scenario (only if schema isolation is disabled)
|
||||
if !isSchemaIsolationEnabled() {
|
||||
if cleanupErr := sharedServer.CleanupDatabase(); cleanupErr != nil {
|
||||
if isCleanupLoggingEnabled() {
|
||||
log.Warn().Err(cleanupErr).Msg("CLEANUP: Failed to cleanup database after scenario")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user