Skip to content

Conversation

@jaissica12
Copy link
Contributor

@jaissica12 jaissica12 commented Nov 5, 2025

Background

  • Session timeout calculations were duplicated in initialize() and endSession() methods
  • Both methods checked for session timeout but used different approaches:
    • initialize() used Date object comparison
    • endSession() used millisecond arithmetic with inverted logic
  • Session end operations (logging event, clearing data, nullifying session) were scattered inline within endSession()

What Has Changed

  • Added performSessionEnd helper - Extracted session end operations into a dedicated function
  • Added hasSessionTimedOut helper - Consolidates duplicate session timeout calculation logic

Screenshots/Video

  • {Include any screenshots or video demonstrating the new feature or fix, if applicable}

Checklist

  • I have performed a self-review of my own code.
  • I have made corresponding changes to the documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have tested this locally.

Additional Notes

  • The original code used different comparison operators:
  • initialize() used > - session expired after 30:00.000
  • endSession() used >= - session expired at 30:00.000

As of now I have standardized on >=, let me know if you think otherwise

Reference Issue (For employees only. Ignore if you are an outside contributor)

@jaissica12 jaissica12 marked this pull request as draft November 5, 2025 15:37
@jaissica12 jaissica12 marked this pull request as ready for review December 1, 2025 14:39
@jaissica12 jaissica12 requested a review from rmi22186 December 1, 2025 14:42
@jaissica12 jaissica12 changed the base branch from development to blackout2025 December 3, 2025 17:31
@jaissica12 jaissica12 changed the base branch from blackout2025 to development December 3, 2025 17:32
@rmi22186 rmi22186 force-pushed the development branch 3 times, most recently from a790359 to 0ba2d13 Compare December 10, 2025 15:31
Comment on lines 215 to 222
function hasSessionTimedOut(lastEventTimestamp: number): boolean {
const sessionTimeoutInMilliseconds: number =
mpInstance._Store.SDKConfig.sessionTimeout * 60000;
const timeSinceLastEvent: number =
new Date().getTime() - lastEventTimestamp;

return timeSinceLastEvent >= sessionTimeoutInMilliseconds;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's avoid pulling in the entire store when we only need a single value.

Suggested change
function hasSessionTimedOut(lastEventTimestamp: number): boolean {
const sessionTimeoutInMilliseconds: number =
mpInstance._Store.SDKConfig.sessionTimeout * 60000;
const timeSinceLastEvent: number =
new Date().getTime() - lastEventTimestamp;
return timeSinceLastEvent >= sessionTimeoutInMilliseconds;
}
function hasSessionTimedOut(lastEventTimestamp: number, sessionTimeout: number): boolean {
const sessionTimeoutInMilliseconds: number = sessionTimeout * 60000;
const timeSinceLastEvent: number =
new Date().getTime() - lastEventTimestamp;
return timeSinceLastEvent >= sessionTimeoutInMilliseconds;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!

const sessionTimeoutInMilliseconds: number =
mpInstance._Store.SDKConfig.sessionTimeout * 60000;
const timeSinceLastEvent: number =
new Date().getTime() - lastEventTimestamp;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think I agree with SonarCloud. let's use Date.now() instead of new Date().getTime().

sessionTimeoutInMilliseconds
)
) {
if (hasSessionTimedOut(mpInstance._Store.dateLastEventSent.getTime())) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you follow my suggestion below, this can be destructured and cleaned up.

Suggested change
if (hasSessionTimedOut(mpInstance._Store.dateLastEventSent.getTime())) {
const { dateLastEventSent, SDKConfig } = mpInstance._Store;
const { sessionTimeout } = SDKConfig;
if (hasSessionTimedOut(dateLastEventSent.getTime(), sessionTimeout)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for suggesting this, cleaned up initialize function

mpInstance._Events.logEvent({
messageType: Types.MessageType.SessionEnd,
});
mpInstance._Store.sessionStartDate = null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we just add this line to nullifySession()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added this.sessionStartDate = null; to Store. nullifySession()

@rmi22186 rmi22186 force-pushed the development branch 2 times, most recently from 905641c to b16bdd2 Compare December 10, 2025 21:07
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors session management logic by extracting two helper functions to eliminate code duplication and improve maintainability. The refactoring consolidates timeout calculations that previously used different comparison approaches and extracts session end operations into a reusable function.

Key Changes:

  • Extracted hasSessionTimedOut() helper to unify session timeout logic previously duplicated between initialize() and endSession() with different comparison operators
  • Extracted performSessionEnd() helper to consolidate session end operations (logging, clearing data, resetting timer)
  • Standardized on >= comparison operator for timeout checks (expires at exactly 30 minutes)

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/sessionManager.ts Refactored session timeout and end logic into two helper functions; simplified initialize() and endSession() by delegating to the new helpers
src/store.ts Updated nullifySession() to include clearing sessionStartDate, consolidating cleanup logic
test/src/tests-session-manager.ts Added comprehensive test coverage for both new helper functions with 11 new test cases covering timeout scenarios, session end operations, and edge cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 363 to 380
const now = new Date();
const exactlyThirtyMinutesAgo = new Date();
exactlyThirtyMinutesAgo.setMinutes(now.getMinutes() - 30);

mParticle.init(apiKey, window.mParticle.config);
const mpInstance = mParticle.getInstance();

sinon.stub(mpInstance._Persistence, 'getPersistence').returns({
gs: {
les: exactlyThirtyMinutesAgo.getTime(),
sid: 'TEST-ID',
},
});

mpInstance._SessionManager.endSession();

// At exactly 30 minutes, session should be expired
expect(mpInstance._Store.sessionId).to.equal(null);
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

This test has a potential timing issue. The test creates exactlyThirtyMinutesAgo using the current time, but by the time hasSessionTimedOut() is called internally (which uses Date.now()), a few milliseconds may have passed. This could make the elapsed time slightly more than 30 minutes, causing the test to pass for the wrong reason. Consider using fake timers (sinon.useFakeTimers) to control time precisely, or add a small buffer to ensure the test is deterministic.

Suggested change
const now = new Date();
const exactlyThirtyMinutesAgo = new Date();
exactlyThirtyMinutesAgo.setMinutes(now.getMinutes() - 30);
mParticle.init(apiKey, window.mParticle.config);
const mpInstance = mParticle.getInstance();
sinon.stub(mpInstance._Persistence, 'getPersistence').returns({
gs: {
les: exactlyThirtyMinutesAgo.getTime(),
sid: 'TEST-ID',
},
});
mpInstance._SessionManager.endSession();
// At exactly 30 minutes, session should be expired
expect(mpInstance._Store.sessionId).to.equal(null);
const now = Date.now();
const clock = sinon.useFakeTimers(now);
const thirtyMinutesInMs = 30 * 60 * 1000;
const exactlyThirtyMinutesAgo = new Date(now - thirtyMinutesInMs);
mParticle.init(apiKey, window.mParticle.config);
const mpInstance = mParticle.getInstance();
const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({
gs: {
les: exactlyThirtyMinutesAgo.getTime(),
sid: 'TEST-ID',
},
});
try {
mpInstance._SessionManager.endSession();
// At exactly 30 minutes, session should be expired
expect(mpInstance._Store.sessionId).to.equal(null);
} finally {
getPersistenceStub.restore();
clock.restore();
}

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

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

@jaissica12 let's test this in case the robot is telling the truth

}
}

mpInstance._timeOnSiteTimer?.resetTimer();
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The timer reset at this line is redundant when the session has timed out, because performSessionEnd() already resets the timer at line 238. This results in the timer being reset twice when a session times out via the endSession() method. Consider removing this line or moving the timer reset logic into the else block at line 170 to avoid the duplication.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

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

@jaissica12 Can you try making this code change and see if it works? I think it may clean up the rafactor more.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!

Comment on lines 349 to 354
sinon.stub(mpInstance._Persistence, 'getPersistence').returns({
gs: {
les: thirtyOneMinutesAgo.getTime(),
sid: newSessionId,
},
});
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The sinon.stub is not cleaned up after the test. This stub will persist and could affect subsequent tests in this test suite. Use sinon.stub().returns() and restore it in a cleanup hook, or use sinon.restore() at the end of the test, or better yet, wrap this test in a context with its own beforeEach/afterEach to handle stub lifecycle.

Copilot uses AI. Check for mistakes.
Comment on lines 370 to 375
sinon.stub(mpInstance._Persistence, 'getPersistence').returns({
gs: {
les: exactlyThirtyMinutesAgo.getTime(),
sid: 'TEST-ID',
},
});
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The sinon.stub is not cleaned up after the test. This stub will persist and could affect subsequent tests in this test suite. Use sinon.stub().returns() and restore it in a cleanup hook, or use sinon.restore() at the end of the test, or better yet, wrap this test in a context with its own beforeEach/afterEach to handle stub lifecycle.

Copilot uses AI. Check for mistakes.
}
}

mpInstance._timeOnSiteTimer?.resetTimer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

@jaissica12 Can you try making this code change and see if it works? I think it may clean up the rafactor more.

Comment on lines 363 to 380
const now = new Date();
const exactlyThirtyMinutesAgo = new Date();
exactlyThirtyMinutesAgo.setMinutes(now.getMinutes() - 30);

mParticle.init(apiKey, window.mParticle.config);
const mpInstance = mParticle.getInstance();

sinon.stub(mpInstance._Persistence, 'getPersistence').returns({
gs: {
les: exactlyThirtyMinutesAgo.getTime(),
sid: 'TEST-ID',
},
});

mpInstance._SessionManager.endSession();

// At exactly 30 minutes, session should be expired
expect(mpInstance._Store.sessionId).to.equal(null);
Copy link
Collaborator

Choose a reason for hiding this comment

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

@jaissica12 let's test this in case the robot is telling the truth

* @param sessionTimeout - Session timeout in minutes
* @returns true if the session has expired, false otherwise
*/
function hasSessionTimedOut(lastEventTimestamp: number, sessionTimeout: number): boolean {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This might be an edge case, since we lastEventTimestamp and sessionTimeout have default values, but I would add a guard clause here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added!

messageType: Types.MessageType.SessionEnd,
});

mpInstance._Store.sessionStartDate = null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I noticed we're not including this line in the performSessionEnd function. Can it be used there? If not, can it be safely removed?

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

remove trailing whitespace

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@sonarqubecloud
Copy link

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants