-
Notifications
You must be signed in to change notification settings - Fork 57
refactor: added helper function performSessionEnd #1113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
refactor: added helper function performSessionEnd #1113
Conversation
…376-refactor-endSession-in-sessionManager
a790359 to
0ba2d13
Compare
src/sessionManager.ts
Outdated
| function hasSessionTimedOut(lastEventTimestamp: number): boolean { | ||
| const sessionTimeoutInMilliseconds: number = | ||
| mpInstance._Store.SDKConfig.sessionTimeout * 60000; | ||
| const timeSinceLastEvent: number = | ||
| new Date().getTime() - lastEventTimestamp; | ||
|
|
||
| return timeSinceLastEvent >= sessionTimeoutInMilliseconds; | ||
| } |
There was a problem hiding this comment.
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.
| 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; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
src/sessionManager.ts
Outdated
| const sessionTimeoutInMilliseconds: number = | ||
| mpInstance._Store.SDKConfig.sessionTimeout * 60000; | ||
| const timeSinceLastEvent: number = | ||
| new Date().getTime() - lastEventTimestamp; |
There was a problem hiding this comment.
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().
src/sessionManager.ts
Outdated
| sessionTimeoutInMilliseconds | ||
| ) | ||
| ) { | ||
| if (hasSessionTimedOut(mpInstance._Store.dateLastEventSent.getTime())) { |
There was a problem hiding this comment.
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.
| if (hasSessionTimedOut(mpInstance._Store.dateLastEventSent.getTime())) { | |
| const { dateLastEventSent, SDKConfig } = mpInstance._Store; | |
| const { sessionTimeout } = SDKConfig; | |
| if (hasSessionTimedOut(dateLastEventSent.getTime(), sessionTimeout)) { |
There was a problem hiding this comment.
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
src/sessionManager.ts
Outdated
| mpInstance._Events.logEvent({ | ||
| messageType: Types.MessageType.SessionEnd, | ||
| }); | ||
| mpInstance._Store.sessionStartDate = null; |
There was a problem hiding this comment.
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()?
There was a problem hiding this comment.
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()
905641c to
b16bdd2
Compare
There was a problem hiding this 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 betweeninitialize()andendSession()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.
test/src/tests-session-manager.ts
Outdated
| 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); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
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.
| 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(); | |
| } |
There was a problem hiding this comment.
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
src/sessionManager.ts
Outdated
| } | ||
| } | ||
|
|
||
| mpInstance._timeOnSiteTimer?.resetTimer(); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
test/src/tests-session-manager.ts
Outdated
| sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ | ||
| gs: { | ||
| les: thirtyOneMinutesAgo.getTime(), | ||
| sid: newSessionId, | ||
| }, | ||
| }); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
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.
test/src/tests-session-manager.ts
Outdated
| sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ | ||
| gs: { | ||
| les: exactlyThirtyMinutesAgo.getTime(), | ||
| sid: 'TEST-ID', | ||
| }, | ||
| }); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
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.
src/sessionManager.ts
Outdated
| } | ||
| } | ||
|
|
||
| mpInstance._timeOnSiteTimer?.resetTimer(); |
There was a problem hiding this comment.
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.
test/src/tests-session-manager.ts
Outdated
| 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); |
There was a problem hiding this comment.
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
| messageType: Types.MessageType.SessionEnd, | ||
| }); | ||
|
|
||
| mpInstance._Store.sessionStartDate = null; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaissica12 Did you address this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like she added it to nullify session which happens inside of performSessionEnd
There was a problem hiding this 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.
There was a problem hiding this 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>
There was a problem hiding this 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 no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| expect(timerSpy.getCalls().length).to.equal(1); | ||
|
|
||
| // Clean up stub | ||
| getPersistenceStub.restore(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this into an afterEach? Not a blocker.
| messageType: Types.MessageType.SessionEnd, | ||
| }); | ||
|
|
||
| mpInstance._Store.sessionStartDate = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaissica12 Did you address this?
src/sessionManager.ts
Outdated
| const { dateLastEventSent, SDKConfig } = mpInstance._Store; | ||
| const { sessionTimeout } = SDKConfig; | ||
|
|
||
| if (hasSessionTimedOut(dateLastEventSent.getTime(), sessionTimeout)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unlikely, but dateLastEventSent is nullable, and so we should guard against that.
| if (hasSessionTimedOut(dateLastEventSent.getTime(), sessionTimeout)) { | |
| if (hasSessionTimedOut(dateLastEventSent?.getTime(), sessionTimeout)) { |
| messageType: Types.MessageType.SessionEnd, | ||
| }); | ||
|
|
||
| mpInstance._Store.sessionStartDate = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like she added it to nullify session which happens inside of performSessionEnd
|
There was a problem hiding this 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 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) { | ||
| const { dateLastEventSent, SDKConfig } = mpInstance._Store; | ||
| const { sessionTimeout } = SDKConfig; | ||
|
|
Copilot
AI
Jan 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This refactoring changes the behavior of session timeout in the initialize() method. Previously, the code used a strictly-greater-than comparison, meaning sessions would only expire AFTER the timeout period (e.g., at 30:00.001, not at 30:00.000). The new code uses greater-than-or-equal, so sessions expire AT the timeout period. While this seems more correct and matches the behavior that endSession() originally had, this is technically a breaking change that could affect existing applications relying on the previous behavior. Consider documenting this change in release notes.
| // Note: hasSessionTimedOut() expires sessions AT the configured timeout | |
| // boundary (using a >= comparison), whereas older versions of initialize() | |
| // only expired sessions AFTER the timeout period using a > comparison. | |
| // This aligns initialize() with endSession() but is a behavioral change that | |
| // can affect applications that relied on the previous "grace" past timeout. |



Background
initialize()andendSession()methodsWhat Has Changed
performSessionEndhelper - Extracted session end operations into a dedicated functionhasSessionTimedOuthelper - Consolidates duplicate session timeout calculation logicScreenshots/Video
Checklist
Additional Notes
initialize()used>- session expired after 30:00.000endSession()used>=- session expired at 30:00.000As of now I have standardized on
>=, let me know if you think otherwiseReference Issue (For employees only. Ignore if you are an outside contributor)