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
|
package memory_test
import (
"errors"
"testing"
"lindenii.org/go/furgit/object/id"
"lindenii.org/go/furgit/ref"
"lindenii.org/go/furgit/ref/store"
"lindenii.org/go/furgit/ref/store/memory"
)
func TestBatchRejectsDuplicateResolvedTargetAndAppliesRemainder(t *testing.T) {
t.Parallel()
for _, objectFormat := range id.SupportedObjectFormats() {
t.Run(objectFormat.String(), func(t *testing.T) {
t.Parallel()
m := memory.New(objectFormat)
mainID := objectFormat.Sum([]byte("main"))
devID := objectFormat.Sum([]byte("dev"))
nextMainID := objectFormat.Sum([]byte("next-main"))
nextDevID := objectFormat.Sum([]byte("next-dev"))
aliasID := objectFormat.Sum([]byte("alias"))
seed(t, m, func(tx store.Transaction) {
err := tx.Create("refs/heads/main", mainID)
if err != nil {
t.Fatalf("Create(main): %v", err)
}
err = tx.Create("refs/heads/dev", devID)
if err != nil {
t.Fatalf("Create(dev): %v", err)
}
err = tx.CreateSymbolic("refs/heads/alias", "refs/heads/main")
if err != nil {
t.Fatalf("CreateSymbolic(alias): %v", err)
}
})
batch, err := m.BeginBatch()
if err != nil {
t.Fatalf("BeginBatch: %v", err)
}
err = batch.Update("refs/heads/main", nextMainID, mainID)
if err != nil {
t.Fatalf("Update(main): %v", err)
}
// Updates the symbolic alias in deref mode,
// which resolves to refs/heads/main
// and therefore duplicates the first operation.
err = batch.Update("refs/heads/alias", aliasID, mainID)
if err != nil {
t.Fatalf("Update(alias): %v", err)
}
err = batch.Update("refs/heads/dev", nextDevID, devID)
if err != nil {
t.Fatalf("Update(dev): %v", err)
}
results, err := batch.Apply()
if err != nil {
t.Fatalf("Apply: %v", err)
}
if len(results) != 3 {
t.Fatalf("len(results) = %d, want 3", len(results))
}
if results[0].Status != store.BatchStatusApplied {
t.Fatalf("results[0].Status = %v, want applied", results[0].Status)
}
if results[1].Status != store.BatchStatusRejected {
t.Fatalf("results[1].Status = %v, want rejected", results[1].Status)
}
if !errors.Is(results[1].Error, store.ErrDuplicateUpdate) {
t.Fatalf("results[1].Error = %v, want ErrDuplicateUpdate", results[1].Error)
}
if results[2].Status != store.BatchStatusApplied {
t.Fatalf("results[2].Status = %v, want applied", results[2].Status)
}
if got := resolveDirect(t, m, "refs/heads/main").ID; got != nextMainID {
t.Fatalf("main after batch = %v, want %v", got, nextMainID)
}
if got := resolveDirect(t, m, "refs/heads/dev").ID; got != nextDevID {
t.Fatalf("dev after batch = %v, want %v", got, nextDevID)
}
resolved, err := m.Resolve("refs/heads/alias")
if err != nil {
t.Fatalf("Resolve(alias): %v", err)
}
symbolic, ok := resolved.(ref.Symbolic)
if !ok {
t.Fatalf("Resolve(alias) = %T, want ref.Symbolic", resolved)
}
if symbolic.Target != "refs/heads/main" {
t.Fatalf("alias target = %q, want refs/heads/main", symbolic.Target)
}
})
}
}
|