scooter  ·  2026-04-13

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}