|
1 | 1 | import { execSync } from 'node:child_process'; |
2 | | -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; |
| 2 | +import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; |
3 | 3 | import os from 'node:os'; |
4 | 4 | import path from 'node:path'; |
5 | 5 |
|
@@ -308,6 +308,56 @@ describe('results repo write path', () => { |
308 | 308 | rmSync(rootDir, { recursive: true, force: true }); |
309 | 309 | }); |
310 | 310 |
|
| 311 | + it('retries an interrupted direct push without dropping the committed run', async () => { |
| 312 | + const { remoteDir } = initializeRemoteRepo(rootDir); |
| 313 | + const cloneDir = path.join(rootDir, 'results-clone'); |
| 314 | + const sourceDir = path.join(rootDir, 'source-run'); |
| 315 | + const runTimestamp = '2026-05-22T11-00-00-000Z'; |
| 316 | + const destinationPath = path.join('retry', runTimestamp); |
| 317 | + const config = createResultsConfig(remoteDir, cloneDir); |
| 318 | + const hookPath = path.join(remoteDir, 'hooks', 'pre-receive'); |
| 319 | + writeRunArtifacts(sourceDir, 'retry', '2026-05-22T11:00:00.000Z'); |
| 320 | + |
| 321 | + await ensureResultsRepoClone(config); |
| 322 | + git('git config user.email "test@example.com"', cloneDir); |
| 323 | + git('git config user.name "Test User"', cloneDir); |
| 324 | + |
| 325 | + writeFileSync(hookPath, '#!/usr/bin/env sh\necho "simulated interrupted push" >&2\nexit 1\n'); |
| 326 | + chmodSync(hookPath, 0o755); |
| 327 | + |
| 328 | + await expect( |
| 329 | + directPushResults({ |
| 330 | + config, |
| 331 | + sourceDir, |
| 332 | + destinationPath, |
| 333 | + commitMessage: 'feat(results): retry - 1/1 PASS (1.000)', |
| 334 | + }), |
| 335 | + ).rejects.toThrow(/simulated interrupted push/); |
| 336 | + expect(git('git rev-list --count origin/main..HEAD', cloneDir)).toBe('1'); |
| 337 | + expect(git(`git --git-dir "${remoteDir}" ls-tree -r --name-only main`, rootDir)).not.toContain( |
| 338 | + `.agentv/results/runs/retry/${runTimestamp}/benchmark.json`, |
| 339 | + ); |
| 340 | + |
| 341 | + rmSync(hookPath, { force: true }); |
| 342 | + |
| 343 | + await expect( |
| 344 | + directPushResults({ |
| 345 | + config, |
| 346 | + sourceDir, |
| 347 | + destinationPath, |
| 348 | + commitMessage: 'feat(results): retry - 1/1 PASS (1.000)', |
| 349 | + }), |
| 350 | + ).resolves.toBe(true); |
| 351 | + |
| 352 | + expect(git('git rev-list --count origin/main..HEAD', cloneDir)).toBe('0'); |
| 353 | + expect(git(`git --git-dir "${remoteDir}" ls-tree -r --name-only main`, rootDir)).toContain( |
| 354 | + `.agentv/results/runs/retry/${runTimestamp}/benchmark.json`, |
| 355 | + ); |
| 356 | + expect(git(`git --git-dir "${remoteDir}" log -1 --pretty=%B main`, rootDir)).toContain( |
| 357 | + `Agentv-Run: retry::${runTimestamp}`, |
| 358 | + ); |
| 359 | + }, 20000); |
| 360 | + |
311 | 361 | it('commits pushed runs into the configured clone with an Agentv-Run trailer', async () => { |
312 | 362 | const { remoteDir } = initializeRemoteRepo(rootDir); |
313 | 363 | const cloneDir = path.join(rootDir, 'results-clone'); |
|
0 commit comments