package steps import ( "crypto/sha256" "encoding/hex" "sync" ) // ScenarioState holds per-scenario state for step definitions // This prevents state pollution between scenarios running in the same test process type ScenarioState struct { LastToken string FirstToken string LastUserID uint LastSecret string LastError string MagicLinkEmail string MagicLinkToken string // Add more fields as needed for other step types } // scenarioStateManager manages per-scenario state isolation type scenarioStateManager struct { mu sync.RWMutex states map[string]*ScenarioState } var globalStateManager *scenarioStateManager var once sync.Once // GetScenarioStateManager returns the singleton scenario state manager func GetScenarioStateManager() *scenarioStateManager { once.Do(func() { globalStateManager = &scenarioStateManager{ states: make(map[string]*ScenarioState), } }) return globalStateManager } // scenarioKey generates a unique key for a scenario func scenarioKey(scenario string) string { // Use SHA256 hash to create a consistent, bounded-length key hash := sha256.Sum256([]byte(scenario)) return hex.EncodeToString(hash[:]) } // GetState returns the state for a given scenario, creating it if necessary func (sm *scenarioStateManager) GetState(scenario string) *ScenarioState { sm.mu.RLock() key := scenarioKey(scenario) state, exists := sm.states[key] sm.mu.RUnlock() if exists { return state } sm.mu.Lock() defer sm.mu.Unlock() // Double-check after acquiring write lock if state, exists = sm.states[key]; exists { return state } state = &ScenarioState{} sm.states[key] = state return state } // ClearState removes the state for a given scenario func (sm *scenarioStateManager) ClearState(scenario string) { sm.mu.Lock() defer sm.mu.Unlock() key := scenarioKey(scenario) delete(sm.states, key) } // ClearAllStates removes all scenario states func (sm *scenarioStateManager) ClearAllStates() { sm.mu.Lock() defer sm.mu.Unlock() sm.states = make(map[string]*ScenarioState) } // Package-level convenience functions // GetScenarioState returns the state for the current scenario func GetScenarioState(scenario string) *ScenarioState { return GetScenarioStateManager().GetState(scenario) } // ClearScenarioState removes the state for the current scenario func ClearScenarioState(scenario string) { GetScenarioStateManager().ClearState(scenario) } // ClearAllScenarioStates removes all scenario states func ClearAllScenarioStates() { GetScenarioStateManager().ClearAllStates() }