package steps import ( "dance-lessons-coach/pkg/bdd/testserver" "fmt" "strings" "github.com/cucumber/godog" ) // StepContext holds the test client and implements all step definitions type StepContext struct { client *testserver.Client } // NewStepContext creates a new step context func NewStepContext(client *testserver.Client) *StepContext { return &StepContext{client: client} } // InitializeAllSteps registers all step definitions for the BDD tests func InitializeAllSteps(ctx *godog.ScenarioContext, client *testserver.Client) { sc := NewStepContext(client) ctx.Step(`^I request a greeting for "([^"]*)"$`, sc.iRequestAGreetingFor) ctx.Step(`^I request the default greeting$`, sc.iRequestTheDefaultGreeting) ctx.Step(`^I request the health endpoint$`, sc.iRequestTheHealthEndpoint) ctx.Step(`^the response should be "{\\"([^"]*)":\\"([^"]*)"}"$`, sc.theResponseShouldBe) ctx.Step(`^the server is running$`, sc.theServerIsRunning) ctx.Step(`^the server is running with v2 enabled$`, sc.theServerIsRunningWithV2Enabled) ctx.Step(`^I send a POST request to v2 greet with name "([^"]*)"$`, sc.iSendPOSTRequestToV2GreetWithName) ctx.Step(`^I send a POST request to v2 greet with invalid JSON "([^"]*)"$`, sc.iSendPOSTRequestToV2GreetWithInvalidJSON) ctx.Step(`^the response should contain error "([^"]*)"$`, sc.theResponseShouldContainError) // User Authentication Steps ctx.Step(`^a user "([^"]*)" exists with password "([^"]*)"$`, sc.aUserExistsWithPassword) ctx.Step(`^I authenticate with username "([^"]*)" and password "([^"]*)"$`, sc.iAuthenticateWithUsernameAndPassword) ctx.Step(`^the authentication should be successful$`, sc.theAuthenticationShouldBeSuccessful) ctx.Step(`^I should receive a valid JWT token$`, sc.iShouldReceiveAValidJWTToken) ctx.Step(`^the authentication should fail$`, sc.theAuthenticationShouldFail) ctx.Step(`^I authenticate as admin with master password "([^"]*)"$`, sc.iAuthenticateAsAdminWithMasterPassword) ctx.Step(`^the token should contain admin claims$`, sc.theTokenShouldContainAdminClaims) ctx.Step(`^I register a new user "([^"]*)" with password "([^"]*)"$`, sc.iRegisterANewUserWithPassword) ctx.Step(`^the registration should be successful$`, sc.theRegistrationShouldBeSuccessful) ctx.Step(`^I should be able to authenticate with the new credentials$`, sc.iShouldBeAbleToAuthenticateWithTheNewCredentials) ctx.Step(`^I am authenticated as admin$`, sc.iAmAuthenticatedAsAdmin) ctx.Step(`^I request password reset for user "([^"]*)"$`, sc.iRequestPasswordResetForUser) ctx.Step(`^the password reset should be allowed$`, sc.thePasswordResetShouldBeAllowed) ctx.Step(`^the user should be flagged for password reset$`, sc.theUserShouldBeFlaggedForPasswordReset) ctx.Step(`^I complete password reset for "([^"]*)" with new password "([^"]*)"$`, sc.iCompletePasswordResetForWithNewPassword) ctx.Step(`^I should be able to authenticate with the new password$`, sc.iShouldBeAbleToAuthenticateWithTheNewPassword) ctx.Step(`^a user "([^"]*)" exists and is flagged for password reset$`, sc.aUserExistsAndIsFlaggedForPasswordReset) ctx.Step(`^the password reset should be successful$`, sc.thePasswordResetShouldBeSuccessful) } func (sc *StepContext) iRequestAGreetingFor(name string) error { return sc.client.Request("GET", fmt.Sprintf("/api/v1/greet/%s", name), nil) } func (sc *StepContext) iRequestTheDefaultGreeting() error { return sc.client.Request("GET", "/api/v1/greet/", nil) } func (sc *StepContext) iRequestTheHealthEndpoint() error { return sc.client.Request("GET", "/api/health", nil) } func (sc *StepContext) theResponseShouldBe(arg1, arg2 string) error { // The regex captures the full JSON from the feature file, including quotes // We need to extract just the key and value without the surrounding quotes and backslashes // Remove the surrounding quotes and backslashes cleanArg1 := strings.Trim(arg1, `"\`) cleanArg2 := strings.Trim(arg2, `"\`) // Build the expected JSON string expected := fmt.Sprintf(`{"%s":"%s"}`, cleanArg1, cleanArg2) return sc.client.ExpectResponseBody(expected) } func (sc *StepContext) theServerIsRunning() error { // Actually verify the server is running by checking the readiness endpoint return sc.client.Request("GET", "/api/ready", nil) } func (sc *StepContext) theServerIsRunningWithV2Enabled() error { // Verify the server is running and v2 is enabled by checking v2 endpoint exists // First check server is running if err := sc.client.Request("GET", "/api/ready", nil); err != nil { return err } // Check if v2 endpoint is available (should return 405 Method Not Allowed for GET, which means endpoint exists) // If v2 is disabled, this will return 404 resp, err := sc.client.CustomRequest("GET", "/api/v2/greet", nil) if err != nil { return err } defer resp.Body.Close() // If we get 405, v2 is enabled (endpoint exists but doesn't allow GET) // If we get 404, v2 is disabled if resp.StatusCode == 404 { return fmt.Errorf("v2 endpoint not available - v2 feature flag not enabled") } return nil } func (sc *StepContext) iSendPOSTRequestToV2GreetWithName(name string) error { // Create JSON request body requestBody := map[string]string{"name": name} return sc.client.Request("POST", "/api/v2/greet", requestBody) } func (sc *StepContext) iSendPOSTRequestToV2GreetWithInvalidJSON(invalidJSON string) error { // Send raw invalid JSON return sc.client.Request("POST", "/api/v2/greet", invalidJSON) } func (sc *StepContext) theResponseShouldContainError(expectedError string) error { // Check if the response contains the expected error body := string(sc.client.GetLastBody()) if !strings.Contains(body, expectedError) { return fmt.Errorf("expected response to contain error %q, got %q", expectedError, body) } return nil } // User Authentication Steps func (sc *StepContext) aUserExistsWithPassword(username, password string) error { // This will need to be implemented when user management is available return fmt.Errorf("user management not yet implemented") } func (sc *StepContext) iAuthenticateWithUsernameAndPassword(username, password string) error { // This will need to be implemented when authentication endpoints are available return fmt.Errorf("authentication not yet implemented") } func (sc *StepContext) theAuthenticationShouldBeSuccessful() error { // This will need to be implemented when authentication is available return fmt.Errorf("authentication not yet implemented") } func (sc *StepContext) iShouldReceiveAValidJWTToken() error { // This will need to be implemented when JWT generation is available return fmt.Errorf("JWT generation not yet implemented") } func (sc *StepContext) theAuthenticationShouldFail() error { // This will need to be implemented when authentication is available return fmt.Errorf("authentication not yet implemented") } func (sc *StepContext) iAuthenticateAsAdminWithMasterPassword(password string) error { // This will need to be implemented when admin authentication is available return fmt.Errorf("admin authentication not yet implemented") } func (sc *StepContext) theTokenShouldContainAdminClaims() error { // This will need to be implemented when JWT claims are available return fmt.Errorf("JWT claims not yet implemented") } func (sc *StepContext) iRegisterANewUserWithPassword(username, password string) error { // This will need to be implemented when user registration is available return fmt.Errorf("user registration not yet implemented") } func (sc *StepContext) theRegistrationShouldBeSuccessful() error { // This will need to be implemented when user registration is available return fmt.Errorf("user registration not yet implemented") } func (sc *StepContext) iShouldBeAbleToAuthenticateWithTheNewCredentials() error { // This will need to be implemented when authentication is available return fmt.Errorf("authentication not yet implemented") } func (sc *StepContext) iAmAuthenticatedAsAdmin() error { // This will need to be implemented when admin authentication is available return fmt.Errorf("admin authentication not yet implemented") } func (sc *StepContext) iRequestPasswordResetForUser(username string) error { // This will need to be implemented when password reset is available return fmt.Errorf("password reset not yet implemented") } func (sc *StepContext) thePasswordResetShouldBeAllowed() error { // This will need to be implemented when password reset is available return fmt.Errorf("password reset not yet implemented") } func (sc *StepContext) theUserShouldBeFlaggedForPasswordReset() error { // This will need to be implemented when password reset is available return fmt.Errorf("password reset not yet implemented") } func (sc *StepContext) iCompletePasswordResetForWithNewPassword(username, password string) error { // This will need to be implemented when password reset is available return fmt.Errorf("password reset not yet implemented") } func (sc *StepContext) aUserExistsAndIsFlaggedForPasswordReset(username string) error { // This will need to be implemented when password reset is available return fmt.Errorf("password reset not yet implemented") } func (sc *StepContext) thePasswordResetShouldBeSuccessful() error { // This will need to be implemented when password reset is available return fmt.Errorf("password reset not yet implemented") } func (sc *StepContext) iShouldBeAbleToAuthenticateWithTheNewPassword() error { // This will need to be implemented when authentication is available return fmt.Errorf("authentication not yet implemented") }