shortid_test.go
1package main
2
3import (
4 "errors"
5 "sort"
6 "testing"
7)
8
9func TestDiffPosition(t *testing.T) {
10 tests := []struct {
11 name string
12 a string
13 b string
14 expected int
15 }{
16 {"identical strings", "abc", "abc", 3},
17 {"differs at start", "abc", "xyz", 0},
18 {"differs at position 1", "abc", "axc", 1},
19 {"differs at position 2", "abc", "abx", 2},
20 {"prefix of other", "abc", "abcd", 3},
21 {"empty strings", "", "", 0},
22 {"one empty", "abc", "", 0},
23 {"hex IDs", "87cghty", "87dfty4", 2},
24 }
25
26 for _, tt := range tests {
27 t.Run(tt.name, func(t *testing.T) {
28 result := diffPosition(tt.a, tt.b)
29 if result != tt.expected {
30 t.Errorf("diffPosition(%q, %q) = %d, want %d", tt.a, tt.b, result, tt.expected)
31 }
32 })
33 }
34}
35
36func TestFindMinPrefix(t *testing.T) {
37 tests := []struct {
38 name string
39 ids []string
40 index int
41 expected int
42 }{
43 {"single ID", []string{"abc"}, 0, 1},
44 {"two IDs - first", []string{"87c", "87d"}, 0, 3},
45 {"two IDs - second", []string{"87c", "87d"}, 1, 3},
46 {"three IDs - first", []string{"87cghty", "87dfty4", "abcdef"}, 0, 3},
47 {"three IDs - middle", []string{"87cghty", "87dfty4", "abcdef"}, 1, 3},
48 {"three IDs - last", []string{"87cghty", "87dfty4", "abcdef"}, 2, 1},
49 {"all unique at start", []string{"a", "b", "c"}, 0, 1},
50 {"shared long prefix", []string{"abcd123", "abcd456", "abcd789"}, 1, 5},
51 {"empty slice", []string{}, 0, 0},
52 }
53
54 for _, tt := range tests {
55 t.Run(tt.name, func(t *testing.T) {
56 result := findMinPrefix(tt.ids, tt.index)
57 if result != tt.expected {
58 t.Errorf("findMinPrefix(%v, %d) = %d, want %d", tt.ids, tt.index, result, tt.expected)
59 }
60 })
61 }
62}
63
64func TestShortIDMap_GetShortID(t *testing.T) {
65 // Create a test map
66 m := &ShortIDMap{
67 fullToShort: map[string]string{
68 "87cghty": "87c",
69 "87dfty4": "87d",
70 "abcdef": "a",
71 },
72 shortToFull: map[string]string{
73 "87c": "87cghty",
74 "87d": "87dfty4",
75 "a": "abcdef",
76 },
77 allFullIDs: []string{"87cghty", "87dfty4", "abcdef"},
78 }
79
80 tests := []struct {
81 name string
82 fullID string
83 want string
84 wantErr bool
85 errIs error
86 }{
87 {"existing ID 1", "87cghty", "87c", false, nil},
88 {"existing ID 2", "87dfty4", "87d", false, nil},
89 {"existing ID 3", "abcdef", "a", false, nil},
90 {"non-existent ID", "nonexistent", "", true, ErrIDNotFound},
91 }
92
93 for _, tt := range tests {
94 t.Run(tt.name, func(t *testing.T) {
95 got, err := m.GetShortID(tt.fullID)
96 if (err != nil) != tt.wantErr {
97 t.Errorf("GetShortID() error = %v, wantErr %v", err, tt.wantErr)
98 return
99 }
100 if tt.wantErr && tt.errIs != nil && !errors.Is(err, tt.errIs) {
101 t.Errorf("GetShortID() error = %v, want error Is %v", err, tt.errIs)
102 return
103 }
104 if got != tt.want {
105 t.Errorf("GetShortID() = %v, want %v", got, tt.want)
106 }
107 })
108 }
109}
110
111func TestShortIDMap_GetShortID_NilMap(t *testing.T) {
112 var m *ShortIDMap
113 _, err := m.GetShortID("test")
114 if !errors.Is(err, ErrIDNotFound) {
115 t.Errorf("expected ErrIDNotFound for nil map, got %v", err)
116 }
117}
118
119func TestShortIDMap_GetFullID(t *testing.T) {
120 // Create a test map
121 m := &ShortIDMap{
122 fullToShort: map[string]string{
123 "87cghty": "87c",
124 "87dfty4": "87d",
125 "abcdef": "a",
126 },
127 shortToFull: map[string]string{
128 "87c": "87cghty",
129 "87d": "87dfty4",
130 "a": "abcdef",
131 },
132 allFullIDs: []string{"87cghty", "87dfty4", "abcdef"},
133 }
134
135 tests := []struct {
136 name string
137 shortID string
138 want string
139 wantErr bool
140 errIs error
141 }{
142 {"exact short ID 1", "87c", "87cghty", false, nil},
143 {"exact short ID 2", "87d", "87dfty4", false, nil},
144 {"exact short ID 3", "a", "abcdef", false, nil},
145 {"longer prefix", "87cg", "87cghty", false, nil},
146 {"non-existent prefix", "xyz", "", true, ErrPrefixNotFound},
147 {"ambiguous prefix", "87", "", true, ErrAmbiguousPrefix},
148 {"empty prefix", "", "", true, ErrAmbiguousPrefix},
149 }
150
151 for _, tt := range tests {
152 t.Run(tt.name, func(t *testing.T) {
153 got, err := m.GetFullID(tt.shortID)
154 if (err != nil) != tt.wantErr {
155 t.Errorf("GetFullID() error = %v, wantErr %v", err, tt.wantErr)
156 return
157 }
158 if tt.wantErr && tt.errIs != nil && !errors.Is(err, tt.errIs) {
159 t.Errorf("GetFullID() error = %v, want error Is %v", err, tt.errIs)
160 return
161 }
162 if got != tt.want {
163 t.Errorf("GetFullID() = %v, want %v", got, tt.want)
164 }
165 })
166 }
167}
168
169func TestShortIDMap_GetFullID_NilMap(t *testing.T) {
170 var m *ShortIDMap
171 _, err := m.GetFullID("test")
172 if !errors.Is(err, ErrPrefixNotFound) {
173 t.Errorf("expected ErrPrefixNotFound for nil map, got %v", err)
174 }
175}
176
177func TestShortIDMap_EdgeCases(t *testing.T) {
178 tests := []struct {
179 name string
180 ids []string
181 expectedMap map[string]string // fullID -> expected shortID
182 }{
183 {
184 name: "empty repo",
185 ids: []string{},
186 expectedMap: map[string]string{},
187 },
188 {
189 name: "single ID",
190 ids: []string{"abc123"},
191 expectedMap: map[string]string{
192 "abc123": "a",
193 },
194 },
195 {
196 name: "two IDs differ at start",
197 ids: []string{"abc", "xyz"},
198 expectedMap: map[string]string{
199 "abc": "a",
200 "xyz": "x",
201 },
202 },
203 {
204 name: "shared long prefix",
205 ids: []string{"abcd123", "abcd456", "abcd789"},
206 expectedMap: map[string]string{
207 "abcd123": "abcd1",
208 "abcd456": "abcd4",
209 "abcd789": "abcd7",
210 },
211 },
212 {
213 name: "sequential hex IDs",
214 ids: []string{"87cghty", "87dfty4", "abcdef"},
215 expectedMap: map[string]string{
216 "87cghty": "87c",
217 "87dfty4": "87d",
218 "abcdef": "a",
219 },
220 },
221 }
222
223 for _, tt := range tests {
224 t.Run(tt.name, func(t *testing.T) {
225 // Sort the IDs like Generate does
226 sort.Strings(tt.ids)
227
228 // Build the map
229 fullToShort := make(map[string]string)
230 for i, fullID := range tt.ids {
231 prefixLen := findMinPrefix(tt.ids, i)
232 shortID := fullID[:prefixLen]
233 fullToShort[fullID] = shortID
234 }
235
236 // Verify
237 for fullID, expectedShort := range tt.expectedMap {
238 if got := fullToShort[fullID]; got != expectedShort {
239 t.Errorf("fullID %q: got shortID %q, want %q", fullID, got, expectedShort)
240 }
241 }
242 })
243 }
244}
245
246func TestNewShortIDGenerator(t *testing.T) {
247 gen := NewShortIDGenerator("/path/to/repo")
248 if gen == nil {
249 t.Fatal("NewShortIDGenerator returned nil")
250 }
251 if gen.repoPath != "/path/to/repo" {
252 t.Errorf("repoPath = %q, want %q", gen.repoPath, "/path/to/repo")
253 }
254}