/ tests / cron / cleanup-test-dbs.test.js
cleanup-test-dbs.test.js
  1  /**
  2   * Tests for cleanup-test-dbs cron job
  3   *
  4   * Tests runCleanupTestDbs() using real /tmp filesystem.
  5   * Verifies it deletes stale test DB files matching known patterns.
  6   */
  7  
  8  import { test, describe, before, after } from 'node:test';
  9  import assert from 'node:assert/strict';
 10  import { mkdirSync, writeFileSync, existsSync, rmSync, utimesSync } from 'fs';
 11  import { join } from 'path';
 12  
 13  import { runCleanupTestDbs } from '../../src/cron/cleanup-test-dbs.js';
 14  
 15  // ─── Helpers ────────────────────────────────────────────────────────────────
 16  
 17  const TEST_TMP_DIR = `/tmp/test-cleanup-cron-${process.pid}`;
 18  
 19  /**
 20   * Create a fake .db file directly in /tmp (where runCleanupTestDbs scans)
 21   */
 22  function createDb(filename, ageMins = 120) {
 23    const p = join('/tmp', filename);
 24    writeFileSync(p, 'x'.repeat(1024));
 25    // Set mtime in the past
 26    const mtime = new Date(Date.now() - ageMins * 60 * 1000);
 27    utimesSync(p, mtime, mtime);
 28    return p;
 29  }
 30  
 31  // ─── Setup ───────────────────────────────────────────────────────────────────
 32  
 33  before(() => {
 34    mkdirSync(TEST_TMP_DIR, { recursive: true });
 35  });
 36  
 37  after(() => {
 38    rmSync(TEST_TMP_DIR, { recursive: true, force: true });
 39  });
 40  
 41  // ─── Tests ───────────────────────────────────────────────────────────────────
 42  
 43  describe('runCleanupTestDbs', () => {
 44    test('returns zero stats when no test DBs exist', () => {
 45      const result = runCleanupTestDbs();
 46      assert.ok(typeof result.deleted === 'number');
 47      assert.ok(typeof result.freed_kb === 'number');
 48    });
 49  
 50    test('returns correct shape', () => {
 51      const result = runCleanupTestDbs();
 52      assert.ok('deleted' in result, 'should have deleted field');
 53      assert.ok('freed_kb' in result, 'should have freed_kb field');
 54    });
 55  
 56    test('isTestDb: matches known test DB patterns', () => {
 57      // These files have the right patterns and are old enough (2h+)
 58      // We create them in /tmp which IS scanned by runCleanupTestDbs
 59      const files = [
 60        createDb('test-monitor-12345.db', 180),
 61        createDb('test-qa-98765.db', 180),
 62        createDb('test-runner-coverage.db', 180),
 63        createDb('test-architect-abc.db', 180),
 64        createDb('test-developer-xyz.db', 180),
 65        createDb('test-triage-foo.db', 180),
 66        createDb('test-security-bar.db', 180),
 67        createDb('test-sites-2024.db', 180),
 68      ];
 69  
 70      // These files should be deleted
 71      const result = runCleanupTestDbs();
 72      assert.ok(
 73        result.deleted >= files.length,
 74        `Expected at least ${files.length} deleted, got ${result.deleted}`
 75      );
 76      assert.ok(result.freed_kb >= 0, 'freed_kb should be non-negative');
 77  
 78      // Verify files are actually gone
 79      for (const f of files) {
 80        assert.ok(!existsSync(f), `Expected ${f} to be deleted`);
 81      }
 82    });
 83  
 84    test('ignores non-test DB files', () => {
 85      // Create non-matching files in /tmp
 86      const nonTest = join('/tmp', `not-a-test-db-${process.pid}.db`);
 87      writeFileSync(nonTest, 'data');
 88      const mtime = new Date(Date.now() - 200 * 60 * 1000);
 89      utimesSync(nonTest, mtime, mtime);
 90  
 91      const resultBefore = runCleanupTestDbs();
 92      // File should still exist since it doesn't match test DB patterns
 93      assert.ok(existsSync(nonTest), 'Non-test DB should not be deleted');
 94  
 95      // Cleanup manually
 96      rmSync(nonTest, { force: true });
 97    });
 98  
 99    test('ignores files younger than 1 hour', () => {
100      // Create a fresh test DB (5 minutes old) — should NOT be deleted
101      const fresh = join('/tmp', `test-sites-fresh-${process.pid}-${Date.now()}.db`);
102      writeFileSync(fresh, 'data');
103      const mtime = new Date(Date.now() - 5 * 60 * 1000); // 5 min old
104      utimesSync(fresh, mtime, mtime);
105  
106      runCleanupTestDbs();
107  
108      assert.ok(existsSync(fresh), 'Fresh test DB should NOT be deleted (< 1 hour old)');
109  
110      // Cleanup
111      rmSync(fresh, { force: true });
112    });
113  
114    test('handles non-existent directories gracefully', () => {
115      // /tmp always exists but tests/ dir may or may not have test DBs
116      // runCleanupTestDbs scans tests/, tests/agents/, /tmp — should never throw
117      assert.doesNotThrow(() => runCleanupTestDbs());
118    });
119  });