Add failing tests for HEAD propagation: source/target HEAD in Result + ProbeResult · Entire
Add failing tests for HEAD propagation: source/target HEAD in Result + ProbeResult
7d1c3b8·
Soph·2mo ago·1 file·+103 added/-0 removed
issue #45: git-sync mirrors refs but never propagates the source's HEAD
symref target to the target. A repo mirrored from a default=main source
into a default=master target ends up with a dangling target HEAD; a
subsequent git clone target checks out master instead of main.
Step 1 of the implementation: pin the public contract via three
failing integration tests that drive the API shape.
- TestRun_IntegrationBootstrapSurfacesHEADInResult: source default
master, target init'd with --default-branch=main, asserts Result
exposes both HEAD targets as plumbing.ReferenceName. - TestRun_IntegrationBootstrapHEADMatch: same default on both sides,
asserts SourceHEAD == TargetHEAD so callers can detect mismatch via
field equality. - TestProbe_IntegrationSurfacesBothHEADs: probe surfaces the same info
without mutating target, useful for previewing the mismatch.
The tests fail to build today (Result and ProbeResult don't have the
SourceHEAD/TargetHEAD fields yet); subsequent commits add the fields
and the capture logic.
Refs #45.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Sessions
ce76aab97a1fView transcript
Changes
1
internal/syncer
Mintegration_test.go+103
1849 unmodified lines
... // ProbeResult.SourceHEAD and TargetHEAD let `git-sync probe` preview the
// default-branch mismatch without actually mutating the target.
func TestProbe_IntegrationSurfacesBothHEADs(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 1)
targetRepo, err := git.Init(memory.NewStorage(), nil, git.WithDefaultBranch("refs/heads/main"))
if err != nil {
t.Fatalf("init target repo: %v", err)
}
sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
targetServer := newSmartHTTPRepoServer(t, targetRepo)
defer sourceServer.Close()
defer targetServer.Close()
result, err := Probe(context.Background(), Config{
Source: Endpoint{URL: sourceServer.RepoURL()},
Target: Endpoint{URL: targetServer.RepoURL()},
ProtocolMode: protocolModeAuto,
})
if err != nil {
t.Fatalf("probe: %v", err)
}
wantSource := plumbing.NewBranchReferenceName(testBranch)
if result.SourceHEAD != wantSource {
t.Errorf("SourceHEAD = %q, want %q", result.SourceHEAD, wantSource)
}
wantTarget := plumbing.ReferenceName("refs/heads/main")
if result.TargetHEAD != wantTarget {
t.Errorf("TargetHEAD = %q, want %q", result.TargetHEAD, wantTarget)
}
}
func TestFetch_IntegrationProtocolV2Source(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 4)
...
}
// Result.SourceHEAD and Result.TargetHEAD capture each side's symref HEAD
// target, so library callers can detect a default-branch mismatch without
// scraping the notice stream. Bootstrap into an empty target whose bare-repo
// HEAD default differs from the source's surfaces this case.
func TestRun_IntegrationBootstrapSurfacesHEADInResult(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 1)
// go-git Init defaults HEAD to refs/heads/master; pin target to
// refs/heads/main so the symrefs diverge.
targetRepo, err := git.Init(memory.NewStorage(), nil, git.WithDefaultBranch("refs/heads/main"))
if err != nil {
t.Fatalf("init target repo: %v", err)
}
result, err := Run(context.Background(), Config{
Source: Endpoint{URL: sourceServer.RepoURL()},
Target: Endpoint{URL: targetServer.RepoURL()},
ProtocolMode: protocolModeAuto,
})
if err != nil {
t.Fatalf("sync: %v", err)
}
wantSourceHEAD := plumbing.NewBranchReferenceName(testBranch)
if result.SourceHEAD != wantSourceHEAD {
t.Errorf("SourceHEAD = %q, want %q", result.SourceHEAD, wantSourceHEAD)
}
wantTargetHEAD := plumbing.ReferenceName("refs/heads/main")
if result.TargetHEAD != wantTargetHEAD {
t.Errorf("TargetHEAD = %q, want %q", result.TargetHEAD, wantTargetHEAD)
}
}
// When source HEAD and target HEAD match, the sync surfaces both fields
// identically — no mismatch from the consumer's perspective.
func TestRun_IntegrationBootstrapHEADMatch(t *testing.T) {
...
}
// --exclude-ref-prefix trims namespaces from --all-refs auto-discovery. The
// GitHub use case is `--all-refs --exclude-ref-prefix refs/pull/`: mirror
// branches/tags/notes but skip the fork-commit blowup from PR refs.