Implement comprehensive BDD testing framework using Godog: - Added feature files for greet and health endpoints - Created test server that runs on port 9191 - Implemented step definitions using Godog's exact patterns - Fixed undefined step warnings by following Godog conventions - All tests passing with proper response validation - Maintained black box testing principles Key files: - pkg/bdd/steps/steps.go - Step definitions using StepContext struct - pkg/bdd/testserver/ - Test server implementation - features/*.feature - BDD feature files - pkg/bdd/README.md - Documentation for proper step patterns The implementation follows Godog's exact pattern suggestions to avoid undefined step warnings and provides comprehensive API testing.
105 lines
2.0 KiB
Go
105 lines
2.0 KiB
Go
package testserver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"DanceLessonsCoach/pkg/config"
|
|
"DanceLessonsCoach/pkg/server"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Server struct {
|
|
httpServer *http.Server
|
|
port int
|
|
baseURL string
|
|
}
|
|
|
|
func NewServer() *Server {
|
|
return &Server{
|
|
port: 9191,
|
|
}
|
|
}
|
|
|
|
func (s *Server) Start() error {
|
|
s.baseURL = fmt.Sprintf("http://localhost:%d", s.port)
|
|
|
|
// Create real server instance from pkg/server
|
|
cfg := createTestConfig(s.port)
|
|
realServer := server.NewServer(cfg, context.Background())
|
|
|
|
// Start HTTP server in same process
|
|
s.httpServer = &http.Server{
|
|
Addr: fmt.Sprintf(":%d", s.port),
|
|
Handler: realServer.Router(),
|
|
}
|
|
|
|
go func() {
|
|
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
if err != http.ErrServerClosed {
|
|
log.Error().Err(err).Msg("Test server failed")
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Wait for server to be ready
|
|
return s.waitForServerReady()
|
|
}
|
|
|
|
func (s *Server) waitForServerReady() error {
|
|
maxAttempts := 30
|
|
attempt := 0
|
|
|
|
for attempt < maxAttempts {
|
|
resp, err := http.Get(fmt.Sprintf("%s/api/ready", s.baseURL))
|
|
if err == nil && resp.StatusCode == http.StatusOK {
|
|
resp.Body.Close()
|
|
return nil
|
|
}
|
|
if resp != nil {
|
|
resp.Body.Close()
|
|
}
|
|
attempt++
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
|
|
return fmt.Errorf("server did not become ready after %d attempts", maxAttempts)
|
|
}
|
|
|
|
func (s *Server) Stop() error {
|
|
if s.httpServer == nil {
|
|
return nil
|
|
}
|
|
|
|
// Shutdown HTTP server gracefully
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
return s.httpServer.Shutdown(ctx)
|
|
}
|
|
|
|
func (s *Server) GetBaseURL() string {
|
|
return s.baseURL
|
|
}
|
|
|
|
func createTestConfig(port int) *config.Config {
|
|
return &config.Config{
|
|
Server: config.ServerConfig{
|
|
Host: "localhost",
|
|
Port: port,
|
|
},
|
|
Shutdown: config.ShutdownConfig{
|
|
Timeout: 5 * time.Second,
|
|
},
|
|
Logging: config.LoggingConfig{
|
|
JSON: false,
|
|
Level: "trace",
|
|
},
|
|
Telemetry: config.TelemetryConfig{
|
|
Enabled: false,
|
|
},
|
|
}
|
|
}
|