/**
* Regression: the registry-resolution fd-lock leak (2026-07-13).
*
* The install path (resolveRelease / installViaRegistry) opens a Corestore at
* ~/.config/pgh/registries/<key16> to replicate a publisher's ReleaseRegistry.
* ReleaseRegistry.close() closes ONLY the autobase — NOT the underlying
* Corestore — so tearing down via registry.close() alone leaves the store's
* exclusive fd-lock held. In the always-on, in-process pgh-sidecar autoupdate
* loop that orphaned lock made every subsequent install of the same app fail
* with "File descriptor could not be locked", and the zero-backoff retry loop
* bled the sidecar to 232k fds / 15.8 GB on stortron.
*
* The fix: callers close `store` too. These tests pin the invariant — closing
* the registry alone LEAKS the lock; closing the store RELEASES it.
*/
import test from 'brittle'
import Corestore from 'corestore'
import fs from '#node:fs'
import os from '#node:os'
import path from '#node:path'
import { ReleaseRegistry } from '../lib/release-registry.js'
test('registry.close() alone leaks the Corestore fd-lock (characterizes the bug)', async (t) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pgh-reglock-a-'))
t.teardown(() => { try { fs.rmSync(dir, { recursive: true, force: true }) } catch {} })
const store1 = new Corestore(dir)
const reg1 = new ReleaseRegistry(store1)
await reg1.ready()
// OLD (buggy) teardown: close the registry autobase but NOT the store.
await reg1.close()
// The store still holds the dir's exclusive fd-lock, so a second Corestore on
// the same dir cannot acquire it.
const store2 = new Corestore(dir)
const reg2 = new ReleaseRegistry(store2)
await t.exception(reg2.ready(), /lock/i, 'second open fails while store1 still holds the lock')
try { await reg2.close() } catch {}
try { await store2.close() } catch {}
// Now release the lock the way the fix does.
await store1.close()
})
test('closing the store releases the lock — re-open succeeds (validates the fix)', async (t) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pgh-reglock-b-'))
t.teardown(() => { try { fs.rmSync(dir, { recursive: true, force: true }) } catch {} })
const store1 = new Corestore(dir)
const reg1 = new ReleaseRegistry(store1)
await reg1.ready()
// FIXED teardown: close registry AND store.
await reg1.close()
await store1.close()
// The lock is released, so a fresh Corestore on the same dir opens cleanly —
// exactly what the sidecar autoupdate needs on its next attempt.
const store2 = new Corestore(dir)
const reg2 = new ReleaseRegistry(store2)
await reg2.ready()
t.pass('re-open after store.close() succeeds')
await reg2.close()
await store2.close()
})