实现客户端登录七天滑动续期
需求:解决短效访问令牌到期后客户端一小时掉登录的问题。 实现:由 Electron Main 加密管理并轮换刷新凭据,按真实用户活动续期,七天闲置后清理会话,并补齐并发、迁移和终态回归测试。
This commit is contained in:
@@ -1175,7 +1175,7 @@ describe('Works Square AI design adapter', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps the persisted Session key when a close result is uncertain', async () => {
|
||||
it('rotates the persisted Session key even when a close result is uncertain', async () => {
|
||||
const rotate = vi.fn().mockResolvedValue('design-stream-next');
|
||||
const fetchMock = vi.fn<typeof fetch>()
|
||||
.mockResolvedValueOnce(jsonResponse({ session_id: 'session-one', status: 'active' }, 201))
|
||||
@@ -1204,7 +1204,44 @@ describe('Works Square AI design adapter', () => {
|
||||
await expect(adapter.closeEventSessions()).rejects.toThrow(
|
||||
'Failed to close 1 AI design Agent Session(s)',
|
||||
);
|
||||
expect(rotate).not.toHaveBeenCalled();
|
||||
expect(rotate).toHaveBeenCalledWith('workspace-one');
|
||||
});
|
||||
|
||||
it('uses the captured terminal token and tolerates remote close failure after rotating', async () => {
|
||||
const rotate = vi.fn().mockResolvedValue('design-stream-next');
|
||||
const fetchMock = vi.fn<typeof fetch>()
|
||||
.mockResolvedValueOnce(jsonResponse({ session_id: 'session-one', status: 'active' }, 201))
|
||||
.mockResolvedValueOnce(jsonResponse({
|
||||
stream_url: '/api/agents/sessions/session-one/ws?ticket=ticket-one',
|
||||
}))
|
||||
.mockResolvedValueOnce(jsonResponse({
|
||||
detail: { code: 'token_expired', message: 'expired' },
|
||||
}, 401));
|
||||
const { webSocketFactory } = scriptedSockets([{ closeCode: 1000 }]);
|
||||
const adapter = new WorksSquareDesignWorkspace({
|
||||
apiBaseUrl: 'https://square.example',
|
||||
fetchImpl: fetchMock,
|
||||
webSocketFactory,
|
||||
eventSessionClientIdStore: {
|
||||
getOrCreate: vi.fn().mockResolvedValue('design-stream-current'),
|
||||
rotate,
|
||||
},
|
||||
});
|
||||
|
||||
const stream = await adapter.openWorkspaceEvents({ workspaceId: 'workspace-one' });
|
||||
for await (const _event of stream.events) {
|
||||
// Empty stream.
|
||||
}
|
||||
|
||||
await expect(adapter.closeEventSessions({
|
||||
accessToken: 'captured-terminal-access-token',
|
||||
tolerateRemoteFailure: true,
|
||||
})).resolves.toBeUndefined();
|
||||
const deleteCall = fetchMock.mock.calls.find(([, init]) => init?.method === 'DELETE');
|
||||
expect(deleteCall?.[1]?.headers).toMatchObject({
|
||||
Authorization: 'Bearer captured-terminal-access-token',
|
||||
});
|
||||
expect(rotate).toHaveBeenCalledWith('workspace-one');
|
||||
});
|
||||
|
||||
it('reuses a stable Session idempotency key after an unclean application restart', async () => {
|
||||
|
||||
Reference in New Issue
Block a user