OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / org-join-direct-clone.test.js

81 lines · javascript ref:
// Regression guard for the org-join content-sync fix (diagnosed 2026-07-17).
//
// SYMPTOM (before the fix): after `pgh org join`, a joiner got the repo's REFS
// (the per-repo tracker autobase replicates) but ZERO objects — git-repos/
// <name>.git was `git init --bare`'d and never populated (confirmed on two
// joiners, ashatron + celltron). The git OBJECTS ride a SEPARATE punch:// PACK
// clone (hyper-sync RepoPuller), and that only ever booted for a device-join
// MIRROR — never for an org join.
//
// FIX: mountOrgGroupedRepos now runs the #16 bidirectional loop over the ORG
// autobase — it ANNOUNCES this daemon's origin coords (writers) and boots a
// SCOPED reverse RepoPuller toward each co-member that announced coords. That
// reverse puller does the git PACK clone (`git fetch punch://<peer>/<repo>`),
// filtered to this org's repos (#35 isolation). This test pins that fan-out.

import test from 'brittle'
import { mountOrgGroupedRepos } from '../lib/services/org-grouped-repos.js'

test('org-join mounts trackers AND boots a reverse puller per announced co-member', async (t) => {
  const org = {
    name: 'house',
    resources: ['electrical-review', 'other_accounting'],
    resourceKeys: {
      'electrical-review': 'a'.repeat(64),
      'other_accounting': 'b'.repeat(64)
    }
  }
  const ensured = []
  const announced = []
  const pullers = []
  // Two co-members that announced their origin coords into the org autobase.
  const peerCoords = [
    { serverKey: 'c'.repeat(64), repoEventLog: 'd'.repeat(64) },
    { serverKey: 'e'.repeat(64), repoEventLog: 'f'.repeat(64) }
  ]
  const deps = {
    getOrg: async () => org,
    activateRoom: () => {},
    refReconcilers: { ensureFor: (n) => { ensured.push(n); return Promise.resolve() } },
    issueRegistry: {
      bindRepoBootstrapKey: () => {},
      getForRepo: async (n) => ({ discoveryKey: 'dk:' + n })
    },
    // The three deps that carry the git-OBJECT clone (wired daemon-side in
    // handlers/orgs.js; injected as spies here).
    announceCoords: async (name) => { announced.push(name) },
    listPeerCoords: async () => peerCoords,
    bootReversePuller: (serverKey, repoEventLog) => { pullers.push({ serverKey, repoEventLog }) }
  }

  const mounted = await mountOrgGroupedRepos('house', deps)

  // Tracker/refs path unchanged.
  t.alike([...mounted].sort(), ['electrical-review', 'other_accounting'], 'both resource repos mounted')
  t.alike(ensured.sort(), ['electrical-review', 'other_accounting'], 'gip/RefReconciler path ran (refs)')

  // The daemon announces its own coords so co-members can pull FROM it.
  t.alike(announced, ['house'], 'announced our origin coords into the org autobase')

  // A reverse puller boots toward EACH announced co-member — the git-OBJECT
  // clone (punch:// PACK) that was missing for org joins.
  t.is(pullers.length, 2, 'booted a reverse puller per co-member')
  t.alike(pullers.map((p) => p.serverKey).sort(), ['c'.repeat(64), 'e'.repeat(64)], 'toward both co-members')
  t.is(pullers[0].repoEventLog, 'd'.repeat(64), 'puller carries the peer repoEventLog key')
})

// A name-only org (no announced peers, no coords deps) must still work — the
// announce/puller halves are all optional and no-op when their deps are absent.
test('org-join with no peer-coord deps still mounts trackers (backward compatible)', async (t) => {
  const org = { name: 'solo', resources: ['r1'], resourceKeys: { r1: 'a'.repeat(64) } }
  const ensured = []
  const mounted = await mountOrgGroupedRepos('solo', {
    getOrg: async () => org,
    activateRoom: () => {},
    refReconcilers: { ensureFor: (n) => { ensured.push(n); return Promise.resolve() } },
    issueRegistry: { bindRepoBootstrapKey: () => {}, getForRepo: async (n) => ({ discoveryKey: 'dk:' + n }) }
  })
  t.alike([...mounted], ['r1'], 'repo mounted without any coord deps')
  t.alike(ensured, ['r1'], 'refs path ran')
})