Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gateway/it/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@

# Run integration tests (always with coverage)
# Requires: make build-coverage-image (run once before tests)
# Uses script to force unbuffered output on macOS
test:
go test -v ./...
script -q /dev/null go test -v -count=1 ./...
Comment on lines +23 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "Makefile" -path "*/gateway/it/*" -type f | head -5

Repository: wso2/api-platform

Length of output: 82


🏁 Script executed:

if [ -f "gateway/it/Makefile" ]; then
  cat -n "gateway/it/Makefile" | head -50
else
  find . -name "Makefile" | grep -E "(gateway|it)" | head -10
fi

Repository: wso2/api-platform

Length of output: 2154


🏁 Script executed:

ls -la gateway/it/ 2>/dev/null || find . -type d -name "it" | head -5

Repository: wso2/api-platform

Length of output: 1571


🏁 Script executed:

find . -path "*/gateway/it/Makefile" -type f 2>/dev/null && cat -n ./gateway/it/Makefile | sed -n '20,40p'

Repository: wso2/api-platform

Length of output: 790


Portability issue: script -q /dev/null syntax is macOS/BSD-specific and will fail on Linux.

The test target uses BSD-style script -q /dev/null syntax, which is incompatible with GNU script on Linux systems. This breaks cross-platform development and CI/CD pipelines running on Linux.

Additionally, the test-verbose target (line 36) runs tests without the script wrapper, creating inconsistent behavior between the two targets.

The -count=1 flag is correct for disabling test caching in integration tests. However, go test -v already provides line-buffered streaming output; the script wrapper may be unnecessary. Consider either:

  • Removing the wrapper and relying on go test -v output buffering
  • Using OS detection to apply the wrapper only on macOS (if unbuffered output is critical)
🤖 Prompt for AI Agents
In gateway/it/Makefile lines 23-25 the test target uses macOS/BSD-specific
`script -q /dev/null` which fails on Linux; remove the script wrapper and run
`go test -v -count=1 ./...` directly (or add OS detection to apply the wrapper
only on macOS if truly required), and make the test-verbose target consistent
with the change so both targets behave the same across platforms.


# Build all coverage-instrumented gateway components
build-coverage:
Expand Down
71 changes: 71 additions & 0 deletions gateway/it/config_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package it

import (
"context"
"fmt"
"log"
"strings"
"sync"

"github.com/cucumber/godog"
)

const ConfigTagPrefix = "@config-"

// GatewayConfigManager handles configuration switching for the gateway
type GatewayConfigManager struct {
registry *ConfigProfileRegistry
composeManager *ComposeManager
currentProfile string
mu sync.Mutex
}

// NewGatewayConfigManager creates a new config manager
func NewGatewayConfigManager(cm *ComposeManager) *GatewayConfigManager {
return &GatewayConfigManager{
registry: NewConfigProfileRegistry(),
composeManager: cm,
currentProfile: "default", // Assume default on startup
}
}

// EnsureConfig checks the scenario tags and restarts the gateway if a different config is required
func (m *GatewayConfigManager) EnsureConfig(ctx context.Context, sc *godog.Scenario) error {
m.mu.Lock()
defer m.mu.Unlock()

requiredProfile := m.extractConfigTag(sc)
if requiredProfile == "" {
requiredProfile = "default"
}

if m.currentProfile == requiredProfile {
return nil // No restart needed
}

log.Printf("Switching gateway config from '%s' to '%s'...", m.currentProfile, requiredProfile)

profile, ok := m.registry.Get(requiredProfile)
if !ok {
return fmt.Errorf("unknown config profile: %s", requiredProfile)
}

// Restart gateway-controller with new env vars
if err := m.composeManager.RestartGatewayController(ctx, profile.EnvVars); err != nil {
return fmt.Errorf("failed to restart gateway with profile %s: %w", requiredProfile, err)
}

m.currentProfile = requiredProfile
log.Printf("Switched to '%s' profile successfully", requiredProfile)
return nil
}

// extractConfigTag finds the first tag starting with @config- and returns the suffix
func (m *GatewayConfigManager) extractConfigTag(sc *godog.Scenario) string {
for _, tag := range sc.Tags {
if strings.HasPrefix(tag.Name, ConfigTagPrefix) {
return strings.TrimPrefix(tag.Name, ConfigTagPrefix)
}
}
return ""
}
74 changes: 74 additions & 0 deletions gateway/it/config_profiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package it

// ConfigProfile defines a named configuration set for the gateway
type ConfigProfile struct {
Name string
EnvVars map[string]string
Description string
}

// ConfigProfileRegistry manages the available configuration profiles
type ConfigProfileRegistry struct {
profiles map[string]*ConfigProfile
defaultProfile string
}

// NewConfigProfileRegistry creates a new registry with standard profiles
func NewConfigProfileRegistry() *ConfigProfileRegistry {
registry := &ConfigProfileRegistry{
profiles: make(map[string]*ConfigProfile),
defaultProfile: "default",
}

// Register standard profiles
registry.Register(&ConfigProfile{
Name: "default",
EnvVars: map[string]string{
"GATEWAY_LOGGING_LEVEL": "info",
"GATEWAY_STORAGE_TYPE": "sqlite",
},
Description: "Standard configuration using SQLite and Info logging",
})

registry.Register(&ConfigProfile{
Name: "debug",
EnvVars: map[string]string{
"GATEWAY_LOGGING_LEVEL": "debug",
"GATEWAY_STORAGE_TYPE": "sqlite",
},
Description: "Debug configuration enabling verbose logging",
})

registry.Register(&ConfigProfile{
Name: "memory",
EnvVars: map[string]string{
"GATEWAY_LOGGING_LEVEL": "info",
"GATEWAY_STORAGE_TYPE": "memory",
},
Description: "In-memory storage configuration (non-persistent)",
})

registry.Register(&ConfigProfile{
Name: "tracing",
EnvVars: map[string]string{
"GATEWAY_LOGGING_LEVEL": "info",
"GATEWAY_STORAGE_TYPE": "memory",
"GATEWAY_TRACING_ENABLED": "true",
},
Description: "Configuration with OpenTelemetry tracing enabled",
})

return registry

}

// Register adds a profile to the registry
func (r *ConfigProfileRegistry) Register(profile *ConfigProfile) {
r.profiles[profile.Name] = profile
}

// Get retrieves a profile by name
func (r *ConfigProfileRegistry) Get(name string) (*ConfigProfile, bool) {
profile, ok := r.profiles[name]
return profile, ok
}
Loading
Loading