Skip to content

Commit d6aa6ec

Browse files
QinYuuuuDev Agentpulltheflower
committed
Fix repositories total mismatch bug (#710)
* Fix repositories total mismatch bug * Fix tests --------- Co-authored-by: Dev Agent <dev-agent@example.com> Co-authored-by: zhzhang <pulltheflower@163.com>
1 parent 8f48237 commit d6aa6ec

9 files changed

Lines changed: 351 additions & 1 deletion

File tree

builder/store/database/repository.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,22 @@ func (s *repoStoreImpl) PublicToUser(ctx context.Context, repoType types.Reposit
667667

668668
q.Where("repository.repository_type = ?", repoType)
669669

670+
// join table by repo type to filter out deleted and half-created records
671+
switch repoType {
672+
case types.ModelRepo:
673+
q.Join("LEFT JOIN models ON models.repository_id = repository.id")
674+
case types.DatasetRepo:
675+
q.Join("LEFT JOIN datasets ON datasets.repository_id = repository.id")
676+
case types.CodeRepo:
677+
q.Join("LEFT JOIN codes ON codes.repository_id = repository.id")
678+
case types.SpaceRepo:
679+
q.Join("LEFT JOIN spaces ON spaces.repository_id = repository.id")
680+
case types.PromptRepo:
681+
q.Join("LEFT JOIN prompts ON prompts.repository_id = repository.id")
682+
case types.MCPServerRepo:
683+
q.Join("LEFT JOIN mcp_servers ON mcp_servers.repository_id = repository.id")
684+
}
685+
670686
if !isAdmin {
671687
if len(userIDs) > 0 {
672688
q.Where("repository.private = ? or repository.user_id in (?)", false, bun.In(userIDs))
@@ -695,7 +711,7 @@ func (s *repoStoreImpl) PublicToUser(ctx context.Context, repoType types.Reposit
695711
}
696712

697713
if len(filter.SpaceSDK) > 0 {
698-
q.Join("LEFT JOIN spaces AS spaces ON repository.id = spaces.repository_id").Where("spaces.sdk = ?", filter.SpaceSDK)
714+
q.Where("spaces.sdk = ?", filter.SpaceSDK)
699715
}
700716

701717
if len(filter.Tags) > 0 {

builder/store/database/repository_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ func TestRepoStore_PublicToUserSimple(t *testing.T) {
307307
})
308308
require.Empty(t, err)
309309
require.NotNil(t, repo)
310+
ds := database.NewDatasetStoreWithDB(db)
311+
_, err = ds.Create(ctx, database.Dataset{
312+
RepositoryID: repo.ID,
313+
})
314+
require.Empty(t, err)
310315

311316
ts := database.NewTagStoreWithDB(db)
312317
var tags []*database.Tag
@@ -516,6 +521,13 @@ func setupTestRepositories(ctx context.Context, store database.RepoStore, db *da
516521
if err != nil {
517522
return nil, err
518523
}
524+
code := database.Code{
525+
RepositoryID: rn.ID,
526+
}
527+
_, err = db.Core.NewInsert().Model(&code).Exec(ctx)
528+
if err != nil {
529+
return nil, err
530+
}
519531
for _, tag := range repo.Tags {
520532
_, err = db.Core.NewInsert().Model(&tag).Exec(ctx, &tag)
521533
if err != nil {

component/code.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ func (c *codeComponentImpl) Index(ctx context.Context, filter *types.RepoFilter,
189189
break
190190
}
191191
}
192+
if code == nil {
193+
continue
194+
}
192195
var (
193196
tags []types.RepoTag
194197
mirrorTaskStatus types.MirrorTaskStatus

component/code_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,37 @@ func TestCodeComponent_Index(t *testing.T) {
115115
}, data)
116116
}
117117

118+
func TestCodeComponent_Index_HalfCreatedRepos(t *testing.T) {
119+
ctx := context.TODO()
120+
cc := initializeTestCodeComponent(ctx, t)
121+
122+
filter := &types.RepoFilter{Username: "user"}
123+
// PublicToUser returns 3 repositories, but only 2 have corresponding codes
124+
cc.mocks.components.repo.EXPECT().PublicToUser(ctx, types.CodeRepo, "user", filter, 10, 1).Return(
125+
[]*database.Repository{
126+
{ID: 1, Name: "r1", Tags: []database.Tag{{Name: "t1"}}},
127+
{ID: 2, Name: "r2"},
128+
{ID: 3, Name: "half-created", Tags: []database.Tag{{Name: "t3"}}}, // This is a half-created repo with no code
129+
}, 3, nil, // Total should be 3
130+
)
131+
132+
// ByRepoIDs returns only 2 codes (no code for repo ID 3)
133+
cc.mocks.stores.CodeMock().EXPECT().ByRepoIDs(ctx, []int64{1, 2, 3}).Return([]database.Code{
134+
{ID: 11, RepositoryID: 2, Repository: &database.Repository{ID: 2, Name: "r2", Mirror: database.Mirror{}}},
135+
{ID: 12, RepositoryID: 1, Repository: &database.Repository{ID: 1, Name: "r1", Mirror: database.Mirror{}}},
136+
}, nil)
137+
138+
data, total, err := cc.Index(ctx, filter, 10, 1, false)
139+
require.Nil(t, err)
140+
require.Equal(t, 3, total) // Total should match PublicToUser's return value
141+
require.Len(t, data, 2) // But only 2 codes should be returned
142+
143+
require.Equal(t, []*types.Code{
144+
{ID: 12, RepositoryID: 1, Name: "r1", Tags: []types.RepoTag{{Name: "t1"}}, RecomOpWeight: 0},
145+
{ID: 11, RepositoryID: 2, Name: "r2", RecomOpWeight: 0},
146+
}, data)
147+
}
148+
118149
func TestCodeComponent_Update(t *testing.T) {
119150
ctx := context.TODO()
120151
cc := initializeTestCodeComponent(ctx, t)

component/dataset_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,55 @@ func TestDatasetCompnent_Index(t *testing.T) {
143143

144144
}
145145

146+
func TestDatasetComponent_Index_HalfCreatedRepos(t *testing.T) {
147+
ctx := context.TODO()
148+
dc := initializeTestDatasetComponent(ctx, t)
149+
150+
filter := &types.RepoFilter{Username: "user"}
151+
// PublicToUser returns 3 repositories, but only 2 have corresponding datasets
152+
dc.mocks.components.repo.EXPECT().PublicToUser(ctx, types.DatasetRepo, "user", filter, 10, 1).Return(
153+
[]*database.Repository{
154+
{ID: 1, Tags: []database.Tag{{Name: "t1"}}},
155+
{ID: 2},
156+
{ID: 3, Tags: []database.Tag{{Name: "half-created"}}}, // This is a half-created repo with no dataset
157+
}, 3, nil, // Total should be 3
158+
)
159+
160+
// ByRepoIDs returns only 2 datasets (no dataset for repo ID 3)
161+
dc.mocks.stores.DatasetMock().EXPECT().ByRepoIDs(ctx, []int64{1, 2, 3}).Return([]database.Dataset{
162+
{
163+
ID: 11, RepositoryID: 2, Repository: &database.Repository{
164+
User: database.User{Username: "user2"},
165+
},
166+
},
167+
{
168+
ID: 12, RepositoryID: 1, Repository: &database.Repository{
169+
User: database.User{Username: "user1"},
170+
},
171+
},
172+
}, nil)
173+
174+
data, total, err := dc.Index(ctx, filter, 10, 1, false)
175+
require.Nil(t, err)
176+
require.Equal(t, 3, total) // Total should match PublicToUser's return value
177+
require.Len(t, data, 2) // But only 2 datasets should be returned
178+
179+
require.Equal(t, []*types.Dataset{
180+
{ID: 12, RepositoryID: 1, Repository: types.Repository{
181+
HTTPCloneURL: "/s/.git",
182+
SSHCloneURL: ":s/.git",
183+
}, User: types.User{Username: "user1"},
184+
Tags: []types.RepoTag{{Name: "t1"}},
185+
},
186+
{ID: 11, RepositoryID: 2, Repository: types.Repository{
187+
HTTPCloneURL: "/s/.git",
188+
SSHCloneURL: ":s/.git",
189+
}, User: types.User{Username: "user2"},
190+
},
191+
}, data)
192+
193+
}
194+
146195
func TestDatasetCompnent_Update(t *testing.T) {
147196
ctx := context.TODO()
148197
dc := initializeTestDatasetComponent(ctx, t)

component/mcp_server_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,60 @@ func TestMCPServerComponent_Index(t *testing.T) {
307307
require.Equal(t, total, 1)
308308
}
309309

310+
func TestMCPServerComponent_Index_HalfCreatedRepos(t *testing.T) {
311+
ctx := context.TODO()
312+
mc := initializeTestMCPServerComponent(ctx, t)
313+
314+
user := database.User{
315+
Username: "user",
316+
Email: "foo@bar.com",
317+
}
318+
319+
// Create two repositories, one with complete data and one half-created
320+
repo1 := &database.Repository{
321+
ID: 321,
322+
User: user,
323+
Tags: []database.Tag{{Name: "t1"}},
324+
Name: "complete-repo",
325+
License: "MIT",
326+
Nickname: "complete-repo",
327+
Path: "ns/complete-repo",
328+
}
329+
330+
repo2 := &database.Repository{
331+
ID: 322,
332+
User: user,
333+
Tags: []database.Tag{{Name: "t2"}},
334+
Name: "half-created",
335+
License: "MIT",
336+
Nickname: "half-created",
337+
Path: "ns/half-created",
338+
}
339+
340+
filter := &types.RepoFilter{
341+
Username: "user",
342+
}
343+
344+
// PublicToUser returns both repositories
345+
mc.mocks.components.repo.EXPECT().PublicToUser(ctx, types.MCPServerRepo, "user", filter, 10, 1).
346+
Return([]*database.Repository{repo1, repo2}, 2, nil)
347+
348+
// ByRepoIDs returns only 1 MCP server (no MCP server for repo2)
349+
mc.mocks.stores.MCPServerMock().EXPECT().ByRepoIDs(ctx, []int64{321, 322}).Return([]database.MCPServer{
350+
{
351+
RepositoryID: 321,
352+
Repository: repo1,
353+
},
354+
}, nil)
355+
356+
res, total, err := mc.Index(ctx, filter, 10, 1, false)
357+
require.Nil(t, err)
358+
require.NotNil(t, res)
359+
require.Equal(t, total, 2) // Total should match PublicToUser's return value
360+
require.Len(t, res, 1) // But only 1 MCP server should be returned
361+
}
362+
363+
310364
func TestMCPServerComponent_Properties(t *testing.T) {
311365
ctx := context.TODO()
312366
mc := initializeTestMCPServerComponent(ctx, t)

component/model_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,50 @@ func TestModelComponent_Index(t *testing.T) {
5555

5656
}
5757

58+
func TestModelComponent_Index_HalfCreatedRepos(t *testing.T) {
59+
ctx := context.TODO()
60+
mc := initializeTestModelComponent(ctx, t)
61+
62+
filter := &types.RepoFilter{Username: "user"}
63+
// PublicToUser returns 3 repositories, but only 2 have corresponding models
64+
mc.mocks.components.repo.EXPECT().PublicToUser(ctx, types.ModelRepo, "user", filter, 10, 1).Return(
65+
[]*database.Repository{
66+
{ID: 1, Name: "r1", Tags: []database.Tag{{Name: "t1"}}},
67+
{ID: 2, Name: "r2", Tags: []database.Tag{{Name: "t2"}}},
68+
{ID: 3, Name: "half-created", Tags: []database.Tag{{Name: "t3"}}}, // This is a half-created repo with no model
69+
}, 3, nil, // Total should be 3
70+
)
71+
72+
// ByRepoIDs returns only 2 models (no model for repo ID 3)
73+
mc.mocks.stores.ModelMock().EXPECT().ByRepoIDs(ctx, []int64{1, 2, 3}).Return([]database.Model{
74+
{RepositoryID: 1, ID: 11, Repository: &database.Repository{}},
75+
{RepositoryID: 2, ID: 12, Repository: &database.Repository{}},
76+
}, nil)
77+
78+
data, total, err := mc.Index(ctx, filter, 10, 1, false)
79+
require.Nil(t, err)
80+
require.Equal(t, 3, total) // Total should match PublicToUser's return value
81+
require.Len(t, data, 2) // But only 2 models should be returned
82+
83+
require.Equal(t, []*types.Model{
84+
{
85+
ID: 11, Name: "r1", Tags: []types.RepoTag{{Name: "t1"}}, RepositoryID: 1,
86+
Repository: types.Repository{
87+
HTTPCloneURL: "https://foo.com/s/.git",
88+
SSHCloneURL: "test@127.0.0.1:s/.git",
89+
},
90+
},
91+
{
92+
ID: 12, Name: "r2", Tags: []types.RepoTag{{Name: "t2"}}, RepositoryID: 2,
93+
Repository: types.Repository{
94+
HTTPCloneURL: "https://foo.com/s/.git",
95+
SSHCloneURL: "test@127.0.0.1:s/.git",
96+
},
97+
},
98+
}, data)
99+
100+
}
101+
58102
func TestModelComponent_Create(t *testing.T) {
59103
ctx := context.TODO()
60104
mc := initializeTestModelComponent(ctx, t)

component/prompt_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,53 @@ func TestPromptComponent_IndexPromptRepo(t *testing.T) {
635635

636636
}
637637

638+
func TestPromptComponent_IndexPromptRepo_HalfCreatedRepos(t *testing.T) {
639+
ctx := context.TODO()
640+
pc := initializeTestPromptComponent(ctx, t)
641+
642+
filter := &types.RepoFilter{Username: "foo"}
643+
// PublicToUser returns 4 repositories, but only 3 have corresponding prompts
644+
pc.mocks.components.repo.EXPECT().PublicToUser(ctx, types.PromptRepo, "foo", filter, 10, 1).Return([]*database.Repository{
645+
{ID: 1, Name: "rp1"},
646+
{ID: 2, Name: "rp2"},
647+
{ID: 3, Name: "rp3", Tags: []database.Tag{{Name: "t1"}}},
648+
{ID: 4, Name: "half-created", Tags: []database.Tag{{Name: "t2"}}}, // This is a half-created repo with no prompt
649+
}, 4, nil).Once()
650+
// ByRepoIDs returns only 3 prompts (no prompt for repo ID 4)
651+
pc.mocks.stores.PromptMock().EXPECT().ByRepoIDs(ctx, []int64{1, 2, 3, 4}).Return([]database.Prompt{
652+
{ID: 6, RepositoryID: 2, Repository: &database.Repository{}},
653+
{ID: 5, RepositoryID: 1, Repository: &database.Repository{}},
654+
{ID: 4, RepositoryID: 3, Repository: &database.Repository{}},
655+
}, nil).Once()
656+
657+
results, total, err := pc.IndexPromptRepo(ctx, filter, 10, 1)
658+
require.Nil(t, err)
659+
require.Equal(t, 4, total) // Total should match PublicToUser's return value
660+
require.Len(t, results, 3) // But only 3 prompts should be returned
661+
662+
require.Equal(t, []types.PromptRes{
663+
{
664+
RepositoryID: 1,
665+
ID: 5, Name: "rp1", Repository: types.Repository{
666+
HTTPCloneURL: "",
667+
SSHCloneURL: "",
668+
}},
669+
{
670+
RepositoryID: 2,
671+
ID: 6, Name: "rp2", Repository: types.Repository{
672+
HTTPCloneURL: "",
673+
SSHCloneURL: "",
674+
}},
675+
{
676+
RepositoryID: 3,
677+
ID: 4, Name: "rp3", Repository: types.Repository{
678+
HTTPCloneURL: "",
679+
SSHCloneURL: "",
680+
}, Tags: []types.RepoTag{{Name: "t1"}}},
681+
}, results)
682+
683+
}
684+
638685
func TestPromptComponent_UpdatePromptRepo(t *testing.T) {
639686
ctx := context.TODO()
640687
pc := initializeTestPromptComponent(ctx, t)

0 commit comments

Comments
 (0)