mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
- Implemented share token creation and revocation for CLI sessions. - Added a new page for viewing shared CLI sessions with SSE support. - Introduced hooks for fetching and managing CLI session shares. - Enhanced the IssueTerminalTab component to handle share tokens and display active shares. - Updated API routes to support fetching and revoking share tokens. - Added unit tests for the CLI session share manager and rate limiter. - Updated localization files to include new strings for sharing functionality.
139 lines
3.9 KiB
JavaScript
139 lines
3.9 KiB
JavaScript
/**
|
|
* Unit tests for CLI session share tokens
|
|
*/
|
|
|
|
import { afterEach, describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
const shareUrl = new URL('../dist/core/services/cli-session-share.js', import.meta.url).href;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
let mod;
|
|
|
|
const originalNow = Date.now;
|
|
|
|
afterEach(() => {
|
|
Date.now = originalNow;
|
|
});
|
|
|
|
describe('CliSessionShareManager', async () => {
|
|
mod = await import(shareUrl);
|
|
|
|
it('creates a token record with expected fields', () => {
|
|
const mgr = new mod.CliSessionShareManager();
|
|
const record = mgr.createToken({
|
|
sessionKey: 's-1',
|
|
projectRoot: 'D:\\\\Claude_dms3',
|
|
mode: 'read',
|
|
ttlMs: 10_000,
|
|
});
|
|
|
|
assert.equal(record.sessionKey, 's-1');
|
|
assert.equal(record.projectRoot, 'D:\\\\Claude_dms3');
|
|
assert.equal(record.mode, 'read');
|
|
assert.match(record.token, /^[A-Za-z0-9_-]+$/);
|
|
assert.ok(record.expiresAt.endsWith('Z'));
|
|
});
|
|
|
|
it('validates only when sessionKey matches', () => {
|
|
const mgr = new mod.CliSessionShareManager();
|
|
const record = mgr.createToken({
|
|
sessionKey: 's-abc',
|
|
projectRoot: '/tmp/project',
|
|
mode: 'read',
|
|
ttlMs: 60_000,
|
|
});
|
|
|
|
assert.ok(mgr.validateToken(record.token, 's-abc'));
|
|
assert.equal(mgr.validateToken(record.token, 's-wrong'), null);
|
|
});
|
|
|
|
it('revokes tokens', () => {
|
|
const mgr = new mod.CliSessionShareManager();
|
|
const record = mgr.createToken({
|
|
sessionKey: 's-1',
|
|
projectRoot: '/tmp/project',
|
|
mode: 'write',
|
|
ttlMs: 60_000,
|
|
});
|
|
|
|
assert.equal(mgr.revokeToken(record.token), true);
|
|
assert.equal(mgr.revokeToken(record.token), false);
|
|
assert.equal(mgr.validateToken(record.token, record.sessionKey), null);
|
|
});
|
|
|
|
it('expires tokens and cleans up expired entries', () => {
|
|
let now = 1_700_000_000_000;
|
|
Date.now = () => now;
|
|
|
|
const mgr = new mod.CliSessionShareManager();
|
|
const recordA = mgr.createToken({
|
|
sessionKey: 's-a',
|
|
projectRoot: '/tmp/project',
|
|
mode: 'read',
|
|
ttlMs: 1_000,
|
|
});
|
|
const recordB = mgr.createToken({
|
|
sessionKey: 's-b',
|
|
projectRoot: '/tmp/project',
|
|
mode: 'read',
|
|
ttlMs: 10_000,
|
|
});
|
|
|
|
now += 1_001;
|
|
|
|
assert.equal(mgr.validateToken(recordA.token, recordA.sessionKey), null);
|
|
assert.ok(mgr.validateToken(recordB.token, recordB.sessionKey));
|
|
assert.equal(mgr.cleanupExpired(), 0);
|
|
|
|
now += 10_000;
|
|
assert.equal(mgr.cleanupExpired(), 1);
|
|
assert.equal(mgr.validateToken(recordB.token, recordB.sessionKey), null);
|
|
});
|
|
|
|
it('clamps ttlMs to a minimum of 1000ms', () => {
|
|
const fixedNow = 1_700_000_000_000;
|
|
Date.now = () => fixedNow;
|
|
|
|
const mgr = new mod.CliSessionShareManager();
|
|
const record = mgr.createToken({
|
|
sessionKey: 's-1',
|
|
projectRoot: '/tmp/project',
|
|
mode: 'read',
|
|
ttlMs: 1,
|
|
});
|
|
|
|
assert.equal(record.expiresAt, new Date(fixedNow + 1_000).toISOString());
|
|
});
|
|
|
|
it('lists tokens for a session (scoped to projectRoot) and sorts by expiresAt', () => {
|
|
let now = 1_700_000_000_000;
|
|
Date.now = () => now;
|
|
|
|
const mgr = new mod.CliSessionShareManager();
|
|
const t1 = mgr.createToken({
|
|
sessionKey: 's-1',
|
|
projectRoot: '/p1',
|
|
mode: 'read',
|
|
ttlMs: 5_000,
|
|
});
|
|
const t2 = mgr.createToken({
|
|
sessionKey: 's-1',
|
|
projectRoot: '/p1',
|
|
mode: 'write',
|
|
ttlMs: 10_000,
|
|
});
|
|
mgr.createToken({ sessionKey: 's-1', projectRoot: '/p2', mode: 'read', ttlMs: 10_000 });
|
|
mgr.createToken({ sessionKey: 's-2', projectRoot: '/p1', mode: 'read', ttlMs: 10_000 });
|
|
|
|
const list1 = mgr.listTokensForSession('s-1', '/p1');
|
|
assert.equal(list1.length, 2);
|
|
assert.equal(list1[0].token, t1.token);
|
|
assert.equal(list1[1].token, t2.token);
|
|
|
|
now += 5_001;
|
|
const list2 = mgr.listTokensForSession('s-1', '/p1');
|
|
assert.deepEqual(list2.map(r => r.token), [t2.token]);
|
|
});
|
|
});
|