diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2025-06-04 18:55:21 +0200 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2025-06-04 20:00:05 +0200 |
commit | 094a48be1ddc26aa2b445ab0997ec1f0de5ff593 (patch) | |
tree | f47c4d85474a989673d24f0f33127c51f6aa0b1f | |
parent | 23a6c0cb907ca9b9cd6347350e383aa83ab00815 (diff) | |
download | nextcloud-server-fix/requesttoken.tar.gz nextcloud-server-fix/requesttoken.zip |
test(core): migrate session heartbeat testsfix/requesttoken
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
-rw-r--r-- | core/js/tests/specs/coreSpec.js | 87 | ||||
-rw-r--r-- | core/src/tests/OC/session-heartbeat.spec.ts | 123 |
2 files changed, 123 insertions, 87 deletions
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 195b6dca99a..3cbd7623a47 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -119,93 +119,6 @@ describe('Core base tests', function() { })).toEqual('number=123'); }); }); - describe('Session heartbeat', function() { - var clock, - oldConfig, - counter; - - beforeEach(function() { - clock = sinon.useFakeTimers(); - oldConfig = OC.config; - counter = 0; - - fakeServer.autoRespond = true; - fakeServer.autoRespondAfter = 0; - fakeServer.respondWith(/\/csrftoken/, function(xhr) { - counter++; - xhr.respond(200, {'Content-Type': 'application/json'}, '{"token": "pgBEsb3MzTb1ZPd2mfDZbQ6/0j3OrXHMEZrghHcOkg8=:3khw5PSa+wKQVo4f26exFD3nplud9ECjJ8/Y5zk5/k4="}'); - }); - $(document).off('ajaxComplete'); // ignore previously registered heartbeats - }); - afterEach(function() { - clock.restore(); - /* jshint camelcase: false */ - OC.config = oldConfig; - $(document).off('ajaxError'); - $(document).off('ajaxComplete'); - }); - it('sends heartbeat half the session lifetime when heartbeat enabled', function() { - /* jshint camelcase: false */ - OC.config = { - session_keepalive: true, - session_lifetime: 300 - }; - window.initCore(); - - expect(counter).toEqual(0); - - // less than half, still nothing - clock.tick(100 * 1000); - expect(counter).toEqual(0); - - // reach past half (160), one call - clock.tick(55 * 1000); - expect(counter).toEqual(1); - - // almost there to the next, still one - clock.tick(140 * 1000); - expect(counter).toEqual(1); - - // past it, second call - clock.tick(20 * 1000); - expect(counter).toEqual(2); - }); - it('does not send heartbeat when heartbeat disabled', function() { - /* jshint camelcase: false */ - OC.config = { - session_keepalive: false, - session_lifetime: 300 - }; - window.initCore(); - - expect(counter).toEqual(0); - - clock.tick(1000000); - - // still nothing - expect(counter).toEqual(0); - }); - it('limits the heartbeat between one minute and one day', function() { - /* jshint camelcase: false */ - var setIntervalStub = sinon.stub(window, 'setInterval'); - OC.config = { - session_keepalive: true, - session_lifetime: 5 - }; - window.initCore(); - expect(setIntervalStub.getCall(0).args[1]).toEqual(60 * 1000); - setIntervalStub.reset(); - - OC.config = { - session_keepalive: true, - session_lifetime: 48 * 3600 - }; - window.initCore(); - expect(setIntervalStub.getCall(0).args[1]).toEqual(24 * 3600 * 1000); - - setIntervalStub.restore(); - }); - }); describe('Parse query string', function() { it('Parses query string from full URL', function() { var query = OC.parseQueryString('http://localhost/stuff.php?q=a&b=x'); diff --git a/core/src/tests/OC/session-heartbeat.spec.ts b/core/src/tests/OC/session-heartbeat.spec.ts new file mode 100644 index 00000000000..129291c0847 --- /dev/null +++ b/core/src/tests/OC/session-heartbeat.spec.ts @@ -0,0 +1,123 @@ +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' + +const requestToken = vi.hoisted(() => ({ + fetchRequestToken: vi.fn<() => Promise<string>>(), + setRequestToken: vi.fn<(token: string) => void>(), +})) +vi.mock('../../OC/requesttoken.ts', () => requestToken) + +const initialState = vi.hoisted(() => ({ loadState: vi.fn() })) +vi.mock('@nextcloud/initial-state', () => initialState) + +describe('Session heartbeat', () => { + beforeAll(() => { + vi.useFakeTimers() + }) + + beforeEach(() => { + vi.clearAllTimers() + vi.resetModules() + vi.resetAllMocks() + }) + + it('sends heartbeat half the session lifetime when heartbeat enabled', async () => { + initialState.loadState.mockImplementationOnce(() => ({ + session_keepalive: true, + session_lifetime: 300, + })) + + const { initSessionHeartBeat } = await import('../../session-heartbeat.ts') + initSessionHeartBeat() + + // initial state loaded + expect(initialState.loadState).toBeCalledWith('core', 'config') + + // less than half, still nothing + await vi.advanceTimersByTimeAsync(100 * 1000) + expect(requestToken.fetchRequestToken).not.toBeCalled() + + // reach past half, one call + await vi.advanceTimersByTimeAsync(60 * 1000) + expect(requestToken.fetchRequestToken).toBeCalledTimes(1) + + // almost there to the next, still one + await vi.advanceTimersByTimeAsync(135 * 1000) + expect(requestToken.fetchRequestToken).toBeCalledTimes(1) + + // past it, second call + await vi.advanceTimersByTimeAsync(5 * 1000) + expect(requestToken.fetchRequestToken).toBeCalledTimes(2) + }) + + it('does not send heartbeat when heartbeat disabled', async () => { + initialState.loadState.mockImplementationOnce(() => ({ + session_keepalive: false, + session_lifetime: 300, + })) + + const { initSessionHeartBeat } = await import('../../session-heartbeat.ts') + initSessionHeartBeat() + + // initial state loaded + expect(initialState.loadState).toBeCalledWith('core', 'config') + + // less than half, still nothing + await vi.advanceTimersByTimeAsync(100 * 1000) + expect(requestToken.fetchRequestToken).not.toBeCalled() + + // more than one, still nothing + await vi.advanceTimersByTimeAsync(300 * 1000) + expect(requestToken.fetchRequestToken).not.toBeCalled() + }) + + it('limit heartbeat to at least one minute', async () => { + initialState.loadState.mockImplementationOnce(() => ({ + session_keepalive: true, + session_lifetime: 55, + })) + + const { initSessionHeartBeat } = await import('../../session-heartbeat.ts') + initSessionHeartBeat() + + // initial state loaded + expect(initialState.loadState).toBeCalledWith('core', 'config') + + // 30 / 55 seconds + await vi.advanceTimersByTimeAsync(30 * 1000) + expect(requestToken.fetchRequestToken).not.toBeCalled() + + // 59 / 55 seconds should not be called except it does not limit + await vi.advanceTimersByTimeAsync(29 * 1000) + expect(requestToken.fetchRequestToken).not.toBeCalled() + + // now one minute has passed + await vi.advanceTimersByTimeAsync(1000) + expect(requestToken.fetchRequestToken).toHaveBeenCalledOnce() + }) + + it('limit heartbeat to at least one minute', async () => { + initialState.loadState.mockImplementationOnce(() => ({ + session_keepalive: true, + session_lifetime: 50 * 60 * 60, + })) + + const { initSessionHeartBeat } = await import('../../session-heartbeat.ts') + initSessionHeartBeat() + + // initial state loaded + expect(initialState.loadState).toBeCalledWith('core', 'config') + + // 23 hours + await vi.advanceTimersByTimeAsync(23 * 60 * 60 * 1000) + expect(requestToken.fetchRequestToken).not.toBeCalled() + + // one day - it should be called now + await vi.advanceTimersByTimeAsync(60 * 60 * 1000) + expect(requestToken.fetchRequestToken).toHaveBeenCalledOnce() + }) +}) |