OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git commits / bc2aa81

Add P2P fetch size test — diagnose large repo transfer failures
Tests 6 scenarios from tiny to large: 1-2: ls-remote (just refs, ~1KB) 3: shallow clone small repo (hyper-overlay ~123KB) 4: delta fetch small repo (already have it, new commits only) 5: delta fetch large repo (hyper-tps, should be tiny delta) 6: full shallow clone large repo (~1.4GB, may timeout) Test 5 is the key: if delta fetch works, the issue is full clone size, not P2P transport. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bc2aa81d98a32eb0bc9dc4f339c25542eaea5216
by plankatron <fleeky@gmail.com> · 2026-04-07T14:38:30.000Z

Changed files (1) +160 -0

test/p2p-fetch-size.js +160 -0
……@@ -0,0 +1,160 @@
1#!/usr/bin/env bare
2/**
3 * P2P Fetch Size Test
4 *
5 * Tests whether git fetch over punch:// works for:
6 * 1. Small repos (hyper-overlay ~123KB)
7 * 2. Large repos (hyper-tps ~1.4GB)
8 * 3. Delta-only fetch (already have most data, fetch 1 new commit)
9 *
10 * Run: bare test/p2p-fetch-size.js [server-key]
11 */
12
13import { spawn } from 'bare-subprocess'
14import os from 'bare-os'
15import process from 'bare-process'
16import fs from 'bare-fs'
17import path from 'bare-path'
18
19const SERVER_KEY = process.argv[2] || 'f580547b1fdba52331b0ed9483fc2cbe50fb3fb3cc76c904b580b7b7ee6f3d15'
20const TIMEOUT = 120000
21
22let passed = 0
23let failed = 0
24
25function ok (name, detail) { passed++; console.log(` ✓ ${name}${detail ? ' — ' + detail : ''}`) }
26function fail (name, reason) { failed++; console.log(` ✗ ${name}: ${reason}`) }
27
28function gitCmd (args, opts = {}) {
29 return new Promise((resolve, reject) => {
30 const timeout = opts.timeout || TIMEOUT
31 let done = false
32 const child = spawn('git', args, { stdio: ['ignore', 'pipe', 'pipe'] })
33
34 const timer = setTimeout(() => {
35 if (done) return
36 done = true
37 try { child.kill('SIGKILL') } catch {}
38 reject(new Error(`timeout after ${timeout / 1000}s`))
39 }, timeout)
40
41 let stdout = ''
42 let stderr = ''
43 child.stdout.on('data', (d) => { stdout += d.toString() })
44 child.stderr.on('data', (d) => { stderr += d.toString() })
45 child.on('exit', (code) => {
46 if (done) return
47 done = true
48 clearTimeout(timer)
49 if (code === 0) resolve({ stdout: stdout.trim(), stderr: stderr.trim() })
50 else reject(new Error(`exit ${code}: ${stderr.trim().split('\n').pop()}`))
51 })
52 child.on('error', (err) => {
53 if (done) return
54 done = true
55 clearTimeout(timer)
56 reject(err)
57 })
58 })
59}
60
61console.log('P2P Fetch Size Tests')
62console.log('====================')
63console.log(`Host: ${os.hostname()}`)
64console.log(`Server: ${SERVER_KEY.slice(0, 8)}...`)
65console.log('')
66
67// Test 1: ls-remote (tiny, just refs)
68console.log('1. ls-remote (ref list only, ~1KB)')
69try {
70 const url = `punch://${SERVER_KEY}/hyper-overlay`
71 const { stdout } = await gitCmd(['ls-remote', url, 'refs/heads/release'], { timeout: 30000 })
72 const ref = stdout.split('\t')[0]
73 ok('hyper-overlay ls-remote', ref.slice(0, 12) + '...')
74} catch (err) {
75 fail('hyper-overlay ls-remote', err.message)
76}
77
78// Test 2: ls-remote for hyper-tps
79console.log('2. ls-remote hyper-tps (same, just refs)')
80try {
81 const url = `punch://${SERVER_KEY}/hyper-tps`
82 const { stdout } = await gitCmd(['ls-remote', url, 'refs/heads/release'], { timeout: 30000 })
83 const ref = stdout.split('\t')[0]
84 ok('hyper-tps ls-remote', ref.slice(0, 12) + '...')
85} catch (err) {
86 fail('hyper-tps ls-remote', err.message)
87}
88
89// Test 3: shallow clone small repo
90console.log('3. Shallow clone hyper-overlay (small repo, ~123KB)')
91const smallDir = path.join(os.tmpdir(), 'p2p-test-small-' + Date.now())
92try {
93 const url = `punch://${SERVER_KEY}/hyper-overlay`
94 await gitCmd(['clone', '--depth', '1', '-b', 'release', url, smallDir], { timeout: 60000 })
95 // Check size
96 const { stdout } = await gitCmd(['-C', smallDir, 'count-objects', '-vH'])
97 const sizeMatch = stdout.match(/size-pack: (.+)/)
98 ok('hyper-overlay clone', sizeMatch ? 'pack size: ' + sizeMatch[1] : 'done')
99} catch (err) {
100 fail('hyper-overlay clone', err.message)
101} finally {
102 try { fs.rmSync(smallDir, { recursive: true }) } catch {}
103}
104
105// Test 4: Delta fetch — fetch into existing repo (should be tiny)
106console.log('4. Delta fetch hyper-overlay (already have repo, fetch new commits only)')
107try {
108 const repoPath = path.join(os.homedir(), 'git-repos', 'hyper-overlay.git')
109 await fs.promises.stat(repoPath)
110 const url = `punch://${SERVER_KEY}/hyper-overlay`
111 const t0 = Date.now()
112 const { stderr } = await gitCmd(['-C', repoPath, 'fetch', url, 'release'], { timeout: 30000 })
113 const elapsed = Date.now() - t0
114 ok('hyper-overlay delta fetch', `${elapsed}ms` + (stderr ? ' — ' + stderr.split('\n').pop() : ''))
115} catch (err) {
116 if (err.message.includes('ENOENT')) {
117 fail('hyper-overlay delta fetch', 'local bare repo not found')
118 } else {
119 fail('hyper-overlay delta fetch', err.message)
120 }
121}
122
123// Test 5: Delta fetch hyper-tps (should also be small delta if already have most data)
124console.log('5. Delta fetch hyper-tps (existing repo, delta only)')
125try {
126 const repoPath = path.join(os.homedir(), 'git-repos', 'hyper-tps.git')
127 await fs.promises.stat(repoPath)
128 const url = `punch://${SERVER_KEY}/hyper-tps`
129 const t0 = Date.now()
130 const { stderr } = await gitCmd(['-C', repoPath, 'fetch', url, 'release'], { timeout: 60000 })
131 const elapsed = Date.now() - t0
132 ok('hyper-tps delta fetch', `${elapsed}ms` + (stderr ? ' — ' + stderr.split('\n').pop() : ''))
133} catch (err) {
134 if (err.message.includes('ENOENT')) {
135 fail('hyper-tps delta fetch', 'local bare repo not found')
136 } else {
137 fail('hyper-tps delta fetch', err.message)
138 }
139}
140
141// Test 6: Full shallow clone hyper-tps (the big one)
142console.log('6. Shallow clone hyper-tps (large repo, ~1.4GB — may timeout)')
143const bigDir = path.join(os.tmpdir(), 'p2p-test-big-' + Date.now())
144try {
145 const url = `punch://${SERVER_KEY}/hyper-tps`
146 const t0 = Date.now()
147 await gitCmd(['clone', '--depth', '1', '-b', 'release', url, bigDir], { timeout: TIMEOUT })
148 const elapsed = Date.now() - t0
149 const { stdout } = await gitCmd(['-C', bigDir, 'count-objects', '-vH'])
150 const sizeMatch = stdout.match(/size-pack: (.+)/)
151 ok('hyper-tps clone', `${(elapsed/1000).toFixed(1)}s, pack: ${sizeMatch ? sizeMatch[1] : '?'}`)
152} catch (err) {
153 fail('hyper-tps clone', err.message)
154} finally {
155 try { fs.rmSync(bigDir, { recursive: true }) } catch {}
156}
157
158console.log('')
159console.log(`Results: ${passed} passed, ${failed} failed`)
160process.exit(failed > 0 ? 1 : 0)