merge: integrate remote learning module safely
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
2026-08-17 01:05:49 +08:00
125 changed files with 11451 additions and 12132 deletions

View File

@@ -1,13 +1,16 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import {
consumeWorksSquareStartupRuntimeCleanupRequired,
clearWorksSquareSession,
flushWorksSquareSessionPersistence,
getValidWorksSquareAccessToken,
getWorksSquareAccountBinding,
getWorksSquareSessionSnapshot,
initializeWorksSquareSession,
markWorksSquareSessionActive,
resetWorksSquareSessionForTests,
storeWorksSquareSession,
storeWorksSquareSessionFromTokenPayload,
subscribeWorksSquareSession,
WORKS_SQUARE_SESSION_IDLE_TIMEOUT_MS,
type WorksSquareSessionInput,
@@ -16,6 +19,10 @@ import {
const DAY_MS = 24 * 60 * 60 * 1000;
function jwt(claims: Record<string, unknown>): string {
return `header.${Buffer.from(JSON.stringify(claims)).toString('base64url')}.signature`;
}
describe('works-square-session service', () => {
beforeEach(() => {
vi.useFakeTimers();
@@ -402,4 +409,77 @@ describe('works-square-session service', () => {
const body = secondFetch.mock.calls[0][1].body as URLSearchParams;
expect(String(body)).toContain('refresh_token=rotated-refresh-token');
});
it('uses user_id ahead of username for a stable opaque account partition', () => {
storeWorksSquareSessionFromTokenPayload({
access_token: 'access-a',
user_id: 'raw-user-123',
username: 'first@example.com',
expires_in: 3600,
});
const first = getWorksSquareAccountBinding();
storeWorksSquareSessionFromTokenPayload({
access_token: 'access-b',
user_id: 'raw-user-123',
username: 'renamed@example.com',
expires_in: 3600,
});
const second = getWorksSquareAccountBinding();
expect(first?.accountKey).toMatch(/^[0-9a-f]{64}$/);
expect(second?.accountKey).toBe(first?.accountKey);
expect(second?.accountKey).not.toContain('raw-user-123');
expect(second?.epoch).toBe(first?.epoch);
});
it('retains the account partition across refresh, clears it on logout, and changes it on account switch', async () => {
storeWorksSquareSessionFromTokenPayload({
access_token: 'old-a',
refresh_token: 'refresh-a',
user_id: 'account-a',
expires_in: 1,
});
const accountA = getWorksSquareAccountBinding();
const fetchImpl = vi.fn().mockResolvedValue(new Response(JSON.stringify({
access_token: 'refreshed-a',
refresh_token: 'refresh-a-2',
user_id: 'unexpected-account-b',
expires_in: 3600,
}), { status: 200 }));
await getValidWorksSquareAccessToken({ fetchImpl, forceRefresh: true });
expect(getWorksSquareAccountBinding()).toEqual(accountA);
clearWorksSquareSession();
expect(getWorksSquareAccountBinding()).toBeNull();
storeWorksSquareSessionFromTokenPayload({
access_token: 'access-b',
user_id: 'account-b',
expires_in: 3600,
});
expect(getWorksSquareAccountBinding()?.accountKey).not.toBe(accountA?.accountKey);
expect(getWorksSquareAccountBinding()?.epoch).toBeGreaterThan(accountA?.epoch ?? 0);
});
it('migrates a legacy persisted Renderer session from safe JWT identity claims', async () => {
const persistence: WorksSquareSessionPersistence = {
load: vi.fn().mockResolvedValue({
accessToken: jwt({ user_id: 'legacy-user', username: 'legacy@example.com' }),
refreshToken: 'legacy-refresh',
expiresAt: Date.now() + 3600_000,
lastActiveAt: Date.now(),
}),
save: vi.fn().mockResolvedValue(undefined),
};
await initializeWorksSquareSession({ persistence });
const binding = getWorksSquareAccountBinding();
expect(binding?.accountKey).toMatch(/^[0-9a-f]{64}$/);
expect(binding?.accountKey).not.toContain('legacy-user');
expect(persistence.save).toHaveBeenCalledWith(expect.objectContaining({
accountPartitionKey: binding?.accountKey,
}));
});
});