Skip to content

Commit ff909d1

Browse files
cmrigneyclaude
andcommitted
Error on duplicate profile ID instead of appending number suffix
When creating a profile without an explicit ID, return an error if the derived ID already exists rather than silently appending _2, _3, etc. Removes the now-unused FindWorkingSetsByIDPrefix DB method. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0c22732 commit ff909d1

File tree

5 files changed

+19
-151
lines changed

5 files changed

+19
-151
lines changed

pkg/db/workingset.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
type WorkingSetDAO interface {
1313
GetWorkingSet(ctx context.Context, id string) (*WorkingSet, error)
14-
FindWorkingSetsByIDPrefix(ctx context.Context, prefix string) ([]WorkingSet, error)
1514
ListWorkingSets(ctx context.Context) ([]WorkingSet, error)
1615
CreateWorkingSet(ctx context.Context, workingSet WorkingSet) error
1716
UpdateWorkingSet(ctx context.Context, workingSet WorkingSet) error
@@ -145,17 +144,6 @@ func (d *dao) UpdateWorkingSet(ctx context.Context, workingSet WorkingSet) error
145144
return nil
146145
}
147146

148-
func (d *dao) FindWorkingSetsByIDPrefix(ctx context.Context, prefix string) ([]WorkingSet, error) {
149-
const query = `SELECT id, name, servers, secrets FROM working_set WHERE id LIKE $1`
150-
151-
var workingSets []WorkingSet
152-
err := d.db.SelectContext(ctx, &workingSets, query, prefix+"%")
153-
if err != nil {
154-
return nil, err
155-
}
156-
return workingSets, nil
157-
}
158-
159147
func (d *dao) ListWorkingSets(ctx context.Context) ([]WorkingSet, error) {
160148
const query = `SELECT id, name, servers, secrets FROM working_set`
161149

pkg/db/workingset_test.go

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -257,88 +257,6 @@ func TestListWorkingSetsEmpty(t *testing.T) {
257257
assert.Empty(t, retrieved)
258258
}
259259

260-
func TestFindWorkingSetsByIDPrefix(t *testing.T) {
261-
dao := setupTestDB(t)
262-
ctx := t.Context()
263-
264-
// Create working sets with different prefixes
265-
workingSets := []WorkingSet{
266-
{ID: "dev-frontend", Name: "Dev Frontend", Servers: ServerList{}, Secrets: SecretMap{}},
267-
{ID: "dev-backend", Name: "Dev Backend", Servers: ServerList{}, Secrets: SecretMap{}},
268-
{ID: "prod-frontend", Name: "Prod Frontend", Servers: ServerList{}, Secrets: SecretMap{}},
269-
{ID: "prod-backend", Name: "Prod Backend", Servers: ServerList{}, Secrets: SecretMap{}},
270-
{ID: "test-env", Name: "Test Env", Servers: ServerList{}, Secrets: SecretMap{}},
271-
}
272-
273-
for _, ws := range workingSets {
274-
err := dao.CreateWorkingSet(ctx, ws)
275-
require.NoError(t, err)
276-
}
277-
278-
tests := []struct {
279-
name string
280-
prefix string
281-
expectedCount int
282-
expectedIDs []string
283-
}{
284-
{
285-
name: "dev prefix",
286-
prefix: "dev",
287-
expectedCount: 2,
288-
expectedIDs: []string{"dev-frontend", "dev-backend"},
289-
},
290-
{
291-
name: "prod prefix",
292-
prefix: "prod",
293-
expectedCount: 2,
294-
expectedIDs: []string{"prod-frontend", "prod-backend"},
295-
},
296-
{
297-
name: "test prefix",
298-
prefix: "test",
299-
expectedCount: 1,
300-
expectedIDs: []string{"test-env"},
301-
},
302-
{
303-
name: "dev-f prefix (more specific)",
304-
prefix: "dev-f",
305-
expectedCount: 1,
306-
expectedIDs: []string{"dev-frontend"},
307-
},
308-
{
309-
name: "nonexistent prefix",
310-
prefix: "staging",
311-
expectedCount: 0,
312-
expectedIDs: []string{},
313-
},
314-
{
315-
name: "empty prefix",
316-
prefix: "",
317-
expectedCount: 5,
318-
expectedIDs: []string{"dev-frontend", "dev-backend", "prod-frontend", "prod-backend", "test-env"},
319-
},
320-
}
321-
322-
for _, tt := range tests {
323-
t.Run(tt.name, func(t *testing.T) {
324-
retrieved, err := dao.FindWorkingSetsByIDPrefix(ctx, tt.prefix)
325-
require.NoError(t, err)
326-
expectedCount := len(tt.expectedIDs)
327-
assert.Len(t, retrieved, expectedCount)
328-
329-
if expectedCount > 0 {
330-
ids := make(map[string]bool)
331-
for _, ws := range retrieved {
332-
ids[ws.ID] = true
333-
}
334-
for _, expectedID := range tt.expectedIDs {
335-
assert.True(t, ids[expectedID], "Expected ID %s not found", expectedID)
336-
}
337-
}
338-
})
339-
}
340-
}
341-
342260
func TestServerListJSONMarshaling(t *testing.T) {
343261
dao := setupTestDB(t)
344262
ctx := t.Context()

pkg/workingset/create_test.go

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func TestCreateWithExistingId(t *testing.T) {
196196
assert.Contains(t, err.Error(), "already exists")
197197
}
198198

199-
func TestCreateGeneratesUniqueIds(t *testing.T) {
199+
func TestCreateErrorsOnDuplicateDerivedID(t *testing.T) {
200200
dao := setupTestDB(t)
201201
ctx := t.Context()
202202

@@ -206,34 +206,12 @@ func TestCreateGeneratesUniqueIds(t *testing.T) {
206206
}, []string{})
207207
require.NoError(t, err)
208208

209-
// Create second with same name
209+
// Create second with same name should fail
210210
err = Create(ctx, dao, getMockRegistryClient(), getMockOciService(), "", "Test Set", []string{
211211
"docker://anotherimage:v1.0",
212212
}, []string{})
213-
require.NoError(t, err)
214-
215-
// Create third with same name
216-
err = Create(ctx, dao, getMockRegistryClient(), getMockOciService(), "", "Test Set", []string{
217-
"docker://anotherimage:v1.0",
218-
}, []string{})
219-
require.NoError(t, err)
220-
221-
// List all working sets
222-
sets, err := dao.ListWorkingSets(ctx)
223-
require.NoError(t, err)
224-
assert.Len(t, sets, 3)
225-
226-
// Verify IDs are unique
227-
ids := make(map[string]bool)
228-
for _, set := range sets {
229-
assert.False(t, ids[set.ID], "ID %s should be unique", set.ID)
230-
ids[set.ID] = true
231-
}
232-
233-
// Verify ID pattern
234-
assert.Contains(t, ids, "test_set")
235-
assert.Contains(t, ids, "test_set_2")
236-
assert.Contains(t, ids, "test_set_3")
213+
require.Error(t, err)
214+
assert.Contains(t, err.Error(), "already exists")
237215
}
238216

239217
func TestCreateWithInvalidServerFormat(t *testing.T) {

pkg/workingset/workingset.go

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -360,30 +360,15 @@ func createWorkingSetID(ctx context.Context, name string, dao db.DAO) (string, e
360360
cleaned := re.ReplaceAllString(name, "_")
361361
baseName := strings.ToLower(cleaned)
362362

363-
existingSets, err := dao.FindWorkingSetsByIDPrefix(ctx, baseName)
364-
if err != nil {
365-
return "", fmt.Errorf("failed to find profiles by name prefix: %w", err)
366-
}
367-
368-
if len(existingSets) == 0 {
369-
return baseName, nil
363+
_, err := dao.GetWorkingSet(ctx, baseName)
364+
if err == nil {
365+
return "", fmt.Errorf("a profile with id %q already exists", baseName)
370366
}
371-
372-
takenIDs := make(map[string]bool)
373-
for _, set := range existingSets {
374-
takenIDs[set.ID] = true
375-
}
376-
377-
// TODO(cody): there are better ways to do this, but this is a simple brute force for now
378-
// Append a number to the base name
379-
for i := 2; i <= 100; i++ {
380-
newName := fmt.Sprintf("%s_%d", baseName, i)
381-
if !takenIDs[newName] {
382-
return newName, nil
383-
}
367+
if !errors.Is(err, sql.ErrNoRows) {
368+
return "", fmt.Errorf("failed to look for existing profile: %w", err)
384369
}
385370

386-
return "", fmt.Errorf("failed to create profile id")
371+
return baseName, nil
387372
}
388373

389374
func ResolveServersFromString(ctx context.Context, registryClient registryapi.Client, ociService oci.Service, dao db.DAO, value string) ([]Server, error) {

pkg/workingset/workingset_test.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,7 @@ func TestCreateWorkingSetID(t *testing.T) {
902902
inputName string
903903
existingIDs []string
904904
expectedID string
905+
expectError bool
905906
}{
906907
{
907908
name: "simple name",
@@ -919,16 +920,10 @@ func TestCreateWorkingSetID(t *testing.T) {
919920
expectedID: "my_working_set_",
920921
},
921922
{
922-
name: "name with collision",
923+
name: "name with collision returns error",
923924
inputName: "test",
924925
existingIDs: []string{"test"},
925-
expectedID: "test_2",
926-
},
927-
{
928-
name: "name with multiple collisions",
929-
inputName: "test",
930-
existingIDs: []string{"test", "test_2", "test_3"},
931-
expectedID: "test_4",
926+
expectError: true,
932927
},
933928
}
934929

@@ -950,8 +945,12 @@ func TestCreateWorkingSetID(t *testing.T) {
950945
}
951946

952947
id, err := createWorkingSetID(ctx, tt.inputName, dao)
953-
require.NoError(t, err)
954-
assert.Equal(t, tt.expectedID, id)
948+
if tt.expectError {
949+
require.Error(t, err)
950+
} else {
951+
require.NoError(t, err)
952+
assert.Equal(t, tt.expectedID, id)
953+
}
955954
})
956955
}
957956
}

0 commit comments

Comments
 (0)