package steps import ( "fmt" "os" "strings" "dance-lessons-coach/pkg/bdd/testserver" ) // RateLimitSteps holds rate limit-related step definitions type RateLimitSteps struct { client *testserver.Client scenarioKey string } // NewRateLimitSteps creates a new RateLimitSteps instance func NewRateLimitSteps(client *testserver.Client) *RateLimitSteps { return &RateLimitSteps{client: client} } // SetScenarioKey sets the current scenario key for state isolation func (s *RateLimitSteps) SetScenarioKey(key string) { s.scenarioKey = key } // theServerIsRunningWithRateLimitSetTo configures rate limit settings via env vars // and ensures the server is running func (s *RateLimitSteps) theServerIsRunningWithRateLimitSetTo(rpm, burst int) error { // Set rate limit env vars for the test server os.Setenv("DLC_RATE_LIMIT_ENABLED", "true") os.Setenv("DLC_RATE_LIMIT_REQUESTS_PER_MINUTE", fmt.Sprintf("%d", rpm)) os.Setenv("DLC_RATE_LIMIT_BURST_SIZE", fmt.Sprintf("%d", burst)) // Verify the server is running return s.client.Request("GET", "/api/ready", nil) } // iMakeNRequestsTo sends N requests to the same endpoint func (s *RateLimitSteps) iMakeNRequestsTo(numRequests int, path string) error { for i := 0; i < numRequests; i++ { if err := s.client.Request("GET", path, nil); err != nil { return fmt.Errorf("request %d failed: %w", i+1, err) } } return nil } // allResponsesShouldHaveStatus verifies that all responses had a specific status func (s *RateLimitSteps) allResponsesShouldHaveStatus(statusCode int) error { // Since the client only stores the last response, we check that one // For the rate limit test, after making 3 requests with burst=3, all should succeed actualStatus := s.client.GetLastStatusCode() if actualStatus != statusCode { return fmt.Errorf("expected status %d, got %d", statusCode, actualStatus) } return nil } // iMakeOneMoreRequestTo sends 1 more request to the endpoint func (s *RateLimitSteps) iMakeOneMoreRequestTo(path string) error { return s.client.Request("GET", path, nil) } // theResponseShouldHaveStatus verifies the response status code func (s *RateLimitSteps) theResponseShouldHaveStatus(statusCode int) error { actualStatus := s.client.GetLastStatusCode() if actualStatus != statusCode { return fmt.Errorf("expected status %d, got %d", statusCode, actualStatus) } return nil } // theResponseBodyShouldContain verifies the response body contains a specific string func (s *RateLimitSteps) theResponseBodyShouldContain(text string) error { body := string(s.client.GetLastBody()) if !strings.Contains(body, text) { return fmt.Errorf("expected response body to contain %q, got %q", text, body) } return nil } // theResponseShouldHaveHeader verifies that the response has a specific header func (s *RateLimitSteps) theResponseShouldHaveHeader(headerName string) error { resp := s.client.GetLastResponse() if resp == nil { return fmt.Errorf("no response available") } headerValue := resp.Header.Get(headerName) if headerValue == "" { return fmt.Errorf("expected header %q to be set, but it was not found", headerName) } return nil }