- Updated all package.json name fields and dependency references - Updated all TypeScript/JavaScript imports - Updated .woodpecker/publish.yml filters and registry paths - Updated tools/install.sh scope default - Updated .npmrc registry paths (worktree + host) - Enhanced update-checker.ts with checkForAllUpdates() multi-package support - Updated CLI update command to show table of all packages - Added KNOWN_PACKAGES, formatAllPackagesTable, getInstallAllCommand - Marked checkForUpdate() with @deprecated JSDoc Closes #391
113 lines
3.8 KiB
TypeScript
113 lines
3.8 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { Logger } from '@nestjs/common';
|
|
import type { QueueHandle } from '@mosaicstack/queue';
|
|
import type { LogService } from '@mosaicstack/log';
|
|
import { SessionGCService } from './session-gc.service.js';
|
|
|
|
type MockRedis = {
|
|
scan: ReturnType<typeof vi.fn>;
|
|
del: ReturnType<typeof vi.fn>;
|
|
};
|
|
|
|
describe('SessionGCService', () => {
|
|
let service: SessionGCService;
|
|
let mockRedis: MockRedis;
|
|
let mockLogService: { logs: { promoteToWarm: ReturnType<typeof vi.fn> } };
|
|
|
|
/**
|
|
* Helper: build a scan mock that returns all provided keys in a single
|
|
* cursor iteration (cursor '0' in → ['0', keys] out).
|
|
*/
|
|
function makeScanMock(keys: string[]): ReturnType<typeof vi.fn> {
|
|
return vi.fn().mockResolvedValue(['0', keys]);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
mockRedis = {
|
|
scan: makeScanMock([]),
|
|
del: vi.fn().mockResolvedValue(0),
|
|
};
|
|
|
|
mockLogService = {
|
|
logs: {
|
|
promoteToWarm: vi.fn().mockResolvedValue(0),
|
|
},
|
|
};
|
|
|
|
// Suppress logger output in tests
|
|
vi.spyOn(Logger.prototype, 'log').mockImplementation(() => {});
|
|
|
|
service = new SessionGCService(
|
|
mockRedis as unknown as QueueHandle['redis'],
|
|
mockLogService as unknown as LogService,
|
|
);
|
|
});
|
|
|
|
it('collect() deletes Valkey keys for session', async () => {
|
|
mockRedis.scan = makeScanMock(['mosaic:session:abc:system', 'mosaic:session:abc:foo']);
|
|
const result = await service.collect('abc');
|
|
expect(mockRedis.del).toHaveBeenCalledWith(
|
|
'mosaic:session:abc:system',
|
|
'mosaic:session:abc:foo',
|
|
);
|
|
expect(result.cleaned.valkeyKeys).toBe(2);
|
|
});
|
|
|
|
it('collect() with no keys returns empty cleaned valkeyKeys', async () => {
|
|
mockRedis.scan = makeScanMock([]);
|
|
const result = await service.collect('abc');
|
|
expect(result.cleaned.valkeyKeys).toBeUndefined();
|
|
});
|
|
|
|
it('collect() returns sessionId in result', async () => {
|
|
const result = await service.collect('test-session-id');
|
|
expect(result.sessionId).toBe('test-session-id');
|
|
});
|
|
|
|
it('fullCollect() deletes all session keys', async () => {
|
|
mockRedis.scan = makeScanMock(['mosaic:session:abc:system', 'mosaic:session:xyz:foo']);
|
|
const result = await service.fullCollect();
|
|
expect(mockRedis.del).toHaveBeenCalled();
|
|
expect(result.valkeyKeys).toBe(2);
|
|
});
|
|
|
|
it('fullCollect() with no keys returns 0 valkeyKeys', async () => {
|
|
mockRedis.scan = makeScanMock([]);
|
|
const result = await service.fullCollect();
|
|
expect(result.valkeyKeys).toBe(0);
|
|
expect(mockRedis.del).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('fullCollect() returns duration', async () => {
|
|
const result = await service.fullCollect();
|
|
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('sweepOrphans() extracts unique session IDs and collects them', async () => {
|
|
// First scan call returns the global session list; subsequent calls return
|
|
// per-session keys during collect().
|
|
mockRedis.scan = vi
|
|
.fn()
|
|
.mockResolvedValueOnce([
|
|
'0',
|
|
['mosaic:session:abc:system', 'mosaic:session:abc:messages', 'mosaic:session:xyz:system'],
|
|
])
|
|
// collect('abc') scan
|
|
.mockResolvedValueOnce(['0', ['mosaic:session:abc:system', 'mosaic:session:abc:messages']])
|
|
// collect('xyz') scan
|
|
.mockResolvedValueOnce(['0', ['mosaic:session:xyz:system']]);
|
|
mockRedis.del.mockResolvedValue(1);
|
|
|
|
const result = await service.sweepOrphans();
|
|
expect(result.orphanedSessions).toBeGreaterThanOrEqual(0);
|
|
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('sweepOrphans() returns empty when no session keys', async () => {
|
|
mockRedis.scan = makeScanMock([]);
|
|
const result = await service.sweepOrphans();
|
|
expect(result.orphanedSessions).toBe(0);
|
|
expect(result.totalCleaned).toHaveLength(0);
|
|
});
|
|
});
|