diff --git a/tests/integration/authn/oauth_auth_test.go b/tests/integration/authn/oauth_auth_test.go index b89c3c16a..e96c9c7cd 100644 --- a/tests/integration/authn/oauth_auth_test.go +++ b/tests/integration/authn/oauth_auth_test.go @@ -34,7 +34,6 @@ import ( const ( oauthAuthStart = "/auth/oauth/standard/start" oauthAuthFinish = "/auth/oauth/standard/finish" - mockOAuthPort = 8092 ) var oauthAuthTestOU = testutils.OrganizationUnit{ @@ -82,8 +81,16 @@ func TestOAuthAuthTestSuite(t *testing.T) { } func (suite *OAuthAuthTestSuite) SetupSuite() { - suite.mockOAuthServer = testutils.NewMockOAuthServer(mockOAuthPort, + // Get shared OAuth server (started once for all test suites) + var err error + suite.mockOAuthServer, err = testutils.GetSharedMockServers().GetOAuthServer( "test-oauth-client", "test-oauth-secret") + if err != nil { + suite.T().Fatalf("Failed to get shared OAuth server: %v", err) + } + + // Reset the server to clear any state from previous test suites + suite.mockOAuthServer.Reset() suite.mockOAuthServer.AddUser(&testutils.OAuthUserInfo{ Sub: "user123", @@ -96,9 +103,6 @@ func (suite *OAuthAuthTestSuite) SetupSuite() { }, }) - err := suite.mockOAuthServer.Start() - suite.Require().NoError(err, "Failed to start mock OAuth server") - ouID, err := testutils.CreateOrganizationUnit(oauthAuthTestOU) suite.Require().NoError(err, "Failed to create test organization unit") suite.ouID = ouID @@ -191,9 +195,8 @@ func (suite *OAuthAuthTestSuite) TearDownSuite() { _ = testutils.DeleteIDP(suite.idpID) } - if suite.mockOAuthServer != nil { - _ = suite.mockOAuthServer.Stop() - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. if suite.ouID != "" { if err := testutils.DeleteOrganizationUnit(suite.ouID); err != nil { diff --git a/tests/integration/authn/oidc_auth_test.go b/tests/integration/authn/oidc_auth_test.go index a48ef6a6a..5d2bf1c54 100644 --- a/tests/integration/authn/oidc_auth_test.go +++ b/tests/integration/authn/oidc_auth_test.go @@ -33,7 +33,6 @@ import ( const ( oidcAuthStart = "/auth/oauth/standard/start" oidcAuthFinish = "/auth/oauth/standard/finish" - mockOIDCPort = 8093 ) var oidcAuthTestOU = testutils.OrganizationUnit{ @@ -81,10 +80,16 @@ func TestOIDCAuthTestSuite(t *testing.T) { } func (suite *OIDCAuthTestSuite) SetupSuite() { + // Get shared OIDC server (started once for all test suites) var err error - suite.mockOIDCServer, err = testutils.NewMockOIDCServer(mockOIDCPort, + suite.mockOIDCServer, err = testutils.GetSharedMockServers().GetOIDCServer( "test-oidc-client", "test-oidc-secret") - suite.Require().NoError(err, "Failed to create mock OIDC server") + if err != nil { + suite.T().Fatalf("Failed to get shared OIDC server: %v", err) + } + + // Reset the server to clear any state from previous test suites + suite.mockOIDCServer.Reset() suite.mockOIDCServer.AddUser(&testutils.OIDCUserInfo{ Sub: "user456", @@ -100,9 +105,6 @@ func (suite *OIDCAuthTestSuite) SetupSuite() { }, }) - err = suite.mockOIDCServer.Start() - suite.Require().NoError(err, "Failed to start mock OIDC server") - ouID, err := testutils.CreateOrganizationUnit(oidcAuthTestOU) suite.Require().NoError(err, "Failed to create test organization unit") suite.ouID = ouID @@ -200,9 +202,8 @@ func (suite *OIDCAuthTestSuite) TearDownSuite() { _ = testutils.DeleteIDP(suite.idpID) } - if suite.mockOIDCServer != nil { - _ = suite.mockOIDCServer.Stop() - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. if suite.ouID != "" { if err := testutils.DeleteOrganizationUnit(suite.ouID); err != nil { diff --git a/tests/integration/flowauthn/conditional_exec_auth_test.go b/tests/integration/flowauthn/conditional_exec_auth_test.go index 8f5647e36..64237da7c 100644 --- a/tests/integration/flowauthn/conditional_exec_auth_test.go +++ b/tests/integration/flowauthn/conditional_exec_auth_test.go @@ -21,14 +21,12 @@ package flowauthn import ( "encoding/json" "testing" - "time" "github.com/asgardeo/thunder/tests/integration/testutils" "github.com/stretchr/testify/suite" ) const ( - conditionalExecMockGooglePort = 8093 conditionalExecNewUserSub = "conditional-exec-new-user-sub" conditionalExecNewUserEmail = "newuser@conditional-exec-test.com" conditionalExecExistingUserSub = "conditional-exec-existing-user-sub" @@ -91,11 +89,16 @@ func TestConditionalExecAuthFlowTestSuite(t *testing.T) { } func (ts *ConditionalExecAuthFlowTestSuite) SetupSuite() { - // Start mock Google server - mockGoogleServer, err := testutils.NewMockGoogleOIDCServer(conditionalExecMockGooglePort, + // Get shared Google OIDC server (started once for all test suites) + var err error + ts.mockGoogleServer, err = testutils.GetSharedMockServers().GetGoogleServer( "test_google_client", "test_google_secret") - ts.Require().NoError(err, "Failed to create mock Google server") - ts.mockGoogleServer = mockGoogleServer + if err != nil { + ts.T().Fatalf("Failed to get shared Google server: %v", err) + } + + // Reset the server to clear any state from previous test suites + ts.mockGoogleServer.Reset() // Add test users ts.mockGoogleServer.AddUser(&testutils.GoogleUserInfo{ @@ -119,9 +122,6 @@ func (ts *ConditionalExecAuthFlowTestSuite) SetupSuite() { Locale: "en", }) - err = ts.mockGoogleServer.Start() - ts.Require().NoError(err, "Failed to start mock Google server") - // Create test organization unit ouID, err := testutils.CreateOrganizationUnit(conditionalExecTestOU) ts.Require().NoError(err, "Failed to create test organization unit") @@ -186,11 +186,8 @@ func (ts *ConditionalExecAuthFlowTestSuite) TearDownSuite() { _ = testutils.DeleteOrganizationUnit(conditionalExecPreCreatedOUID) } - // Stop mock server - if ts.mockGoogleServer != nil { - _ = ts.mockGoogleServer.Stop() - time.Sleep(200 * time.Millisecond) - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. } func (ts *ConditionalExecAuthFlowTestSuite) TestSkipConditionalNodes() { diff --git a/tests/integration/flowauthn/decision_auth_test.go b/tests/integration/flowauthn/decision_auth_test.go index f895338c8..985f5cbeb 100644 --- a/tests/integration/flowauthn/decision_auth_test.go +++ b/tests/integration/flowauthn/decision_auth_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - mockDecisionNotificationServerPort = 8098 -) + var ( decisionTestApp = testutils.Application{ @@ -139,14 +137,12 @@ func (ts *DecisionAndMFAFlowTestSuite) SetupSuite() { } decisionUserSchemaID = schemaID - // Start mock notification server - ts.mockServer = testutils.NewMockNotificationServer(mockDecisionNotificationServerPort) - err = ts.mockServer.Start() + // Get shared notification server (started once for all test suites) + ts.mockServer, err = testutils.GetSharedMockServers().GetNotificationServer() if err != nil { - ts.T().Fatalf("Failed to start mock notification server: %v", err) + ts.T().Fatalf("Failed to get shared notification server: %v", err) } - time.Sleep(100 * time.Millisecond) - ts.T().Log("Mock notification server started successfully") + ts.T().Log("Using shared notification server") // Create test users with the created OU userWithMobile := testUserWithMobileDecision @@ -180,13 +176,8 @@ func (ts *DecisionAndMFAFlowTestSuite) TearDownSuite() { ts.T().Logf("Failed to cleanup users during teardown: %v", err) } - // Stop mock server - if ts.mockServer != nil { - err := ts.mockServer.Stop() - if err != nil { - ts.T().Logf("Failed to stop mock notification server during teardown: %v", err) - } - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. // Delete test application if decisionTestAppID != "" { diff --git a/tests/integration/flowauthn/github_auth_test.go b/tests/integration/flowauthn/github_auth_test.go index b83e82a61..7df5bbf0c 100644 --- a/tests/integration/flowauthn/github_auth_test.go +++ b/tests/integration/flowauthn/github_auth_test.go @@ -23,7 +23,6 @@ import ( "net/url" "strings" "testing" - "time" "github.com/asgardeo/thunder/tests/integration/testutils" "github.com/stretchr/testify/suite" @@ -51,9 +50,7 @@ var ( } ) -const ( - mockGithubFlowPort = 8092 -) + var githubUserSchema = testutils.UserSchema{ Name: "github_flow_user", @@ -92,9 +89,16 @@ func TestGithubAuthFlowTestSuite(t *testing.T) { } func (ts *GithubAuthFlowTestSuite) SetupSuite() { - // Start mock GitHub server - ts.mockGithubServer = testutils.NewMockGithubOAuthServer(mockGithubFlowPort, + // Get shared GitHub OAuth server (started once for all test suites) + var err error + ts.mockGithubServer, err = testutils.GetSharedMockServers().GetGithubServer( "test_github_client", "test_github_secret") + if err != nil { + ts.T().Fatalf("Failed to get shared GitHub server: %v", err) + } + + // Reset the server to clear any state from previous test suites + ts.mockGithubServer.Reset() email := "testuser@github.com" ts.mockGithubServer.AddUser(&testutils.GithubUserInfo{ @@ -115,9 +119,6 @@ func (ts *GithubAuthFlowTestSuite) SetupSuite() { }, }) - err := ts.mockGithubServer.Start() - ts.Require().NoError(err, "Failed to start mock GitHub server") - // Use the IDP created by database scripts ts.idpID = "test-github-idp-id" @@ -183,12 +184,8 @@ func (ts *GithubAuthFlowTestSuite) TearDownSuite() { _ = testutils.DeleteUserType(ts.userSchemaID) } - // Stop mock server - if ts.mockGithubServer != nil { - _ = ts.mockGithubServer.Stop() - // Wait for port to be released - time.Sleep(200 * time.Millisecond) - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. } func (ts *GithubAuthFlowTestSuite) TestGithubAuthFlowInitiation() { diff --git a/tests/integration/flowauthn/google_auth_test.go b/tests/integration/flowauthn/google_auth_test.go index 247864d50..110313dcf 100644 --- a/tests/integration/flowauthn/google_auth_test.go +++ b/tests/integration/flowauthn/google_auth_test.go @@ -23,7 +23,6 @@ import ( "net/url" "strings" "testing" - "time" "github.com/asgardeo/thunder/tests/integration/testutils" "github.com/stretchr/testify/suite" @@ -51,9 +50,7 @@ var ( } ) -const ( - mockGoogleFlowPort = 8093 -) + var googleUserSchema = testutils.UserSchema{ Name: "google_flow_user", @@ -92,11 +89,16 @@ func TestGoogleAuthFlowTestSuite(t *testing.T) { } func (ts *GoogleAuthFlowTestSuite) SetupSuite() { - // Start mock Google server - mockServer, err := testutils.NewMockGoogleOIDCServer(mockGoogleFlowPort, + // Get shared Google OIDC server (started once for all test suites) + var err error + ts.mockGoogleServer, err = testutils.GetSharedMockServers().GetGoogleServer( "test_google_client", "test_google_secret") - ts.Require().NoError(err, "Failed to create mock Google server") - ts.mockGoogleServer = mockServer + if err != nil { + ts.T().Fatalf("Failed to get shared Google server: %v", err) + } + + // Reset the server to clear any state from previous test suites + ts.mockGoogleServer.Reset() ts.mockGoogleServer.AddUser(&testutils.GoogleUserInfo{ Sub: "google-test-user-123", @@ -109,9 +111,6 @@ func (ts *GoogleAuthFlowTestSuite) SetupSuite() { Locale: "en", }) - err = ts.mockGoogleServer.Start() - ts.Require().NoError(err, "Failed to start mock Google server") - // Use the IDP created by database scripts ts.idpID = "test-google-idp-id" @@ -177,12 +176,8 @@ func (ts *GoogleAuthFlowTestSuite) TearDownSuite() { _ = testutils.DeleteUserType(ts.userSchemaID) } - // Stop mock server - if ts.mockGoogleServer != nil { - _ = ts.mockGoogleServer.Stop() - // Wait for port to be released - time.Sleep(200 * time.Millisecond) - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. } func (ts *GoogleAuthFlowTestSuite) TestGoogleAuthFlowInitiation() { diff --git a/tests/integration/flowauthn/http_request_executor_auth_test.go b/tests/integration/flowauthn/http_request_executor_auth_test.go index f77b0d47f..5eb19b0e4 100644 --- a/tests/integration/flowauthn/http_request_executor_auth_test.go +++ b/tests/integration/flowauthn/http_request_executor_auth_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - mockHTTPServerPort = 9091 -) + var ( httpRequestTestApp = testutils.Application{ @@ -121,14 +119,12 @@ func (ts *HTTPRequestAuthFlowTestSuite) SetupSuite() { } httpRequestTestAppID = appID - // Start mock HTTP server - ts.mockServer = testutils.NewMockHTTPServer(mockHTTPServerPort) - err = ts.mockServer.Start() + // Get shared HTTP mock server (started once for all test suites) + ts.mockServer, err = testutils.GetSharedMockServers().GetHTTPServer() if err != nil { - ts.T().Fatalf("Failed to start mock HTTP server: %v", err) + ts.T().Fatalf("Failed to get shared HTTP server: %v", err) } - time.Sleep(100 * time.Millisecond) - ts.T().Log("Mock HTTP server started successfully") + ts.T().Log("Using shared HTTP mock server") // Create test user with the created OU testUser := httpRequestTestUser @@ -142,13 +138,8 @@ func (ts *HTTPRequestAuthFlowTestSuite) SetupSuite() { } func (ts *HTTPRequestAuthFlowTestSuite) TearDownSuite() { - // Stop the mock HTTP server - if ts.mockServer != nil { - err := ts.mockServer.Stop() - if err != nil { - ts.T().Logf("Failed to stop mock HTTP server during teardown: %v", err) - } - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. // Delete all created users if err := testutils.CleanupUsers(ts.config.CreatedUserIDs); err != nil { diff --git a/tests/integration/flowauthn/sms_auth_test.go b/tests/integration/flowauthn/sms_auth_test.go index 2d63a05b7..8b072c21a 100644 --- a/tests/integration/flowauthn/sms_auth_test.go +++ b/tests/integration/flowauthn/sms_auth_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - mockNotificationServerPort = 8098 -) + var ( smsAuthTestApp = testutils.Application{ @@ -149,14 +147,12 @@ func (ts *SMSAuthFlowTestSuite) SetupSuite() { } smsAuthUserSchemaID = schemaID - // Start mock notification server - ts.mockServer = testutils.NewMockNotificationServer(mockNotificationServerPort) - err = ts.mockServer.Start() + // Get shared notification server (started once for all test suites) + ts.mockServer, err = testutils.GetSharedMockServers().GetNotificationServer() if err != nil { - ts.T().Fatalf("Failed to start mock notification server: %v", err) + ts.T().Fatalf("Failed to get shared notification server: %v", err) } - time.Sleep(100 * time.Millisecond) - ts.T().Log("Mock notification server started successfully") + ts.T().Log("Using shared notification server") // Create test user with mobile number using the created OU testUserWithMobile := testUserWithMobile @@ -181,13 +177,8 @@ func (ts *SMSAuthFlowTestSuite) TearDownSuite() { ts.T().Logf("Failed to cleanup users during teardown: %v", err) } - // Stop mock server - if ts.mockServer != nil { - err := ts.mockServer.Stop() - if err != nil { - ts.T().Logf("Failed to stop mock notification server during teardown: %v", err) - } - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. // Delete test application if smsAuthTestAppID != "" { diff --git a/tests/integration/flowregistration/github_registration_test.go b/tests/integration/flowregistration/github_registration_test.go index 48b51f140..c699ce8af 100644 --- a/tests/integration/flowregistration/github_registration_test.go +++ b/tests/integration/flowregistration/github_registration_test.go @@ -23,7 +23,6 @@ import ( "net/url" "strings" "testing" - "time" "github.com/asgardeo/thunder/tests/integration/testutils" "github.com/stretchr/testify/suite" @@ -79,9 +78,7 @@ var ( githubRegTestOUID string ) -const ( - mockGithubRegFlowPort = 8092 -) + type GithubRegistrationFlowTestSuite struct { suite.Suite @@ -98,9 +95,16 @@ func TestGithubRegistrationFlowTestSuite(t *testing.T) { func (ts *GithubRegistrationFlowTestSuite) SetupSuite() { ts.config = &TestSuiteConfig{} - // Start mock GitHub server - ts.mockGithubServer = testutils.NewMockGithubOAuthServer(mockGithubRegFlowPort, + // Get shared GitHub OAuth server (started once for all test suites) + var err error + ts.mockGithubServer, err = testutils.GetSharedMockServers().GetGithubServer( "test_github_client", "test_github_secret") + if err != nil { + ts.T().Fatalf("Failed to get shared GitHub server: %v", err) + } + + // Reset the server to clear any state from previous test suites + ts.mockGithubServer.Reset() email := "reguser@github.com" ts.mockGithubServer.AddUser(&testutils.GithubUserInfo{ @@ -121,9 +125,6 @@ func (ts *GithubRegistrationFlowTestSuite) SetupSuite() { }, }) - err := ts.mockGithubServer.Start() - ts.Require().NoError(err, "Failed to start mock GitHub server") - // Use the IDP created by database scripts ts.idpID = "test-github-idp-id" @@ -186,12 +187,8 @@ func (ts *GithubRegistrationFlowTestSuite) TearDownSuite() { _ = testutils.DeleteUserType(ts.userSchemaID) } - // Stop mock server - if ts.mockGithubServer != nil { - _ = ts.mockGithubServer.Stop() - // Wait for port to be released - time.Sleep(200 * time.Millisecond) - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. } func (ts *GithubRegistrationFlowTestSuite) TestGithubRegistrationFlowInitiation() { diff --git a/tests/integration/flowregistration/google_registration_test.go b/tests/integration/flowregistration/google_registration_test.go index 814826692..0866394b3 100644 --- a/tests/integration/flowregistration/google_registration_test.go +++ b/tests/integration/flowregistration/google_registration_test.go @@ -23,7 +23,6 @@ import ( "net/url" "strings" "testing" - "time" "github.com/asgardeo/thunder/tests/integration/testutils" "github.com/stretchr/testify/suite" @@ -79,9 +78,7 @@ var ( googleRegTestOUID string ) -const ( - mockGoogleRegFlowPort = 8093 -) + type GoogleRegistrationFlowTestSuite struct { suite.Suite @@ -98,11 +95,16 @@ func TestGoogleRegistrationFlowTestSuite(t *testing.T) { func (ts *GoogleRegistrationFlowTestSuite) SetupSuite() { ts.config = &TestSuiteConfig{} - // Start mock Google server - mockServer, err := testutils.NewMockGoogleOIDCServer(mockGoogleRegFlowPort, + // Get shared Google OIDC server (started once for all test suites) + var err error + ts.mockGoogleServer, err = testutils.GetSharedMockServers().GetGoogleServer( "test_google_client", "test_google_secret") - ts.Require().NoError(err, "Failed to create mock Google server") - ts.mockGoogleServer = mockServer + if err != nil { + ts.T().Fatalf("Failed to get shared Google server: %v", err) + } + + // Reset the server to clear any state from previous test suites + ts.mockGoogleServer.Reset() ts.mockGoogleServer.AddUser(&testutils.GoogleUserInfo{ Sub: "google-reg-user-456", @@ -115,9 +117,6 @@ func (ts *GoogleRegistrationFlowTestSuite) SetupSuite() { Locale: "en", }) - err = ts.mockGoogleServer.Start() - ts.Require().NoError(err, "Failed to start mock Google server") - // Use the IDP created by database scripts ts.idpID = "test-google-idp-id" @@ -180,12 +179,8 @@ func (ts *GoogleRegistrationFlowTestSuite) TearDownSuite() { _ = testutils.DeleteUserType(ts.userSchemaID) } - // Stop mock server - if ts.mockGoogleServer != nil { - _ = ts.mockGoogleServer.Stop() - // Wait for port to be released - time.Sleep(200 * time.Millisecond) - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. } func (ts *GoogleRegistrationFlowTestSuite) TestGoogleRegistrationFlowInitiation() { diff --git a/tests/integration/flowregistration/http_request_executor_registration_test.go b/tests/integration/flowregistration/http_request_executor_registration_test.go index d1055eb4c..3a438289e 100644 --- a/tests/integration/flowregistration/http_request_executor_registration_test.go +++ b/tests/integration/flowregistration/http_request_executor_registration_test.go @@ -26,9 +26,7 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - mockHTTPServerPortReg = 9091 -) + var ( httpRequestRegTestOU = testutils.OrganizationUnit{ @@ -111,24 +109,17 @@ func (ts *HTTPRequestRegistrationFlowTestSuite) SetupSuite() { } httpRequestRegTestAppID = appID - // Start mock HTTP server - ts.mockServer = testutils.NewMockHTTPServer(mockHTTPServerPortReg) - err = ts.mockServer.Start() + // Get shared HTTP mock server (started once for all test suites) + ts.mockServer, err = testutils.GetSharedMockServers().GetHTTPServer() if err != nil { - ts.T().Fatalf("Failed to start mock HTTP server: %v", err) + ts.T().Fatalf("Failed to get shared HTTP server: %v", err) } - time.Sleep(100 * time.Millisecond) - ts.T().Log("Mock HTTP server started successfully") + ts.T().Log("Using shared HTTP mock server") } func (ts *HTTPRequestRegistrationFlowTestSuite) TearDownSuite() { - // Stop the mock HTTP server - if ts.mockServer != nil { - err := ts.mockServer.Stop() - if err != nil { - ts.T().Logf("Failed to stop mock HTTP server during teardown: %v", err) - } - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. // Delete all created users if err := testutils.CleanupUsers(ts.config.CreatedUserIDs); err != nil { diff --git a/tests/integration/flowregistration/ou_registration_test.go b/tests/integration/flowregistration/ou_registration_test.go index 4071bb7e9..cccadd3b4 100644 --- a/tests/integration/flowregistration/ou_registration_test.go +++ b/tests/integration/flowregistration/ou_registration_test.go @@ -27,10 +27,6 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - mockNotificationServerPortOU = 8098 -) - var ( ouRegTestOU = testutils.OrganizationUnit{ Handle: "ou-reg-flow-test-ou", @@ -149,12 +145,11 @@ func (ts *OURegistrationFlowTestSuite) SetupSuite() { } ts.smsFlowTestOUID = smsOUID - ts.mockServer = testutils.NewMockNotificationServer(mockNotificationServerPortOU) - err = ts.mockServer.Start() + // Get shared notification server (started once for all test suites) + ts.mockServer, err = testutils.GetSharedMockServers().GetNotificationServer() if err != nil { - ts.T().Fatalf("Failed to start mock notification server: %v", err) + ts.T().Fatalf("Failed to get shared notification server: %v", err) } - time.Sleep(100 * time.Millisecond) } func (ts *OURegistrationFlowTestSuite) TearDownSuite() { @@ -166,12 +161,8 @@ func (ts *OURegistrationFlowTestSuite) TearDownSuite() { ts.T().Logf("Failed to delete created OU %s during teardown: %v", ouID, err) } } - if ts.mockServer != nil { - err := ts.mockServer.Stop() - if err != nil { - ts.T().Logf("Failed to stop mock notification server during teardown: %v", err) - } - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. if ts.basicFlowTestAppID != "" { if err := testutils.DeleteApplication(ts.basicFlowTestAppID); err != nil { ts.T().Logf("Failed to delete test application during teardown: %v", err) @@ -371,10 +362,9 @@ func (ts *OURegistrationFlowTestSuite) TestSMSRegistrationFlowWithOUCreation() { ts.Require().NoError(err) ts.Require().Equal("INCOMPLETE", flowStep.FlowStatus) - time.Sleep(500 * time.Millisecond) - - lastMessage := ts.mockServer.GetLastMessage() - ts.Require().NotNil(lastMessage) + // Wait for SMS message with timeout (more reliable than fixed sleep) + lastMessage := ts.mockServer.WaitForMessage(2000) + ts.Require().NotNil(lastMessage, "Expected SMS message to be received within timeout") ts.Require().NotEmpty(lastMessage.OTP) inputs = map[string]string{ @@ -473,10 +463,9 @@ func (ts *OURegistrationFlowTestSuite) TestSMSRegistrationFlowWithOUCreationDupl flowStep, err := initiateRegistrationFlow(ts.smsFlowTestAppID, inputs) ts.Require().NoError(err) - time.Sleep(500 * time.Millisecond) - - lastMessage := ts.mockServer.GetLastMessage() - ts.Require().NotNil(lastMessage) + // Wait for SMS message with timeout (more reliable than fixed sleep) + lastMessage := ts.mockServer.WaitForMessage(2000) + ts.Require().NotNil(lastMessage, "Expected SMS message to be received within timeout") newHandle := tc.newOUHandle if newHandle == "" { diff --git a/tests/integration/flowregistration/sms_registration_test.go b/tests/integration/flowregistration/sms_registration_test.go index 46f2a4784..c722b5417 100644 --- a/tests/integration/flowregistration/sms_registration_test.go +++ b/tests/integration/flowregistration/sms_registration_test.go @@ -27,10 +27,6 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - mockNotificationServerPort = 8098 -) - var ( smsRegTestOU = testutils.OrganizationUnit{ Handle: "sms-reg-flow-test-ou", @@ -116,14 +112,12 @@ func (ts *SMSRegistrationFlowTestSuite) SetupSuite() { } ts.testAppID = appID - // Start mock notification server - ts.mockServer = testutils.NewMockNotificationServer(mockNotificationServerPort) - err = ts.mockServer.Start() + // Get shared notification server (started once for all test suites) + ts.mockServer, err = testutils.GetSharedMockServers().GetNotificationServer() if err != nil { - ts.T().Fatalf("Failed to start mock notification server: %v", err) + ts.T().Fatalf("Failed to get shared notification server: %v", err) } - time.Sleep(100 * time.Millisecond) - ts.T().Log("Mock notification server started successfully") + ts.T().Log("Using shared notification server") // Store original app config (this will be the created app config) ts.config.OriginalAppConfig, err = getAppConfig(ts.testAppID) @@ -138,13 +132,8 @@ func (ts *SMSRegistrationFlowTestSuite) TearDownSuite() { ts.T().Logf("Failed to cleanup users during teardown: %v", err) } - // Stop mock server - if ts.mockServer != nil { - err := ts.mockServer.Stop() - if err != nil { - ts.T().Logf("Failed to stop mock notification server during teardown: %v", err) - } - } + // Note: We don't stop the mock server here because it's shared across test suites. + // The shared server will be cleaned up when the test process exits. // Delete test application if ts.testAppID != "" { @@ -214,12 +203,9 @@ func (ts *SMSRegistrationFlowTestSuite) TestSMSRegistrationFlowWithMobileNumber( ts.Require().NotEmpty(otpFlowStep.Data.Inputs, "Flow should require inputs") ts.Require().True(HasInput(otpFlowStep.Data.Inputs, "otp"), "OTP input should be required") - // Wait for SMS to be sent - time.Sleep(1000 * time.Millisecond) - - // Verify SMS was sent - lastMessage := ts.mockServer.GetLastMessage() - ts.Require().NotNil(lastMessage, "Last message should not be nil") + // Wait for SMS message with timeout (more reliable than fixed sleep) + lastMessage := ts.mockServer.WaitForMessage(2000) + ts.Require().NotNil(lastMessage, "Expected SMS message to be received within timeout") ts.Require().NotEmpty(lastMessage.OTP, "OTP should be extracted from message") // Step 3: Complete registration with OTP @@ -354,12 +340,9 @@ func (ts *SMSRegistrationFlowTestSuite) TestSMSRegistrationFlowWithUsername() { ts.Require().NotEmpty(otpFlowStep.Data.Inputs, "Flow should require inputs after username input") ts.Require().True(HasInput(otpFlowStep.Data.Inputs, "otp"), "OTP input should be required after username input") - // Wait for SMS to be sent - time.Sleep(500 * time.Millisecond) - - // Verify SMS was sent - lastMessage := ts.mockServer.GetLastMessage() - ts.Require().NotNil(lastMessage, "SMS should have been sent") + // Wait for SMS message with timeout (more reliable than fixed sleep) + lastMessage := ts.mockServer.WaitForMessage(2000) + ts.Require().NotNil(lastMessage, "Expected SMS message to be received within timeout") ts.Require().NotEmpty(lastMessage.OTP, "OTP should be available") // Step 4: Complete registration with OTP @@ -433,8 +416,8 @@ func (ts *SMSRegistrationFlowTestSuite) TestSMSRegistrationFlowInvalidOTP() { ts.Require().Equal("INCOMPLETE", otpFlowStep.FlowStatus, "Expected flow status to be INCOMPLETE") - // Wait for SMS to be sent - time.Sleep(500 * time.Millisecond) + // Wait for SMS to be sent (we don't need the actual message for this test, just ensure it was sent) + _ = ts.mockServer.WaitForMessage(2000) // Step 2: Try with invalid OTP invalidOTPInputs := map[string]string{ @@ -484,12 +467,9 @@ func (ts *SMSRegistrationFlowTestSuite) TestSMSRegistrationFlowSingleRequestWith ts.Require().Equal("INCOMPLETE", flowStep.FlowStatus, "Expected flow status to be INCOMPLETE") ts.Require().Equal("VIEW", flowStep.Type, "Expected flow type to be VIEW") - // Wait for SMS to be sent - time.Sleep(500 * time.Millisecond) - - // Get the OTP from mock server - lastMessage := ts.mockServer.GetLastMessage() - ts.Require().NotNil(lastMessage, "SMS should have been sent") + // Wait for SMS message with timeout (more reliable than fixed sleep) + lastMessage := ts.mockServer.WaitForMessage(2000) + ts.Require().NotNil(lastMessage, "Expected SMS message to be received within timeout") ts.Require().NotEmpty(lastMessage.OTP, "OTP should be available") // Step 2: Complete with OTP diff --git a/tests/integration/testutils/mock_github_oauth_server.go b/tests/integration/testutils/mock_github_oauth_server.go index 906a1e391..2518f53cc 100644 --- a/tests/integration/testutils/mock_github_oauth_server.go +++ b/tests/integration/testutils/mock_github_oauth_server.go @@ -163,6 +163,18 @@ func (m *MockGithubOAuthServer) Stop() error { return nil } +// Reset clears all users, auth codes, and tokens from the mock server. +// This should be called at the start of each test suite to ensure clean state. +func (m *MockGithubOAuthServer) Reset() { + m.mutex.Lock() + defer m.mutex.Unlock() + m.authCodes = make(map[string]*GithubAuthCodeData) + m.accessTokens = make(map[string]*GithubTokenData) + m.users = make(map[string]*GithubUserInfo) + m.emails = make(map[string][]*GithubEmail) + m.authorizeFunc = nil +} + // GetURL returns the base URL of the mock server func (m *MockGithubOAuthServer) GetURL() string { return fmt.Sprintf("http://localhost:%d", m.port) diff --git a/tests/integration/testutils/mock_google_oidc_server.go b/tests/integration/testutils/mock_google_oidc_server.go index fca5d805b..28c74d8bf 100644 --- a/tests/integration/testutils/mock_google_oidc_server.go +++ b/tests/integration/testutils/mock_google_oidc_server.go @@ -182,6 +182,17 @@ func (m *MockGoogleOIDCServer) Stop() error { return nil } +// Reset clears all users, auth codes, and tokens from the mock server. +// This should be called at the start of each test suite to ensure clean state. +func (m *MockGoogleOIDCServer) Reset() { + m.mutex.Lock() + defer m.mutex.Unlock() + m.authCodes = make(map[string]*AuthCodeData) + m.accessTokens = make(map[string]*TokenData) + m.users = make(map[string]*GoogleUserInfo) + m.authorizeFunc = nil +} + // GetURL returns the base URL of the mock server func (m *MockGoogleOIDCServer) GetURL() string { return fmt.Sprintf("http://localhost:%d", m.port) diff --git a/tests/integration/testutils/mock_notification_server.go b/tests/integration/testutils/mock_notification_server.go index 5bb76aa8c..07e98a980 100644 --- a/tests/integration/testutils/mock_notification_server.go +++ b/tests/integration/testutils/mock_notification_server.go @@ -25,6 +25,7 @@ import ( "log" "net/http" "sync" + "time" ) // MockNotificationServer provides a mock HTTP server for testing SMS notifications @@ -182,6 +183,29 @@ func (m *MockNotificationServer) GetLastMessage() *SMSMessage { return &lastMessage } +// WaitForMessage waits for a message to arrive with a timeout. +// It polls every 50ms until a message is available or the timeout is reached. +// This is more reliable than a fixed sleep for integration tests. +func (m *MockNotificationServer) WaitForMessage(timeoutMs int) *SMSMessage { + pollInterval := 50 // milliseconds + elapsed := 0 + + for elapsed < timeoutMs { + m.mutex.RLock() + hasMessages := len(m.messages) > 0 + m.mutex.RUnlock() + + if hasMessages { + return m.GetLastMessage() + } + + time.Sleep(time.Duration(pollInterval) * time.Millisecond) + elapsed += pollInterval + } + + return nil +} + // ClearMessages clears all stored messages func (m *MockNotificationServer) ClearMessages() { m.mutex.Lock() diff --git a/tests/integration/testutils/mock_oauth_server.go b/tests/integration/testutils/mock_oauth_server.go index ad1e9b1a5..48d6e883c 100644 --- a/tests/integration/testutils/mock_oauth_server.go +++ b/tests/integration/testutils/mock_oauth_server.go @@ -127,6 +127,17 @@ func (m *MockOAuthServer) Stop() error { return nil } +// Reset clears all users, auth codes, and tokens from the mock server. +// This should be called at the start of each test suite to ensure clean state. +func (m *MockOAuthServer) Reset() { + m.mutex.Lock() + defer m.mutex.Unlock() + m.authCodes = make(map[string]*OAuthAuthCodeData) + m.accessTokens = make(map[string]*OAuthTokenData) + m.users = make(map[string]*OAuthUserInfo) + m.authorizeFunc = nil +} + // GetURL returns the base URL func (m *MockOAuthServer) GetURL() string { return m.baseURL diff --git a/tests/integration/testutils/mock_oidc_server.go b/tests/integration/testutils/mock_oidc_server.go index 29569ff78..cf5ad3968 100644 --- a/tests/integration/testutils/mock_oidc_server.go +++ b/tests/integration/testutils/mock_oidc_server.go @@ -179,6 +179,17 @@ func (m *MockOIDCServer) Stop() error { return nil } +// Reset clears all users, auth codes, and tokens from the mock server. +// This should be called at the start of each test suite to ensure clean state. +func (m *MockOIDCServer) Reset() { + m.mutex.Lock() + defer m.mutex.Unlock() + m.authCodes = make(map[string]*OIDCAuthCodeData) + m.accessTokens = make(map[string]*OIDCTokenData) + m.users = make(map[string]*OIDCUserInfo) + m.authorizeFunc = nil +} + // GetURL returns the base URL func (m *MockOIDCServer) GetURL() string { return m.issuer diff --git a/tests/integration/testutils/shared_mock_servers.go b/tests/integration/testutils/shared_mock_servers.go new file mode 100644 index 000000000..d2d80d0fd --- /dev/null +++ b/tests/integration/testutils/shared_mock_servers.go @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package testutils + +import ( + "fmt" + "log" + "sync" + "time" +) + +// SharedMockServers provides a centralized manager for all mock servers used in integration tests. +// It ensures that each mock server is started only once and shared across all test suites, +// preventing port conflicts and race conditions. +type SharedMockServers struct { + notificationServer *MockNotificationServer + httpServer *MockHTTPServer + githubServer *MockGithubOAuthServer + googleServer *MockGoogleOIDCServer + oauthServer *MockOAuthServer + oidcServer *MockOIDCServer + + // Track initialization state + notificationStarted bool + httpStarted bool + githubStarted bool + googleStarted bool + oauthStarted bool + oidcStarted bool + + mutex sync.Mutex +} + +// Default ports for mock servers +const ( + DefaultNotificationPort = 8098 + DefaultHTTPPort = 9091 + DefaultGithubPort = 8092 + DefaultGooglePort = 8093 + // Note: OAuth and OIDC servers share the same ports as GitHub and Google servers respectively. + // Only one of each pair can be active at a time. Tests are run sequentially, so this is acceptable. + DefaultOAuthPort = 8092 + DefaultOIDCPort = 8093 + // ServerStartupWaitTime is the duration to wait for a mock server to start + ServerStartupWaitTime = 100 * time.Millisecond +) + +// Singleton instance +var ( + sharedServers *SharedMockServers + sharedServersOnce sync.Once +) + +// GetSharedMockServers returns the singleton instance of SharedMockServers. +// This ensures all test suites use the same mock server instances. +func GetSharedMockServers() *SharedMockServers { + sharedServersOnce.Do(func() { + sharedServers = &SharedMockServers{} + }) + return sharedServers +} + +// GetNotificationServer returns the shared notification server, starting it if necessary. +func (s *SharedMockServers) GetNotificationServer() (*MockNotificationServer, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.notificationStarted { + return s.notificationServer, nil + } + + s.notificationServer = NewMockNotificationServer(DefaultNotificationPort) + if err := s.notificationServer.Start(); err != nil { + return nil, fmt.Errorf("failed to start notification server: %w", err) + } + + // Wait for server to be ready + time.Sleep(ServerStartupWaitTime) + s.notificationStarted = true + log.Printf("Shared notification server started on port %d", DefaultNotificationPort) + + return s.notificationServer, nil +} + +// GetHTTPServer returns the shared HTTP mock server, starting it if necessary. +func (s *SharedMockServers) GetHTTPServer() (*MockHTTPServer, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.httpStarted { + return s.httpServer, nil + } + + s.httpServer = NewMockHTTPServer(DefaultHTTPPort) + if err := s.httpServer.Start(); err != nil { + return nil, fmt.Errorf("failed to start HTTP server: %w", err) + } + + // Wait for server to be ready + time.Sleep(ServerStartupWaitTime) + s.httpStarted = true + log.Printf("Shared HTTP mock server started on port %d", DefaultHTTPPort) + + return s.httpServer, nil +} + +// GetGithubServer returns the shared GitHub OAuth server, starting it if necessary. +// The clientID and clientSecret are used for the first initialization only. +// Note: This server and GetOAuthServer use the same port (DefaultGithubPort). Only one can be active at a time. +func (s *SharedMockServers) GetGithubServer(clientID, clientSecret string) (*MockGithubOAuthServer, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.githubStarted { + return s.githubServer, nil + } + + // Check if OAuth server is running on the same port + if s.oauthStarted { + return nil, fmt.Errorf("cannot start GitHub server: OAuth server already running on port %d", DefaultOAuthPort) + } + + s.githubServer = NewMockGithubOAuthServer(DefaultGithubPort, clientID, clientSecret) + if err := s.githubServer.Start(); err != nil { + return nil, fmt.Errorf("failed to start GitHub server: %w", err) + } + + // Wait for server to be ready + time.Sleep(ServerStartupWaitTime) + s.githubStarted = true + log.Printf("Shared GitHub OAuth server started on port %d", DefaultGithubPort) + + return s.githubServer, nil +} + +// GetGoogleServer returns the shared Google OIDC server, starting it if necessary. +// The clientID and clientSecret are used for the first initialization only. +// Note: This server and GetOIDCServer use the same port (DefaultGooglePort). Only one can be active at a time. +func (s *SharedMockServers) GetGoogleServer(clientID, clientSecret string) (*MockGoogleOIDCServer, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.googleStarted { + return s.googleServer, nil + } + + // Check if OIDC server is running on the same port + if s.oidcStarted { + return nil, fmt.Errorf("cannot start Google server: OIDC server already running on port %d", DefaultOIDCPort) + } + + var err error + s.googleServer, err = NewMockGoogleOIDCServer(DefaultGooglePort, clientID, clientSecret) + if err != nil { + return nil, fmt.Errorf("failed to create Google server: %w", err) + } + + if err := s.googleServer.Start(); err != nil { + return nil, fmt.Errorf("failed to start Google server: %w", err) + } + + // Wait for server to be ready + time.Sleep(ServerStartupWaitTime) + s.googleStarted = true + log.Printf("Shared Google OIDC server started on port %d", DefaultGooglePort) + + return s.googleServer, nil +} + +// GetOAuthServer returns the shared generic OAuth server, starting it if necessary. +// Note: This server and GetGithubServer use the same port (DefaultOAuthPort). Only one can be active at a time. +func (s *SharedMockServers) GetOAuthServer(clientID, clientSecret string) (*MockOAuthServer, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.oauthStarted { + return s.oauthServer, nil + } + + // Check if GitHub server is running on the same port + if s.githubStarted { + return nil, fmt.Errorf("cannot start OAuth server: GitHub server already running on port %d", DefaultGithubPort) + } + + s.oauthServer = NewMockOAuthServer(DefaultOAuthPort, clientID, clientSecret) + if err := s.oauthServer.Start(); err != nil { + return nil, fmt.Errorf("failed to start OAuth server: %w", err) + } + + // Wait for server to be ready + time.Sleep(ServerStartupWaitTime) + s.oauthStarted = true + log.Printf("Shared OAuth server started on port %d", DefaultOAuthPort) + + return s.oauthServer, nil +} + +// GetOIDCServer returns the shared generic OIDC server, starting it if necessary. +// Note: This server and GetGoogleServer use the same port (DefaultOIDCPort). Only one can be active at a time. +func (s *SharedMockServers) GetOIDCServer(clientID, clientSecret string) (*MockOIDCServer, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.oidcStarted { + return s.oidcServer, nil + } + + // Check if Google server is running on the same port + if s.googleStarted { + return nil, fmt.Errorf("cannot start OIDC server: Google server already running on port %d", DefaultGooglePort) + } + + var err error + s.oidcServer, err = NewMockOIDCServer(DefaultOIDCPort, clientID, clientSecret) + if err != nil { + return nil, fmt.Errorf("failed to create OIDC server: %w", err) + } + + if err := s.oidcServer.Start(); err != nil { + return nil, fmt.Errorf("failed to start OIDC server: %w", err) + } + + // Wait for server to be ready + time.Sleep(ServerStartupWaitTime) + s.oidcStarted = true + log.Printf("Shared OIDC server started on port %d", DefaultOIDCPort) + + return s.oidcServer, nil +} + +// StopAll stops all mock servers. This should be called once when the entire test suite finishes. +// Note: In practice, the servers will be stopped when the test process exits. +// This method is provided for explicit cleanup if needed. +func (s *SharedMockServers) StopAll() { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.notificationStarted && s.notificationServer != nil { + if err := s.notificationServer.Stop(); err != nil { + log.Printf("Error stopping notification server: %v", err) + } + s.notificationStarted = false + } + + if s.httpStarted && s.httpServer != nil { + if err := s.httpServer.Stop(); err != nil { + log.Printf("Error stopping HTTP server: %v", err) + } + s.httpStarted = false + } + + if s.githubStarted && s.githubServer != nil { + if err := s.githubServer.Stop(); err != nil { + log.Printf("Error stopping GitHub server: %v", err) + } + s.githubStarted = false + } + + if s.googleStarted && s.googleServer != nil { + if err := s.googleServer.Stop(); err != nil { + log.Printf("Error stopping Google server: %v", err) + } + s.googleStarted = false + } + + if s.oauthStarted && s.oauthServer != nil { + if err := s.oauthServer.Stop(); err != nil { + log.Printf("Error stopping OAuth server: %v", err) + } + s.oauthStarted = false + } + + if s.oidcStarted && s.oidcServer != nil { + if err := s.oidcServer.Stop(); err != nil { + log.Printf("Error stopping OIDC server: %v", err) + } + s.oidcStarted = false + } + + log.Println("All shared mock servers stopped") +} + +// IsNotificationServerRunning returns true if the notification server is running. +func (s *SharedMockServers) IsNotificationServerRunning() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.notificationStarted +} + +// IsHTTPServerRunning returns true if the HTTP server is running. +func (s *SharedMockServers) IsHTTPServerRunning() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.httpStarted +} + +// IsGithubServerRunning returns true if the GitHub server is running. +func (s *SharedMockServers) IsGithubServerRunning() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.githubStarted +} + +// IsGoogleServerRunning returns true if the Google server is running. +func (s *SharedMockServers) IsGoogleServerRunning() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.googleStarted +} + +// IsOAuthServerRunning returns true if the OAuth server is running. +func (s *SharedMockServers) IsOAuthServerRunning() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.oauthStarted +} + +// IsOIDCServerRunning returns true if the OIDC server is running. +func (s *SharedMockServers) IsOIDCServerRunning() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.oidcStarted +}