feat(p10): archivage automatique des sessions terminées (rétention configurable)
- DB: migration #10 (sessions.archived_at + index), helpers archive/unarchive/archiveExpired (soft-archive, jamais de DELETE) - core/retention-settings.ts: session_retention_days (défaut 30, 0=jamais, 1-3650) + session_purge_days (off) - core/session-archive.ts: scheduler start/stop/sweep (calqué DiscoveryService), câblé dans runDaemon - pty-manager.list({includeArchived}) + SessionSummary.archived + emitHistoricalUpdate - routes: GET /sessions?includeArchived, POST/DELETE /sessions/:id/archive, POST /sessions/archive-now - protocole additif: message WS session_archived (relayé par la gateway, abonnés 'sessions') - settings: retentionDays/purgeDays exposés et validés dans PATCH /settings - web: store sessions (showArchived + archive/unarchive), SessionsListView (toggle/badge/actions), SettingsView (slider rétention), i18n EN+FR - tests: retention-settings + session-archive (vitest) + settings-routes étendu ; acceptance-p10.mjs (sweep, event WS, resume d'une session archivée → 201, rétention=0)
This commit is contained in:
63
packages/server/test/retention-settings.test.ts
Normal file
63
packages/server/test/retention-settings.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// Réglages de rétention des sessions (P10) : validateurs + lecture/écriture DB.
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { openDb, setSetting, type Db } from '../src/db/index.js';
|
||||
import {
|
||||
DEFAULT_PURGE_DAYS,
|
||||
DEFAULT_RETENTION_DAYS,
|
||||
PURGE_DAYS_KEY,
|
||||
RETENTION_DAYS_KEY,
|
||||
normalizePurgeDays,
|
||||
normalizeRetentionDays,
|
||||
readPurgeDays,
|
||||
readRetentionDays,
|
||||
} from '../src/core/retention-settings.js';
|
||||
|
||||
let db: Db;
|
||||
beforeEach(() => {
|
||||
db = openDb(':memory:');
|
||||
});
|
||||
afterEach(() => db.close());
|
||||
|
||||
describe('normalizeRetentionDays', () => {
|
||||
it('accepte 0 (= jamais)', () => {
|
||||
expect(normalizeRetentionDays(0)).toBe(0);
|
||||
});
|
||||
it('accepte une valeur dans les bornes', () => {
|
||||
expect(normalizeRetentionDays(30)).toBe(30);
|
||||
expect(normalizeRetentionDays(1)).toBe(1);
|
||||
expect(normalizeRetentionDays(3650)).toBe(3650);
|
||||
});
|
||||
it('rejette hors bornes, non-entier, non-nombre', () => {
|
||||
expect(normalizeRetentionDays(-1)).toBeNull();
|
||||
expect(normalizeRetentionDays(3651)).toBeNull();
|
||||
expect(normalizeRetentionDays(1.5)).toBeNull();
|
||||
expect(normalizeRetentionDays('30')).toBeNull();
|
||||
expect(normalizeRetentionDays(null)).toBeNull();
|
||||
});
|
||||
it('purge partage exactement les mêmes règles', () => {
|
||||
expect(normalizePurgeDays).toBe(normalizeRetentionDays);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readRetentionDays / readPurgeDays', () => {
|
||||
it('défaut quand absent', () => {
|
||||
expect(readRetentionDays(db)).toBe(DEFAULT_RETENTION_DAYS);
|
||||
expect(readPurgeDays(db)).toBe(DEFAULT_PURGE_DAYS);
|
||||
});
|
||||
it('lit une valeur posée', () => {
|
||||
setSetting(db, RETENTION_DAYS_KEY, '7');
|
||||
setSetting(db, PURGE_DAYS_KEY, '90');
|
||||
expect(readRetentionDays(db)).toBe(7);
|
||||
expect(readPurgeDays(db)).toBe(90);
|
||||
});
|
||||
it('0 est une valeur valide (désactivé)', () => {
|
||||
setSetting(db, RETENTION_DAYS_KEY, '0');
|
||||
expect(readRetentionDays(db)).toBe(0);
|
||||
});
|
||||
it('tolère une valeur corrompue → défaut', () => {
|
||||
setSetting(db, RETENTION_DAYS_KEY, 'not-a-number');
|
||||
expect(readRetentionDays(db)).toBe(DEFAULT_RETENTION_DAYS);
|
||||
setSetting(db, RETENTION_DAYS_KEY, '99999');
|
||||
expect(readRetentionDays(db)).toBe(DEFAULT_RETENTION_DAYS);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user