Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 30, 2025

Purpose

Integration tests were failing intermittently due to race conditions when multiple test suites started/stopped their own mock servers on the same ports (8092, 8093, 8098, 9091). When teardown didn't complete before the next suite's setup, ports were left in inconsistent states.

Approach

Introduced a singleton SharedMockServers manager that starts each mock server once and shares it across all test suites.

Key changes:

  • Added testutils/shared_mock_servers.go with thread-safe singleton pattern
  • All test suites now call GetSharedMockServers().Get*Server() instead of creating/starting their own
  • Removed per-suite Start()/Stop() calls from SetupSuite()/TearDownSuite()
  • Added guards to prevent conflicting server types on shared ports (GitHub/OAuth on 8092, Google/OIDC on 8093)

Usage:

// Before: Each suite created and managed its own server
ts.mockServer = testutils.NewMockNotificationServer(8098)
ts.mockServer.Start()
// ...
ts.mockServer.Stop()

// After: Get shared server (started on first access, never stopped)
ts.mockServer, err = testutils.GetSharedMockServers().GetNotificationServer()

Updated test files:

  • flowauthn/: sms_auth, decision_auth, http_request_executor_auth, github_auth, google_auth, conditional_exec_auth
  • flowregistration/: sms_registration, ou_registration, http_request_executor_registration, github_registration, google_registration
  • authn/: oauth_auth, oidc_auth

Related Issues

  • N/A

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.
Original prompt

This section details on the original issue you should resolve

<issue_title>Stabilize integration tests by using a single shared mock server instead of multiple per-test servers</issue_title>
<issue_description>Currently, in our integration tests we spin up separate mock servers for the external integrations.

Each of these is started individually within the relevant tests. My assumption is that the intermittent test failures happen when one or more of these mock servers are not killed properly or there is a race condition between starting/stopping them. This can leave ports in an inconsistent state and cause random failures.

Instead of having multiple ad-hoc mock servers, the idea is to introduce a single shared test server that is started once for the integration test suite and stopped once when the suite finishes. Different scenarios (Google, GitHub, SMS, webhook, etc.) can then be handled via different HTTP paths/handlers on this single server.

The intention of this change is specifically to stabilize the following tests, which are currently failing intermittently:

=== Failed
=== FAIL: flowregistration TestOURegistrationFlowTestSuite/TestSMSRegistrationFlowWithOUCreationDuplicateError/DuplicateOUName (5.01s)
    ouregistration_test.go:491: 
                Error Trace:    /Users/piumini/Work/THUNDER/thunder/tests/integration/flowregistration/ouregistration_test.go:491
                                                        /Users/piumini/go/pkg/mod/github.com/stretchr/[email protected]/suite/suite.go:115
                Error:          Expected value not to be nil.
                Test:           TestOURegistrationFlowTestSuite/TestSMSRegistrationFlowWithOUCreationDuplicateError/DuplicateOUName

=== FAIL: flowregistration TestOURegistrationFlowTestSuite/TestSMSRegistrationFlowWithOUCreationDuplicateError (10.04s)

=== FAIL: flowregistration TestOURegistrationFlowTestSuite (11.46s)
2025/11/28 14:52:19 Starting mock notification server on port 8098

These failures appear to be related to the mock notification server (port 8098) and are not consistently reproducible, which suggests a lifecycle / timing issue rather than a logic bug in the test itself.</issue_description>

<agent_instructions>Introduce a single shared test mock server
Start server once per test suite
Stop server once on suite exit
Update existing tests to use the shared server
Add guards against lifecycle/race issues</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Stabilize integration tests using a single mock server Stabilize integration tests by using a single shared mock server instead of multiple per-test servers Nov 30, 2025
@ThaminduDilshan ThaminduDilshan added skip-changelog Skip generating changelog for a particular PR and removed bug Something isn't working Type/Bug labels Dec 5, 2025
Copilot AI and others added 3 commits December 8, 2025 14:45
…erver state

The shared mock server implementation was causing race conditions where users,
auth codes, and tokens from one test suite would persist when another test
suite ran. This led to non-deterministic behavior since Go maps iterate in
random order, causing the mock server to randomly select different users.

Changes:
- Added Reset() method to all mock servers (Google, GitHub, OAuth, OIDC)
  to clear users, auth codes, tokens, and custom authorize functions
- Updated all test suites to call Reset() in SetupSuite() before adding
  test users, ensuring clean state for each test suite
- This eliminates race conditions and makes tests deterministic

Affected test suites:
- GoogleAuthFlowTestSuite
- ConditionalExecAuthFlowTestSuite
- GoogleRegistrationFlowTestSuite
- GithubAuthFlowTestSuite
- GithubRegistrationFlowTestSuite
- OAuthAuthTestSuite
- OIDCAuthTestSuite

Fixes intermittent failures in TestGoogleAuthFlowCompleteSuccess and
TestGoogleAuthFlowMultipleUsersSuccess among others.
@KaveeshaPiumini KaveeshaPiumini force-pushed the copilot/stabilize-integration-tests branch from 8839e50 to 89528a8 Compare December 8, 2025 09:15
@codecov
Copy link

codecov bot commented Dec 8, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.25%. Comparing base (14afd91) to head (d5964f2).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #870       +/-   ##
===========================================
+ Coverage   62.07%   88.25%   +26.18%     
===========================================
  Files         322      324        +2     
  Lines       26441    26446        +5     
  Branches      606      606               
===========================================
+ Hits        16412    23340     +6928     
+ Misses       7593     1933     -5660     
+ Partials     2436     1173     -1263     
Flag Coverage Δ
backend-integration-postgres 58.97% <ø> (-0.02%) ⬇️
backend-integration-sqlite 58.93% <ø> (-0.02%) ⬇️
backend-unit 76.31% <ø> (?)
frontend-apps-develop-unit 88.45% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Add WaitForMessage method to MockNotificationServer that polls for
  messages with a configurable timeout instead of using fixed sleep
- Replace time.Sleep + GetLastMessage calls with WaitForMessage in
  SMS registration tests for more reliable test execution
- This addresses intermittent test failures caused by timing issues
  when waiting for SMS OTP messages
ts.testAppID = appID

// Start mock notification server
ts.mockServer = testutils.NewMockNotificationServer(mockNotificationServerPort)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we change this constructor to private newMockNotificationServer. So we can make sure mock server will be always retrieved from testutils.GetSharedMockServers() in future.

Let's do this for all the mock server constructor in the test_utils package


// Stop mock server
if ts.mockServer != nil {
err := ts.mockServer.Stop()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sameway, we can make the stop package private. So other package can't invoke it.

Let's do this for all the mock server constructor in the test_utils package

time.Sleep(500 * time.Millisecond)

// Verify SMS was sent
lastMessage := ts.mockServer.GetLastMessage()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sameway, we can make the getLastMessage package private. So other package can't invoke it.

Let's do this for all the mock server constructor in the test_utils package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog Skip generating changelog for a particular PR Type/Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stabilize integration tests by using a single shared mock server instead of multiple per-test servers

4 participants