package steps import ( "dance-lessons-coach/pkg/bdd/testserver" "github.com/cucumber/godog" ) // StepContext holds the test client and implements all step definitions type StepContext struct { client *testserver.Client greetSteps *GreetSteps healthSteps *HealthSteps authSteps *AuthSteps commonSteps *CommonSteps jwtRetentionSteps *JWTRetentionSteps } // NewStepContext creates a new step context func NewStepContext(client *testserver.Client) *StepContext { return &StepContext{ client: client, greetSteps: NewGreetSteps(client), healthSteps: NewHealthSteps(client), authSteps: NewAuthSteps(client), commonSteps: NewCommonSteps(client), jwtRetentionSteps: NewJWTRetentionSteps(client), } } // InitializeAllSteps registers all step definitions for the BDD tests func InitializeAllSteps(ctx *godog.ScenarioContext, client *testserver.Client) { sc := NewStepContext(client) // Greet steps ctx.Step(`^I request a greeting for "([^"]*)"$`, sc.greetSteps.iRequestAGreetingFor) ctx.Step(`^I request the default greeting$`, sc.greetSteps.iRequestTheDefaultGreeting) ctx.Step(`^I send a POST request to v2 greet with name "([^"]*)"$`, sc.greetSteps.iSendPOSTRequestToV2GreetWithName) ctx.Step(`^I send a POST request to v2 greet with invalid JSON "([^"]*)"$`, sc.greetSteps.iSendPOSTRequestToV2GreetWithInvalidJSON) ctx.Step(`^the server is running with v2 enabled$`, sc.greetSteps.theServerIsRunningWithV2Enabled) // Health steps ctx.Step(`^I request the health endpoint$`, sc.healthSteps.iRequestTheHealthEndpoint) ctx.Step(`^the server is running$`, sc.healthSteps.theServerIsRunning) // Auth steps ctx.Step(`^a user "([^"]*)" exists with password "([^"]*)"$`, sc.authSteps.aUserExistsWithPassword) ctx.Step(`^I authenticate with username "([^"]*)" and password "([^"]*)"$`, sc.authSteps.iAuthenticateWithUsernameAndPassword) ctx.Step(`^the authentication should be successful$`, sc.authSteps.theAuthenticationShouldBeSuccessful) ctx.Step(`^I should receive a valid JWT token$`, sc.authSteps.iShouldReceiveAValidJWTToken) ctx.Step(`^the authentication should fail$`, sc.authSteps.theAuthenticationShouldFail) ctx.Step(`^I authenticate as admin with master password "([^"]*)"$`, sc.authSteps.iAuthenticateAsAdminWithMasterPassword) ctx.Step(`^the token should contain admin claims$`, sc.authSteps.theTokenShouldContainAdminClaims) ctx.Step(`^I register a new user "([^"]*)" with password "([^"]*)"$`, sc.authSteps.iRegisterANewUserWithPassword) ctx.Step(`^the registration should be successful$`, sc.authSteps.theRegistrationShouldBeSuccessful) ctx.Step(`^I should be able to authenticate with the new credentials$`, sc.authSteps.iShouldBeAbleToAuthenticateWithTheNewCredentials) ctx.Step(`^I am authenticated as admin$`, sc.authSteps.iAmAuthenticatedAsAdmin) ctx.Step(`^I request password reset for user "([^"]*)"$`, sc.authSteps.iRequestPasswordResetForUser) ctx.Step(`^the password reset should be allowed$`, sc.authSteps.thePasswordResetShouldBeAllowed) ctx.Step(`^the user should be flagged for password reset$`, sc.authSteps.theUserShouldBeFlaggedForPasswordReset) ctx.Step(`^I complete password reset for "([^"]*)" with new password "([^"]*)"$`, sc.authSteps.iCompletePasswordResetForWithNewPassword) ctx.Step(`^I should be able to authenticate with the new password$`, sc.authSteps.iShouldBeAbleToAuthenticateWithTheNewPassword) ctx.Step(`^a user "([^"]*)" exists and is flagged for password reset$`, sc.authSteps.aUserExistsAndIsFlaggedForPasswordReset) ctx.Step(`^the password reset should be successful$`, sc.authSteps.thePasswordResetShouldBeSuccessful) ctx.Step(`^the password reset should fail$`, sc.authSteps.thePasswordResetShouldFail) ctx.Step(`^the registration should fail$`, sc.authSteps.theRegistrationShouldFail) ctx.Step(`^the authentication should fail with validation error$`, sc.authSteps.theAuthenticationShouldFailWithValidationError) // JWT edge case steps ctx.Step(`^I use an expired JWT token for authentication$`, sc.authSteps.iUseAnExpiredJWTTokenForAuthentication) ctx.Step(`^I use a JWT token signed with wrong secret for authentication$`, sc.authSteps.iUseAJWTTokenSignedWithWrongSecretForAuthentication) ctx.Step(`^I use a malformed JWT token for authentication$`, sc.authSteps.iUseAMalformedJWTTokenForAuthentication) // JWT validation steps ctx.Step(`^I validate the received JWT token$`, sc.authSteps.iValidateTheReceivedJWTToken) ctx.Step(`^the token should be valid$`, sc.authSteps.theTokenShouldBeValid) ctx.Step(`^it should contain the correct user ID$`, sc.authSteps.itShouldContainTheCorrectUserID) ctx.Step(`^I should receive a different JWT token$`, sc.authSteps.iShouldReceiveADifferentJWTToken) ctx.Step(`^I authenticate with username "([^"]*)" and password "([^"]*)" again$`, sc.authSteps.iAuthenticateWithUsernameAndPasswordAgain) // JWT Secret Rotation steps ctx.Step(`^the server is running with multiple JWT secrets$`, sc.authSteps.theServerIsRunningWithMultipleJWTSecrets) ctx.Step(`^I should receive a valid JWT token signed with the primary secret$`, sc.authSteps.iShouldReceiveAValidJWTTokenSignedWithThePrimarySecret) ctx.Step(`^I validate a JWT token signed with the secondary secret$`, sc.authSteps.iValidateAJWTTokenSignedWithTheSecondarySecret) ctx.Step(`^I add a new secondary JWT secret to the server$`, sc.authSteps.iAddANewSecondaryJWTSecretToTheServer) ctx.Step(`^I add a new secondary JWT secret and rotate to it$`, sc.authSteps.iAddANewSecondaryJWTSecretAndRotateToIt) ctx.Step(`^I authenticate with username "([^"]*)" and password "([^"]*)" after rotation$`, sc.authSteps.iAuthenticateWithUsernameAndPasswordAfterRotation) ctx.Step(`^I should receive a valid JWT token signed with the new secondary secret$`, sc.authSteps.iShouldReceiveAValidJWTTokenSignedWithTheNewSecondarySecret) ctx.Step(`^the token should still be valid during retention period$`, sc.authSteps.theTokenShouldStillBeValidDuringRetentionPeriod) ctx.Step(`^I use a JWT token signed with the expired secondary secret for authentication$`, sc.authSteps.iUseAJWTTokenSignedWithTheExpiredSecondarySecretForAuthentication) ctx.Step(`^I use the old JWT token signed with primary secret$`, sc.authSteps.iUseTheOldJWTTokenSignedWithPrimarySecret) ctx.Step(`^I validate the old JWT token signed with primary secret$`, sc.authSteps.iValidateTheOldJWTTokenSignedWithPrimarySecret) ctx.Step(`^the server is running with primary JWT secret$`, sc.authSteps.theServerIsRunningWithPrimaryJWTSecret) ctx.Step(`^the server is running with primary and expired secondary JWT secrets$`, sc.authSteps.theServerIsRunningWithPrimaryAndExpiredSecondaryJWTSecrets) ctx.Step(`^the token should still be valid$`, sc.authSteps.theTokenShouldStillBeValid) // JWT Retention steps ctx.Step(`^the server is running with JWT secret retention configured$`, sc.jwtRetentionSteps.theServerIsRunningWithJWTSecretRetentionConfigured) ctx.Step(`^the default JWT TTL is (\d+) hours$`, sc.jwtRetentionSteps.theDefaultJWTTTLIsHours) ctx.Step(`^the retention factor is (\d+\.?\d*)$`, sc.jwtRetentionSteps.theRetentionFactorIs) ctx.Step(`^the maximum retention is (\d+) hours$`, sc.jwtRetentionSteps.theMaximumRetentionIsHours) ctx.Step(`^a primary JWT secret exists$`, sc.jwtRetentionSteps.aPrimaryJWTSecretExists) ctx.Step(`^I add a secondary JWT secret with (\d+) hour expiration$`, sc.jwtRetentionSteps.iAddASecondaryJWTSecretWithHourExpiration) ctx.Step(`^I wait for the retention period to elapse$`, sc.jwtRetentionSteps.iWaitForTheRetentionPeriodToElapse) ctx.Step(`^the expired secondary secret should be automatically removed$`, sc.jwtRetentionSteps.theExpiredSecondarySecretShouldBeAutomaticallyRemoved) ctx.Step(`^the primary secret should remain active$`, sc.jwtRetentionSteps.thePrimarySecretShouldRemainActive) ctx.Step(`^I should see cleanup event in logs$`, sc.jwtRetentionSteps.iShouldSeeCleanupEventInLogs) ctx.Step(`^the JWT TTL is set to (\d+) hours$`, sc.jwtRetentionSteps.theJWTTTLIsSetToHours) ctx.Step(`^the retention period should be capped at (\d+) hours$`, sc.jwtRetentionSteps.theRetentionPeriodShouldBeCappedAtHours) ctx.Step(`^the retention period should be (\d+) hours$`, sc.jwtRetentionSteps.theRetentionPeriodShouldBeHours) ctx.Step(`^the cleanup interval is set to (\d+) minutes$`, sc.jwtRetentionSteps.theCleanupIntervalIsSetToMinutes) ctx.Step(`^it should be removed within (\d+) minutes$`, sc.jwtRetentionSteps.itShouldBeRemovedWithinMinutes) ctx.Step(`^I should see cleanup events every (\d+) minutes$`, sc.jwtRetentionSteps.iShouldSeeCleanupEventsEveryMinutes) ctx.Step(`^a user "([^"]*)" exists with password "([^"]*)"$`, sc.jwtRetentionSteps.aUserExistsWithPassword) ctx.Step(`^I authenticate with username "([^"]*)" and password "([^"]*)"$`, sc.jwtRetentionSteps.iAuthenticateWithUsernameAndPassword) ctx.Step(`^I receive a valid JWT token signed with current secret$`, sc.jwtRetentionSteps.iReceiveAValidJWTTokenSignedWithCurrentSecret) ctx.Step(`^I wait for the secret to expire$`, sc.jwtRetentionSteps.iWaitForTheSecretToExpire) ctx.Step(`^I try to validate the expired token$`, sc.jwtRetentionSteps.iTryToValidateTheExpiredToken) ctx.Step(`^the token validation should fail$`, sc.jwtRetentionSteps.theTokenValidationShouldFail) ctx.Step(`^I should receive "([^"]*)" error$`, sc.jwtRetentionSteps.iShouldReceiveInvalidTokenError) ctx.Step(`^I set retention factor to (\d+\.?\d*)$`, sc.jwtRetentionSteps.iSetRetentionFactorTo) ctx.Step(`^I try to start the server$`, sc.jwtRetentionSteps.iTryToStartTheServer) ctx.Step(`^I should receive configuration validation error$`, sc.jwtRetentionSteps.iShouldReceiveConfigurationValidationError) ctx.Step(`^the error should mention "([^"]*)"$`, sc.jwtRetentionSteps.theErrorShouldMention) ctx.Step(`^I have enabled Prometheus metrics$`, sc.jwtRetentionSteps.iHaveEnabledPrometheusMetrics) ctx.Step(`^I should see "([^"]*)" metric increment$`, sc.jwtRetentionSteps.iShouldSeeMetricIncrement) ctx.Step(`^I should see "([^"]*)" metric decrease$`, sc.jwtRetentionSteps.iShouldSeeMetricDecrease) ctx.Step(`^I should see "([^"]*)" histogram update$`, sc.jwtRetentionSteps.iShouldSeeHistogramUpdate) ctx.Step(`^I add a new JWT secret "([^"]*)"$`, sc.jwtRetentionSteps.iAddANewJWTSecret) ctx.Step(`^the logs should show masked secret "([^"]*)"$`, sc.jwtRetentionSteps.theLogsShouldShowMaskedSecret) ctx.Step(`^the logs should not expose the full secret in logs$`, sc.jwtRetentionSteps.theLogsShouldNotExposeTheFullSecret) ctx.Step(`^I have (\d+) JWT secrets$`, sc.jwtRetentionSteps.iHaveJWTSecrets) ctx.Step(`^(\d+) of them are expired$`, sc.jwtRetentionSteps.ofThemAreExpired) ctx.Step(`^it should complete within (\d+) milliseconds$`, sc.jwtRetentionSteps.itShouldCompleteWithinMilliseconds) ctx.Step(`^and not impact server performance$`, sc.jwtRetentionSteps.andNotImpactServerPerformance) ctx.Step(`^I set cleanup interval to (\d+) hours$`, sc.jwtRetentionSteps.iSetCleanupIntervalToHours) ctx.Step(`^they should not be automatically removed$`, sc.jwtRetentionSteps.theyShouldNotBeAutomaticallyRemoved) ctx.Step(`^and manual cleanup should still be possible$`, sc.jwtRetentionSteps.andManualCleanupShouldStillBePossible) ctx.Step(`^the retention period should be (\d+) hour$`, sc.jwtRetentionSteps.theRetentionPeriodShouldBeHour) ctx.Step(`^the secret should expire after (\d+) hour$`, sc.jwtRetentionSteps.theSecretShouldExpireAfterHour) ctx.Step(`^I try to add an invalid JWT secret$`, sc.jwtRetentionSteps.iTryToAddAnInvalidJWTSecret) ctx.Step(`^I should receive validation error$`, sc.jwtRetentionSteps.iShouldReceiveValidationError) ctx.Step(`^the error should mention minimum (\d+) characters$`, sc.jwtRetentionSteps.theErrorShouldMentionMinimumCharacters) ctx.Step(`^the cleanup job encounters an error$`, sc.jwtRetentionSteps.theCleanupJobEncountersAnError) ctx.Step(`^it should log the error$`, sc.jwtRetentionSteps.itShouldLogTheError) ctx.Step(`^and continue with remaining secrets$`, sc.jwtRetentionSteps.andContinueWithRemainingSecrets) ctx.Step(`^and not crash the cleanup process$`, sc.jwtRetentionSteps.andNotCrashTheCleanupProcess) ctx.Step(`^the server is running with default retention settings$`, sc.jwtRetentionSteps.theServerIsRunningWithDefaultRetentionSettings) ctx.Step(`^I update the retention factor via configuration$`, sc.jwtRetentionSteps.iUpdateTheRetentionFactorViaConfiguration) ctx.Step(`^the new settings should take effect immediately$`, sc.jwtRetentionSteps.theNewSettingsShouldTakeEffectImmediately) ctx.Step(`^and existing secrets should be reevaluated$`, sc.jwtRetentionSteps.andExistingSecretsShouldBeReevaluated) ctx.Step(`^and cleanup should use new retention periods$`, sc.jwtRetentionSteps.andCleanupShouldUseNewRetentionPeriods) ctx.Step(`^I enable audit logging$`, sc.jwtRetentionSteps.iEnableAuditLogging) ctx.Step(`^I should see audit log entry with event type "([^"]*)"$`, sc.jwtRetentionSteps.iShouldSeeAuditLogEntryWithEventType) ctx.Step(`^I authenticate and receive token A$`, sc.jwtRetentionSteps.iAuthenticateAndReceiveTokenA) ctx.Step(`^I refresh my token during retention period$`, sc.jwtRetentionSteps.iRefreshMyTokenDuringRetentionPeriod) ctx.Step(`^I should receive new token B$`, sc.jwtRetentionSteps.iShouldReceiveNewTokenB) ctx.Step(`^and token A should still be valid until retention expires$`, sc.jwtRetentionSteps.andTokenAShouldStillBeValidUntilRetentionExpires) ctx.Step(`^and both tokens should work concurrently$`, sc.jwtRetentionSteps.andBothTokensShouldWorkConcurrently) ctx.Step(`^given a security incident requires immediate rotation$`, sc.jwtRetentionSteps.givenASecurityIncidentRequiresImmediateRotation) ctx.Step(`^I rotate to a new primary secret$`, sc.jwtRetentionSteps.iRotateToANewPrimarySecret) ctx.Step(`^old tokens should be invalidated immediately$`, sc.jwtRetentionSteps.oldTokensShouldBeInvalidatedImmediately) ctx.Step(`^and new tokens should use the emergency secret$`, sc.jwtRetentionSteps.andNewTokensShouldUseTheEmergencySecret) ctx.Step(`^and cleanup should remove compromised secrets$`, sc.jwtRetentionSteps.andCleanupShouldRemoveCompromisedSecrets) ctx.Step(`^I have monitoring configured$`, sc.jwtRetentionSteps.iHaveMonitoringConfigured) ctx.Step(`^the cleanup job fails repeatedly$`, sc.jwtRetentionSteps.theCleanupJobFailsRepeatedly) ctx.Step(`^I should receive alert notification$`, sc.jwtRetentionSteps.iShouldReceiveAlertNotification) ctx.Step(`^the alert should include error details$`, sc.jwtRetentionSteps.theAlertShouldIncludeErrorDetails) ctx.Step(`^and suggest remediation steps$`, sc.jwtRetentionSteps.andSuggestRemediationSteps) // Additional missing steps for JWT retention ctx.Step(`^a security incident requires immediate rotation$`, sc.jwtRetentionSteps.givenASecurityIncidentRequiresImmediateRotation) ctx.Step(`^both tokens should work concurrently$`, sc.jwtRetentionSteps.bothTokensShouldWorkConcurrently) ctx.Step(`^both tokens should work until retention period expires$`, sc.jwtRetentionSteps.bothTokensShouldWorkUntilRetentionPeriodExpires) ctx.Step(`^cleanup should remove compromised secrets$`, sc.jwtRetentionSteps.andCleanupShouldRemoveCompromisedSecrets) ctx.Step(`^cleanup should use new retention periods$`, sc.jwtRetentionSteps.andCleanupShouldUseNewRetentionPeriods) ctx.Step(`^continue with remaining secrets$`, sc.jwtRetentionSteps.andContinueWithRemainingSecrets) ctx.Step(`^existing secrets should be reevaluated$`, sc.jwtRetentionSteps.andExistingSecretsShouldBeReevaluated) ctx.Step(`^I add a new JWT secret$`, sc.jwtRetentionSteps.iAddANewJWTSecretNoArgs) ctx.Step(`^I add a new secondary secret and rotate to it$`, sc.authSteps.iAddANewSecondaryJWTSecretAndRotateToIt) ctx.Step(`^I add an expired JWT secret$`, sc.jwtRetentionSteps.iAddAnExpiredJWTSecret) ctx.Step(`^I add expired JWT secrets$`, sc.jwtRetentionSteps.iAddExpiredJWTSecrets) ctx.Step(`^I authenticate again with username "([^"]*)" and password "([^"]*)"$`, sc.jwtRetentionSteps.iAuthenticateAgainWithUsernameAndPassword) ctx.Step(`^I have (\d+) JWT secrets of different ages$`, sc.jwtRetentionSteps.iHaveJWTSecretsOfDifferentAges) ctx.Step(`^I receive a valid JWT token signed with primary secret$`, sc.jwtRetentionSteps.iReceiveAValidJWTTokenSignedWithPrimarySecret) ctx.Step(`^I should receive a new token signed with secondary secret$`, sc.jwtRetentionSteps.iShouldReceiveANewTokenSignedWithSecondarySecret) ctx.Step(`^it tries to remove a secret$`, sc.jwtRetentionSteps.itTriesToRemoveASecret) ctx.Step(`^manual cleanup should still be possible$`, sc.jwtRetentionSteps.manualCleanupShouldStillBePossible) ctx.Step(`^new tokens should use the emergency secret$`, sc.jwtRetentionSteps.newTokensShouldUseTheEmergencySecret) ctx.Step(`^not crash the cleanup process$`, sc.jwtRetentionSteps.andNotCrashTheCleanupProcess) ctx.Step(`^not exceed the maximum retention limit$`, sc.jwtRetentionSteps.notExceedTheMaximumRetentionLimit) ctx.Step(`^not expose the full secret in logs$`, sc.jwtRetentionSteps.notExposeTheFullSecretInLogs) ctx.Step(`^not impact server performance$`, sc.jwtRetentionSteps.andNotImpactServerPerformance) ctx.Step(`^remove all (\d+) expired secrets$`, sc.jwtRetentionSteps.removeAllExpiredSecrets) ctx.Step(`^secret A is (\d+) hour old \(within retention\)$`, sc.jwtRetentionSteps.secretAIsHourOldWithinRetention) ctx.Step(`^secret A should be retained$`, sc.jwtRetentionSteps.secretAShouldBeRetained) ctx.Step(`^secret B is (\d+) hours old \(expired\)$`, sc.jwtRetentionSteps.secretBIsHoursOldExpired) ctx.Step(`^secret B should be removed$`, sc.jwtRetentionSteps.secretBShouldBeRemoved) ctx.Step(`^secret C is the primary secret$`, sc.jwtRetentionSteps.secretCIsThePrimarySecret) ctx.Step(`^secret C should be retained as primary$`, sc.jwtRetentionSteps.secretCShouldBeRetainedAsPrimary) ctx.Step(`^suggest remediation steps$`, sc.jwtRetentionSteps.andSuggestRemediationSteps) ctx.Step(`^the cleanup job removes expired secrets$`, sc.jwtRetentionSteps.theCleanupJobRemovesExpiredSecrets) ctx.Step(`^the cleanup job runs$`, sc.jwtRetentionSteps.theCleanupJobRuns) ctx.Step(`^the JWT TTL is (\d+) hour$`, sc.jwtRetentionSteps.theJWTTTLIsHour) ctx.Step(`^the old token should still be valid during retention period$`, sc.jwtRetentionSteps.theOldTokenShouldStillBeValidDuringRetentionPeriod) ctx.Step(`^the primary secret is older than retention period$`, sc.jwtRetentionSteps.thePrimarySecretIsOlderThanRetentionPeriod) ctx.Step(`^the primary secret should not be removed$`, sc.jwtRetentionSteps.thePrimarySecretShouldNotBeRemoved) ctx.Step(`^the secret is less than (\d+) characters$`, sc.jwtRetentionSteps.theSecretIsLessThanCharacters) ctx.Step(`^the secret should expire after (\d+) hours$`, sc.jwtRetentionSteps.theSecretShouldExpireAfterHours) ctx.Step(`^token A should still be valid until retention expires$`, sc.jwtRetentionSteps.tokenAShouldStillBeValidUntilRetentionExpires) ctx.Step(`^when the secret is removed by cleanup$`, sc.jwtRetentionSteps.whenTheSecretIsRemovedByCleanup) // Common steps ctx.Step(`^the response should be "{\\"([^"]*)":\\"([^"]*)"}"$`, sc.commonSteps.theResponseShouldBe) ctx.Step(`^the response should contain error "([^"]*)"$`, sc.commonSteps.theResponseShouldContainError) ctx.Step(`^the status code should be (\d+)$`, sc.commonSteps.theStatusCodeShouldBe) }