✨ merge: implement JWT secret rotation with BDD scenario isolation - Implement JWT secret rotation mechanism (closes #8) - Add per-scenario state isolation for BDD tests (closes #14) - Validate password reset workflow via BDD tests (closes #7) - Fix port conflicts in test validation - Add state tracer for debugging test execution - Document BDD isolation strategies in ADR 0025 - Fix PostgreSQL configuration environment variables Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai> Co-authored-by: Gabriel Radureau <arcodange@gmail.com> Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
package parallel
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// PortManager manages port allocation for parallel test execution
|
|
type PortManager struct {
|
|
portsInUse map[int]bool
|
|
basePort int
|
|
maxPort int
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
// NewPortManager creates a new port manager with the specified port range
|
|
func NewPortManager(basePort, maxPort int) *PortManager {
|
|
return &PortManager{
|
|
portsInUse: make(map[int]bool),
|
|
basePort: basePort,
|
|
maxPort: maxPort,
|
|
}
|
|
}
|
|
|
|
// AcquirePort acquires an available port for a feature
|
|
func (pm *PortManager) AcquirePort(featureName string) (int, error) {
|
|
pm.mutex.Lock()
|
|
defer pm.mutex.Unlock()
|
|
|
|
// Check if this feature already has a port assigned
|
|
// In a real implementation, this would be more sophisticated
|
|
|
|
// Try to find an available port
|
|
for port := pm.basePort; port <= pm.maxPort; port++ {
|
|
if !pm.portsInUse[port] {
|
|
pm.portsInUse[port] = true
|
|
return port, nil
|
|
}
|
|
}
|
|
|
|
return 0, errors.New("no available ports in the specified range")
|
|
}
|
|
|
|
// ReleasePort releases a port back to the pool
|
|
func (pm *PortManager) ReleasePort(port int) {
|
|
pm.mutex.Lock()
|
|
defer pm.mutex.Unlock()
|
|
|
|
if pm.portsInUse[port] {
|
|
delete(pm.portsInUse, port)
|
|
}
|
|
}
|
|
|
|
// CheckPortConflict checks if a port is already in use
|
|
func (pm *PortManager) CheckPortConflict(port int) bool {
|
|
pm.mutex.Lock()
|
|
defer pm.mutex.Unlock()
|
|
|
|
return pm.portsInUse[port]
|
|
}
|
|
|
|
// GetAvailablePorts returns a list of available ports
|
|
func (pm *PortManager) GetAvailablePorts() []int {
|
|
pm.mutex.Lock()
|
|
defer pm.mutex.Unlock()
|
|
|
|
var available []int
|
|
for port := pm.basePort; port <= pm.maxPort; port++ {
|
|
if !pm.portsInUse[port] {
|
|
available = append(available, port)
|
|
}
|
|
}
|
|
return available
|
|
}
|
|
|
|
// GetPortForFeature gets the standard port for a feature (without dynamic allocation)
|
|
func GetPortForFeature(featureName string) int {
|
|
// Standard port mapping for features
|
|
switch featureName {
|
|
case "auth":
|
|
return 9192
|
|
case "config":
|
|
return 9193
|
|
case "greet":
|
|
return 9194
|
|
case "health":
|
|
return 9195
|
|
case "jwt":
|
|
return 9196
|
|
default:
|
|
return 9191 // Default port
|
|
}
|
|
}
|
|
|
|
// ValidatePortRange validates that a port is within acceptable range
|
|
func ValidatePortRange(port int) error {
|
|
if port < 1024 || port > 65535 {
|
|
return fmt.Errorf("port %d is outside valid range (1024-65535)", port)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckPortAvailable checks if a specific port is available on the system
|
|
func CheckPortAvailable(port int) (bool, error) {
|
|
// In a real implementation, this would actually check if the port is available
|
|
// For now, we'll just validate the range
|
|
if err := ValidatePortRange(port); err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|