forked from GoCodeAlone/modular
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbdd_lifecycle_test.go
More file actions
392 lines (327 loc) · 15.6 KB
/
bdd_lifecycle_test.go
File metadata and controls
392 lines (327 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
package httpserver
import (
"context"
"fmt"
"net/http"
"testing"
"time"
"github.com/cucumber/godog"
)
// Server lifecycle step implementations
func (ctx *HTTPServerBDDTestContext) theHTTPServerIsStarted() error {
if ctx.service == nil {
return fmt.Errorf("HTTP server service not available")
}
// Set a simple handler for testing
if ctx.customHandler != nil {
ctx.service.handler = ctx.customHandler
} else {
ctx.service.handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/health" {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("test response"))
})
}
// Start the server with a timeout context
startCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := ctx.service.Start(startCtx)
if err != nil {
ctx.lastError = err
return err
}
// Get the actual server address for testing
if ctx.service.server != nil {
addr := ctx.service.server.Addr
if addr != "" {
ctx.serverAddress = addr
}
}
return nil
}
func (ctx *HTTPServerBDDTestContext) theServerShouldListenOnTheConfiguredAddress() error {
if ctx.service == nil || ctx.service.server == nil {
return fmt.Errorf("server not started")
}
// Verify the server is listening
expectedAddr := fmt.Sprintf("%s:%d", ctx.serverConfig.Host, ctx.serverConfig.Port)
if ctx.service.server.Addr != expectedAddr && ctx.serverConfig.Port != 0 {
// For dynamic ports, just check that server has an address
if ctx.service.server.Addr == "" {
return fmt.Errorf("server not listening on any address")
}
}
return nil
}
func (ctx *HTTPServerBDDTestContext) theServerShouldAcceptHTTPRequests() error {
if ctx.service == nil || ctx.service.server == nil {
return fmt.Errorf("server not configured to accept HTTP requests")
}
if ctx.service.server.Handler == nil {
return fmt.Errorf("server has no handler configured")
}
// Verify server address is properly set and listening
if ctx.service.server.Addr == "" {
return fmt.Errorf("server has no address configured")
}
// Verify server timeouts are configured for HTTP request handling
if ctx.service.config != nil {
if ctx.service.config.ReadTimeout <= 0 {
return fmt.Errorf("server read timeout not configured for HTTP request handling")
}
if ctx.service.config.WriteTimeout <= 0 {
return fmt.Errorf("server write timeout not configured for HTTP request handling")
}
}
// Verify the server is not in a shutdown state by checking it can still process requests
if ctx.lastError != nil {
return fmt.Errorf("server has errors that may prevent accepting HTTP requests: %w", ctx.lastError)
}
return nil
}
func (ctx *HTTPServerBDDTestContext) iHaveARunningHTTPServer() error {
err := ctx.iHaveAnHTTPServerConfiguration()
if err != nil {
return err
}
return ctx.theHTTPServerIsStarted()
}
func (ctx *HTTPServerBDDTestContext) iHaveAnHTTPServerRunning() error {
return ctx.iHaveARunningHTTPServer()
}
func (ctx *HTTPServerBDDTestContext) theServerShutdownIsInitiated() error {
if ctx.service == nil {
return fmt.Errorf("server not available")
}
// Initiate shutdown
err := ctx.service.Stop(context.Background())
if err != nil {
ctx.lastError = err
}
return nil
}
func (ctx *HTTPServerBDDTestContext) theServerShouldStopAcceptingNewConnections() error {
// Verify that the server shutdown process is initiated without error
if ctx.lastError != nil {
return fmt.Errorf("server shutdown failed: %w", ctx.lastError)
}
// For BDD test purposes, validate that the server service is still available
// but shutdown process has been initiated (server stops accepting new connections)
if ctx.service == nil {
return fmt.Errorf("httpserver service not available for shutdown verification")
}
return nil
}
func (ctx *HTTPServerBDDTestContext) existingConnectionsShouldBeAllowedToComplete() error {
if ctx.service == nil {
return fmt.Errorf("server service not available to validate connection handling")
}
// Verify shutdown timeout is configured to allow connections to complete
if ctx.service.config == nil {
return fmt.Errorf("server config not available to validate connection handling")
}
// Check that shutdown timeout is set appropriately (not zero or negative)
if ctx.service.config.ShutdownTimeout <= 0 {
// If no shutdown timeout is set, HTTP server uses default graceful shutdown
// which allows existing connections to complete, so this is acceptable
// However, we should verify the server supports graceful shutdown
if ctx.service.server == nil {
return fmt.Errorf("server not available to validate graceful shutdown capability")
}
}
// Verify that Stop() method was called (tracked in lastError if it failed)
// If shutdown was initiated without error, connections should be handled gracefully
return nil
}
func (ctx *HTTPServerBDDTestContext) theShutdownShouldCompleteWithinTheTimeout() error {
// Validate that shutdown completed successfully
if ctx.lastError != nil {
return fmt.Errorf("shutdown did not complete successfully: %w", ctx.lastError)
}
return nil
}
// Timeout configuration step implementations
func (ctx *HTTPServerBDDTestContext) iHaveAnHTTPServerWithCustomTimeoutSettings() error {
ctx.resetContext()
// Create HTTP server configuration with custom timeouts
ctx.serverConfig = &HTTPServerConfig{
Host: "127.0.0.1",
Port: 8081, // Fixed port for timeout testing
ReadTimeout: 5 * time.Second, // Short timeout for testing
WriteTimeout: 5 * time.Second,
IdleTimeout: 10 * time.Second,
TLS: nil,
}
return ctx.setupApplicationWithConfig()
}
func (ctx *HTTPServerBDDTestContext) theServerProcessesRequests() error {
if ctx.service == nil {
return fmt.Errorf("server service not available")
}
// Start the server if it's not running
if ctx.service.server == nil {
err := ctx.theHTTPServerIsStarted()
if err != nil {
return fmt.Errorf("failed to start server for request processing: %w", err)
}
}
// Now verify server is properly configured for request processing
if ctx.service.server == nil {
return fmt.Errorf("server failed to start for request processing")
}
// Verify server is actually listening by checking its configuration
addr := ctx.service.server.Addr
if addr == "" {
return fmt.Errorf("server has no address configured")
}
// Verify server has handler for processing requests
if ctx.service.server.Handler == nil {
return fmt.Errorf("server has no handler configured for request processing")
}
// Verify server configuration supports request processing
if ctx.service.config == nil {
return fmt.Errorf("server configuration not available for request processing")
}
// Check that timeouts are configured properly for request processing
if ctx.service.config.ReadTimeout <= 0 {
return fmt.Errorf("read timeout not configured for request processing")
}
if ctx.service.config.WriteTimeout <= 0 {
return fmt.Errorf("write timeout not configured for request processing")
}
return nil
}
func (ctx *HTTPServerBDDTestContext) theReadTimeoutShouldBeRespected() error {
if ctx.service == nil {
return fmt.Errorf("server not available")
}
if ctx.service.config == nil {
return fmt.Errorf("server config not available")
}
expectedTimeout := ctx.serverConfig.ReadTimeout
actualTimeout := ctx.service.config.ReadTimeout
if actualTimeout != expectedTimeout {
return fmt.Errorf("read timeout not configured correctly: expected %v, got %v",
expectedTimeout, actualTimeout)
}
return nil
}
func (ctx *HTTPServerBDDTestContext) theWriteTimeoutShouldBeRespected() error {
if ctx.service == nil {
return fmt.Errorf("server not available")
}
if ctx.service.config == nil {
return fmt.Errorf("server config not available")
}
expectedTimeout := ctx.serverConfig.WriteTimeout
actualTimeout := ctx.service.config.WriteTimeout
if actualTimeout != expectedTimeout {
return fmt.Errorf("write timeout not configured correctly: expected %v, got %v",
expectedTimeout, actualTimeout)
}
return nil
}
func (ctx *HTTPServerBDDTestContext) theIdleTimeoutShouldBeRespected() error {
if ctx.service == nil {
return fmt.Errorf("server not available")
}
if ctx.service.config == nil {
return fmt.Errorf("server config not available")
}
expectedTimeout := ctx.serverConfig.IdleTimeout
actualTimeout := ctx.service.config.IdleTimeout
if actualTimeout != expectedTimeout {
return fmt.Errorf("idle timeout not configured correctly: expected %v, got %v",
expectedTimeout, actualTimeout)
}
return nil
}
// TestHTTPServerModuleLifecycle runs the lifecycle BDD tests for the HTTP server module
func TestHTTPServerModuleLifecycle(t *testing.T) {
suite := godog.TestSuite{
ScenarioInitializer: func(ctx *godog.ScenarioContext) {
testCtx := &HTTPServerBDDTestContext{}
// Background
ctx.Given(`^I have a modular application with httpserver module configured$`, testCtx.iHaveAModularApplicationWithHTTPServerModuleConfigured)
// Basic HTTP server configuration
ctx.Given(`^I have an HTTP server configuration$`, testCtx.iHaveAnHTTPServerConfiguration)
ctx.When(`^the httpserver module is initialized$`, testCtx.theHTTPServerModuleIsInitialized)
ctx.Then(`^the HTTP server service should be available$`, testCtx.theHTTPServerServiceShouldBeAvailable)
ctx.Then(`^the server should be configured with default settings$`, testCtx.theServerShouldBeConfiguredWithDefaultSettings)
// Server startup/shutdown steps
ctx.When(`^the HTTP server is started$`, testCtx.theHTTPServerIsStarted)
ctx.Then(`^the server should listen on the configured address$`, testCtx.theServerShouldListenOnTheConfiguredAddress)
ctx.Then(`^the server should accept HTTP requests$`, testCtx.theServerShouldAcceptHTTPRequests)
// Steps for timeout configuration
ctx.Given(`^I have an HTTP server with custom timeout settings$`, testCtx.iHaveAnHTTPServerWithCustomTimeoutSettings)
ctx.When(`^the server processes requests$`, testCtx.theServerProcessesRequests)
ctx.Then(`^the read timeout should be respected$`, testCtx.theReadTimeoutShouldBeRespected)
ctx.Then(`^the write timeout should be respected$`, testCtx.theWriteTimeoutShouldBeRespected)
ctx.Then(`^the idle timeout should be respected$`, testCtx.theIdleTimeoutShouldBeRespected)
// Steps for graceful shutdown
ctx.Given(`^I have a running HTTP server$`, testCtx.iHaveARunningHTTPServer)
ctx.Given(`^I have an HTTP server running$`, testCtx.iHaveAnHTTPServerRunning)
ctx.When(`^the server shutdown is initiated$`, testCtx.theServerShutdownIsInitiated)
ctx.Then(`^the server should stop accepting new connections$`, testCtx.theServerShouldStopAcceptingNewConnections)
ctx.Then(`^existing connections should be allowed to complete$`, testCtx.existingConnectionsShouldBeAllowedToComplete)
ctx.Then(`^the shutdown should complete within the timeout$`, testCtx.theShutdownShouldCompleteWithinTheTimeout)
// Additional steps needed for full coverage
ctx.Given(`^I have an HTTPS server configuration with TLS enabled$`, testCtx.iHaveAnHTTPSServerConfigurationWithTLSEnabled)
ctx.Given(`^I have an HTTP server with health checks enabled$`, testCtx.iHaveAnHTTPServerWithHealthChecksEnabled)
ctx.Given(`^I have an HTTP server service available$`, testCtx.iHaveAnHTTPServerServiceAvailable)
ctx.Given(`^I have an HTTP server with middleware configured$`, testCtx.iHaveAnHTTPServerWithMiddlewareConfigured)
ctx.Given(`^I have a TLS configuration without certificate files$`, testCtx.iHaveATLSConfigurationWithoutCertificateFiles)
ctx.Given(`^I have an HTTP server with monitoring enabled$`, testCtx.iHaveAnHTTPServerWithMonitoringEnabled)
ctx.Given(`^I have an httpserver with event observation enabled$`, testCtx.iHaveAnHTTPServerWithEventObservationEnabled)
ctx.Given(`^I have an httpserver with TLS and event observation enabled$`, testCtx.iHaveAnHTTPServerWithTLSAndEventObservationEnabled)
// Additional When steps
ctx.When(`^the HTTPS server is started$`, testCtx.theHTTPSServerIsStarted)
ctx.When(`^I request the health check endpoint$`, testCtx.iRequestTheHealthCheckEndpoint)
ctx.When(`^I register custom handlers with the server$`, testCtx.iRegisterCustomHandlersWithTheServer)
ctx.When(`^requests are processed through the server$`, testCtx.requestsAreProcessedThroughTheServer)
ctx.When(`^the HTTPS server is started with auto-generation$`, testCtx.theHTTPSServerIsStartedWithAutoGeneration)
ctx.When(`^an error occurs during request processing$`, testCtx.anErrorOccursDuringRequestProcessing)
ctx.When(`^the httpserver module starts$`, func() error { return nil })
ctx.When(`^the TLS server module starts$`, func() error { return nil })
ctx.When(`^the httpserver processes a request$`, testCtx.theHTTPServerProcessesARequest)
// Additional Then steps
ctx.Then(`^the server should listen on the configured TLS port$`, testCtx.theServerShouldListenOnTheConfiguredTLSPort)
ctx.Then(`^the server should accept HTTPS requests$`, testCtx.theServerShouldAcceptHTTPSRequests)
ctx.Then(`^the health check should return server status$`, testCtx.theHealthCheckShouldReturnServerStatus)
ctx.Then(`^the response should indicate server health$`, testCtx.theResponseShouldIndicateServerHealth)
ctx.Then(`^the handlers should be available for requests$`, testCtx.theHandlersShouldBeAvailableForRequests)
ctx.Then(`^the server should route requests to the correct handlers$`, testCtx.theServerShouldRouteRequestsToTheCorrectHandlers)
ctx.Then(`^the middleware should be applied to requests$`, testCtx.theMiddlewareShouldBeAppliedToRequests)
ctx.Then(`^the middleware chain should execute in order$`, testCtx.theMiddlewareChainShouldExecuteInOrder)
ctx.Then(`^the server should generate self-signed certificates$`, testCtx.theServerShouldGenerateSelfSignedCertificates)
ctx.Then(`^the server should use the generated certificates$`, testCtx.theServerShouldUseTheGeneratedCertificates)
ctx.Then(`^the server should handle errors gracefully$`, testCtx.theServerShouldHandleErrorsGracefully)
ctx.Then(`^appropriate error responses should be returned$`, testCtx.appropriateErrorResponsesShouldBeReturned)
ctx.Then(`^server metrics should be collected$`, testCtx.serverMetricsShouldBeCollected)
ctx.Then(`^the metrics should include request counts and response times$`, testCtx.theMetricsShouldIncludeRequestCountsAndResponseTimes)
ctx.Then(`^a server started event should be emitted$`, testCtx.aServerStartedEventShouldBeEmitted)
ctx.Then(`^a config loaded event should be emitted$`, testCtx.aConfigLoadedEventShouldBeEmitted)
ctx.Then(`^the events should contain server configuration details$`, testCtx.theEventsShouldContainServerConfigurationDetails)
ctx.Then(`^a TLS enabled event should be emitted$`, testCtx.aTLSEnabledEventShouldBeEmitted)
ctx.Then(`^a TLS configured event should be emitted$`, testCtx.aTLSConfiguredEventShouldBeEmitted)
ctx.Then(`^the events should contain TLS configuration details$`, testCtx.theEventsShouldContainTLSConfigurationDetails)
ctx.Then(`^a request received event should be emitted$`, testCtx.aRequestReceivedEventShouldBeEmitted)
ctx.Then(`^a request handled event should be emitted$`, testCtx.aRequestHandledEventShouldBeEmitted)
ctx.Then(`^the events should contain request details$`, testCtx.theEventsShouldContainRequestDetails)
ctx.Then(`^all registered events should be emitted during testing$`, testCtx.allRegisteredEventsShouldBeEmittedDuringTesting)
},
Options: &godog.Options{
Format: "pretty",
Paths: []string{"features"},
TestingT: t,
Strict: true,
},
}
if suite.Run() != 0 {
t.Fatal("non-zero status returned, failed to run lifecycle feature tests")
}
}