Repository navigation
Expand file tree
/
Copy pathcommit_fallback_test.go
More file actions
124 lines (107 loc) · 4.43 KB
/
Copy pathcommit_fallback_test.go
File metadata and controls
124 lines (107 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// commit_fallback_test.go tests the graceful fallback behaviour when a
// repository's HEAD or branch refs point to objects that do not exist in any
// pack file. The scanner should return zero commits rather than an error,
// allowing callers to handle repositories with dangling references.
package objstore
import (
"os"
"path/filepath"
"runtime"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestLoadAllCommits_SkipsMissingRefObjects simulates a repository whose HEAD
// resolves to refs/heads/main, which in turn contains a 40-hex OID that does
// not correspond to any object in the pack directory. The expected behaviour
// is that loadAllCommits returns an empty commit slice (not an error), because
// missing ref targets are silently skipped during the commit walk.
func TestLoadAllCommits_SkipsMissingRefObjects(t *testing.T) {
gitDir := t.TempDir()
for _, rel := range []string{
filepath.Join("objects", "pack"),
filepath.Join("refs", "heads"),
} {
if err := os.MkdirAll(filepath.Join(gitDir, rel), 0o755); err != nil {
t.Fatalf("mkdir %s: %v", rel, err)
}
}
const missingOID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
if err := os.WriteFile(filepath.Join(gitDir, "HEAD"), []byte("ref: refs/heads/main\n"), 0o644); err != nil {
t.Fatalf("write HEAD: %v", err)
}
if err := os.WriteFile(filepath.Join(gitDir, "refs", "heads", "main"), []byte(missingOID+"\n"), 0o644); err != nil {
t.Fatalf("write refs/heads/main: %v", err)
}
scanner, err := NewHistoryScanner(gitDir)
if err != nil {
t.Fatalf("NewHistoryScanner: %v", err)
}
defer scanner.Close()
commits, err := scanner.loadAllCommits()
if err != nil {
t.Fatalf("LoadAllCommits: %v", err)
}
if len(commits) != 0 {
t.Fatalf("expected zero commits, got %d", len(commits))
}
}
// TestReadRefHash_PropagatesIOErrors verifies that readRefHash returns an error
// (not just false) when the ref file cannot be read due to a permission error.
// Before the fix, all errors were swallowed as "not found".
func TestReadRefHash_PropagatesIOErrors(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("permission-based test not reliable on Windows")
}
gitDir := t.TempDir()
refsDir := filepath.Join(gitDir, "refs", "heads")
require.NoError(t, os.MkdirAll(refsDir, 0o755))
refPath := filepath.Join(refsDir, "main")
require.NoError(t, os.WriteFile(refPath, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"), 0o000))
// Before the fix: readRefHash returns (Hash{}, false, nil), swallowing
// the permission error. After the fix: it returns a non-nil error.
_, _, err := readRefHash(gitDir, "refs/heads/main")
assert.Error(t, err, "permission errors should be propagated, not swallowed")
}
// TestReadRefHash_ChecksScannerError verifies that readRefHash checks sc.Err()
// after the scanner loop, so I/O errors from packed-refs are reported.
func TestReadRefHash_ChecksScannerError(t *testing.T) {
gitDir := t.TempDir()
// Create a packed-refs file with valid content. readRefHash should
// return (Hash{}, false, nil) when the ref is simply not in packed-refs.
packedRefs := "# pack-refs with: peeled fully-peeled sorted\n" +
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb refs/heads/other\n"
require.NoError(t, os.WriteFile(filepath.Join(gitDir, "packed-refs"), []byte(packedRefs), 0o644))
_, ok, err := readRefHash(gitDir, "refs/heads/nonexistent")
require.NoError(t, err)
assert.False(t, ok)
}
// TestWalkCommitsFromRefsOrdered_VisitsSeriallyInStableOrder pins the contract
// the scan planners depend on: a single worker never calls visit concurrently,
// and the visit order is reproducible, so first-wins blob dedup attributes each
// blob to the same commit on every run.
func TestWalkCommitsFromRefsOrdered_VisitsSeriallyInStableOrder(t *testing.T) {
scanner := createScannerForRepo(t, "with-merges")
defer scanner.Close()
collect := func() []Hash {
var (
inVisit atomic.Bool
overlapped atomic.Bool
order []Hash
)
require.NoError(t, scanner.walkCommitsFromRefsOrdered(func(c commitInfo) error {
if !inVisit.CompareAndSwap(false, true) {
overlapped.Store(true)
}
order = append(order, c.OID)
inVisit.Store(false)
return nil
}))
require.False(t, overlapped.Load(), "the ordered walk invoked visit concurrently")
return order
}
first := collect()
require.Len(t, first, 5, "with-merges holds five commits")
require.Equal(t, first, collect(), "ordered visit order must be reproducible")
}