OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / mirror-pull-refresh.test.js

113 lines · javascript ref:
// A mirror's repo listing went stale the moment a pull landed.
//
// RepoPuller._process fetches into an EXISTING bare repo. Nothing rescans
// afterwards: the repos-dir watcher (services/sync.js) is a non-recursive
// watch(reposDir), so writes INSIDE <repo>.git never wake it, and the
// puller's only hook is onMount → scheduleMirrorReannounce. So
// sync.status() — which feeds list-repos and the /repos ordering — kept
// serving whatever the last full scan saw.
//
// Measured on the live fleet 2026-09-05: stortron (up 3d) reported
// blender-bridge lastUpdated 2026-06-12 while its OWN disk said
// 2026-09-04 15:57 — the refs were 2s behind tabertron's. Four repos were
// buried on one backend and top-of-page on the other for this reason alone.
//
// Fix: refresh THAT repo's record after a successful pull. Per-repo, not a
// full sync.scan() — scanRepos walks every repo synchronously (364ms across
// 147 live repos) and would stall the event loop once per pulled repo.

import test from 'brittle'
import fs from '#node:fs'
import os from '#node:os'
import { join } from '#node:path'
import { RepoPuller } from '../lib/services/repo-mirror.js'
import { attachRepoRefresh } from '../lib/services/sync.js'

function tmpdir (t) {
  const dir = fs.mkdtempSync(join(os.tmpdir(), 'pgv3-refresh-'))
  t.teardown(() => { try { fs.rmSync(dir, { recursive: true, force: true }) } catch {} })
  return dir
}

function bareRepo (dir, name, sha) {
  const p = join(dir, name + '.git')
  fs.mkdirSync(join(p, 'refs', 'heads'), { recursive: true })
  fs.mkdirSync(join(p, 'objects', 'pack'), { recursive: true })
  fs.writeFileSync(join(p, 'HEAD'), 'ref: refs/heads/main\n')
  fs.writeFileSync(join(p, 'refs', 'heads', 'main'), sha + '\n')
  return p
}

test('RepoPuller refreshes the pulled repo, and only on success', async (t) => {
  const ok = []
  const good = new RepoPuller({
    sync: {
      pull: async () => {},
      remotes: new Map(),
      addRemote () {},
      refreshRepo: (name) => ok.push(name)
    },
    issueRegistry: {},
    log: { close: async () => {} },
    primaryServerKey: 'c'.repeat(64)
  })
  await good._process({ name: 'demo', op: 0 }, 0)
  t.alike(ok, ['demo'], 'refreshRepo called with the repo name on success')

  const bad = []
  const fails = new RepoPuller({
    sync: {
      pull: async () => { throw new Error('boom') },
      remotes: new Map(),
      addRemote () {},
      refreshRepo: (name) => bad.push(name)
    },
    issueRegistry: {},
    log: { close: async () => {} },
    primaryServerKey: 'd'.repeat(64)
  })
  await fails._process({ name: 'nope', op: 0 }, 0)
  t.alike(bad, [], 'refreshRepo NOT called when the pull fails')
})

test('RepoPuller survives a sync without refreshRepo (older hyper-sync)', async (t) => {
  const puller = new RepoPuller({
    sync: { pull: async () => {}, remotes: new Map(), addRemote () {} },
    issueRegistry: {},
    log: { close: async () => {} },
    primaryServerKey: 'e'.repeat(64)
  })
  await puller._process({ name: 'demo', op: 0 }, 0)
  t.pass('no throw when the sync server has no refreshRepo')
})

test('attachRepoRefresh re-stats ONE repo without walking the others', async (t) => {
  const dir = tmpdir(t)
  bareRepo(dir, 'alpha', 'a'.repeat(40))
  bareRepo(dir, 'beta', 'b'.repeat(40))

  // Model a daemon whose in-memory map is stale for alpha: an old lastUpdated
  // and no branches, exactly what a post-boot pull leaves behind.
  const stale = { name: 'alpha', path: join(dir, 'alpha.git'), branches: [], branchCount: 0, head: null, packSize: 0, lastUpdated: 1, footprint: 0 }
  const betaEntry = { name: 'beta', path: join(dir, 'beta.git'), branches: [], branchCount: 0, head: null, packSize: 0, lastUpdated: 2, footprint: 0 }
  const sync = { _reposDir: dir, repos: new Map([['alpha', stale], ['beta', betaEntry]]) }

  attachRepoRefresh(sync, dir)
  const updated = sync.refreshRepo('alpha')

  t.ok(updated, 'returns the refreshed record')
  t.is(sync.repos.get('alpha').branchCount, 1, 'alpha picked up its branch')
  t.is(sync.repos.get('alpha').head, 'a'.repeat(40), 'alpha picked up its head')
  t.ok(sync.repos.get('alpha').lastUpdated > 1, 'alpha lastUpdated moved to the real ref mtime')
  t.is(sync.repos.get('beta'), betaEntry, 'beta was left untouched — not a full rescan')
})

test('attachRepoRefresh is a no-op for an unknown or vanished repo', async (t) => {
  const dir = tmpdir(t)
  const sync = { _reposDir: dir, repos: new Map() }
  attachRepoRefresh(sync, dir)
  t.is(sync.refreshRepo('ghost'), null, 'unknown repo returns null')
  t.is(sync.refreshRepo(''), null, 'empty name returns null')
  t.is(sync.repos.size, 0, 'nothing was added to the map')
})