OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / retired-not-resurrected.test.js

98 lines · javascript ref:
// A retired repo must not be re-created by the tracker-open path.
//
// services/retired-repos.js lists four resurrection paths. Path 4 is "residual
// references: issue-registry-index.json, tracker autobases" — and it was the
// one still open. RefReconcilerManager.ensureFor() opens a tracker via
// getForRepo() and runs `git init --bare` when the directory is missing, with
// no tombstone check. reconcileAllRepoRefs already gated its readdir on
// isRetired; this path did not.
//
// Observed on the live fleet: after retiring 16 repos and removing their bytes,
// a daemon restart re-created exactly the two that had real tracker autobases
// (nvidia-rtx-godot, pear-git-v2) as empty 124K skeletons — still tombstoned,
// still listed as retired, and back on disk regardless. The other 14 had no
// tracker to open and stayed gone.

import test from 'brittle'
import { mkdtempSync, existsSync, rmSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'

import { RefReconcilerManager } from '../lib/services/ref-reconciler-manager.js'

const tmp = (p) => mkdtempSync(join(tmpdir(), p))

// Minimal stand-ins: enough for `enabled` to be true and for ensureFor to
// reach its `git init --bare`.
function makeManager ({ retired = [] } = {}) {
  const reposDir = tmp('pgv3-resurrect-')
  const opened = []
  const mgr = new RefReconcilerManager({
    issueRegistry: {
      async getForRepo (name) {
        opened.push(name)
        return { writable: false, base: { on () {}, off () {} }, async listRefs () { return [] } }
      }
    },
    overlay: {},
    reposDir,
    isRetired: (name) => retired.includes(name)
  })
  return { mgr, reposDir, opened }
}

test('ensureFor refuses a RETIRED repo and creates nothing', async (t) => {
  const { mgr, reposDir, opened } = makeManager({ retired: ['pear-git-v2'] })

  const rec = await mgr.ensureFor('pear-git-v2')

  t.is(rec, null, 'no reconciler started for a tombstoned repo')
  t.absent(existsSync(join(reposDir, 'pear-git-v2.git')), 'no bare repo was re-created')
  t.absent(opened.includes('pear-git-v2'), 'its tracker was never even opened')
})

test('a repo that is NOT retired still works', async (t) => {
  const { mgr, reposDir, opened } = makeManager({ retired: ['pear-git-v2'] })

  const rec = await mgr.ensureFor('live-repo')

  t.ok(rec, 'reconciler started for a live repo')
  t.ok(existsSync(join(reposDir, 'live-repo.git')), 'its bare repo was materialized as before')
  t.ok(opened.includes('live-repo'), 'and its tracker was opened')
})

test('un-retiring restores normal behaviour', async (t) => {
  // The tombstone is reversible, so the gate must be too — it reads the live
  // set on every call rather than caching a decision.
  const retired = ['pear-git-v2']
  const reposDir = tmp('pgv3-resurrect-')
  const mgr = new RefReconcilerManager({
    issueRegistry: {
      async getForRepo () { return { writable: false, base: { on () {}, off () {} }, async listRefs () { return [] } } }
    },
    overlay: {},
    reposDir,
    isRetired: (name) => retired.includes(name)
  })

  t.is(await mgr.ensureFor('pear-git-v2'), null, 'refused while retired')

  retired.length = 0 // unretire
  const rec = await mgr.ensureFor('pear-git-v2')
  t.ok(rec, 'accepted once un-retired')
  t.ok(existsSync(join(reposDir, 'pear-git-v2.git')), 'and materialized then')
})

test('with no isRetired supplied, nothing is gated (back-compat)', async (t) => {
  const reposDir = tmp('pgv3-resurrect-')
  const mgr = new RefReconcilerManager({
    issueRegistry: {
      async getForRepo () { return { writable: false, base: { on () {}, off () {} }, async listRefs () { return [] } } }
    },
    overlay: {},
    reposDir
  })
  t.ok(await mgr.ensureFor('anything'), 'default predicate retires nothing')
  rmSync(reposDir, { recursive: true, force: true })
})