Skip to content

Commit 42bb08d

Browse files
committed
refactor: split oversized domain owners
1 parent 68603f6 commit 42bb08d

18 files changed

Lines changed: 2203 additions & 2119 deletions
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"errors"
6+
"reflect"
7+
"strings"
8+
"testing"
9+
"time"
10+
11+
"github.com/morluto/gitcontribute/internal/contracts"
12+
"github.com/morluto/gitcontribute/internal/github"
13+
"github.com/morluto/gitcontribute/internal/mcpcontract"
14+
)
15+
16+
type fakeRepositorySearchReader struct {
17+
github.Reader
18+
result github.RepositorySearchResult
19+
options github.RepositorySearchOptions
20+
}
21+
22+
func (f *fakeRepositorySearchReader) SearchRepositories(_ context.Context, options github.RepositorySearchOptions) (github.RepositorySearchResult, error) {
23+
f.options = options
24+
return f.result, nil
25+
}
26+
27+
func TestSearchGitHubRepositoriesPersistsObservedMetadata(t *testing.T) {
28+
t.Parallel()
29+
ctx := context.Background()
30+
svc := newSearchTestService(t)
31+
now := time.Unix(1000, 0).UTC()
32+
remote := github.Repository{Owner: "acme", Name: "rocket", Description: "fast inference", Stars: 9001, Language: "Go", UpdatedAt: now}
33+
reader := &fakeRepositorySearchReader{result: github.RepositorySearchResult{Total: 321, Items: []github.Repository{remote}, Page: github.PageInfo{Page: 2, NextPage: 3, HasNext: true}}}
34+
svc.SetGitHubReader(reader)
35+
36+
out, err := (&MCPReader{svc}).SearchGitHubRepositories(ctx, mcpcontract.SearchGitHubRepositoriesInput{Text: "fast inference", MatchFields: []string{"name", "description"}, Topics: []string{"llm-inference"}, Language: "Go", StarsMin: ptr(200), PushedAfter: "2026-06-15", Archived: ptr(false), Fork: ptr(false), Sort: "stars", Order: "desc", Limit: 12, Page: 2, ResponseFormat: "concise"})
37+
if err != nil {
38+
t.Fatal(err)
39+
}
40+
if reader.options.PerPage != 12 || reader.options.Page != 2 || reader.options.Sort != "stars" || reader.options.Query != `"fast inference" in:name,description topic:llm-inference language:Go stars:>=200 pushed:>=2026-06-15 archived:false fork:false` {
41+
t.Fatalf("compiled options = %+v", reader.options)
42+
}
43+
if out.NextPage != 3 || out.ResponseFormat != "concise" || len(out.Items) != 1 || out.Items[0].Value == nil || out.Items[0].Value.Ref != "repository:acme/rocket" || *out.Items[0].Value.Stars != 9001 {
44+
t.Fatalf("live search result = %+v, options = %+v", out, reader.options)
45+
}
46+
if out.Items[0].Value.Watchers != nil || len(out.RecoveryPlans) != 1 || len(out.RecoveryPlans[0].Then) != 1 || out.RecoveryPlans[0].Then[0].Type() != "sync_threads" {
47+
t.Fatalf("concise search context = %+v", out)
48+
}
49+
if out.Items[0].Value.DossierStatus != "missing" {
50+
t.Fatalf("new search result dossier availability = %+v", out.Items[0].Value)
51+
}
52+
stored, err := (&MCPReader{svc}).GetRepositories(ctx, mcpcontract.GetRepositoriesInput{Repositories: []mcpcontract.RepositoryRef{{Owner: "acme", Repo: "rocket"}}})
53+
if err != nil {
54+
t.Fatal(err)
55+
}
56+
if stored.Items[0].Value == nil || stored.Items[0].Value.Metadata.Status != "complete" || *stored.Items[0].Value.Stars != 9001 {
57+
t.Fatalf("search metadata was not persisted: %+v", stored)
58+
}
59+
if _, err := svc.BuildRepositoryDossier(ctx, contracts.RepoRef{Owner: "acme", Repo: "rocket"}); err != nil {
60+
t.Fatal(err)
61+
}
62+
out, err = (&MCPReader{svc}).SearchGitHubRepositories(ctx, mcpcontract.SearchGitHubRepositoriesInput{Text: "fast inference", Limit: 12, Page: 2})
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
if out.Items[0].Value == nil || out.Items[0].Value.DossierStatus != "available" || out.Items[0].Value.DossierAsOf == "" {
67+
t.Fatalf("live search did not report local dossier availability: %+v", out)
68+
}
69+
}
70+
71+
func TestCompileRepositorySearchRejectsAmbiguousAndInvalidInputs(t *testing.T) {
72+
t.Parallel()
73+
cases := []struct {
74+
name string
75+
in mcpcontract.SearchGitHubRepositoriesInput
76+
}{
77+
{name: "empty", in: mcpcontract.SearchGitHubRepositoriesInput{}},
78+
{name: "raw and structured", in: mcpcontract.SearchGitHubRepositoriesInput{RawQuery: "cuda", Language: "Go"}},
79+
{name: "unknown match field", in: mcpcontract.SearchGitHubRepositoriesInput{Text: "cuda", MatchFields: []string{"topics"}}},
80+
{name: "reversed stars", in: mcpcontract.SearchGitHubRepositoriesInput{Text: "cuda", StarsMin: ptr(20), StarsMax: ptr(10)}},
81+
{name: "invalid date", in: mcpcontract.SearchGitHubRepositoriesInput{PushedAfter: "yesterday"}},
82+
{name: "reversed dates", in: mcpcontract.SearchGitHubRepositoriesInput{CreatedAfter: "2026-07-01", CreatedBefore: "2026-06-01"}},
83+
}
84+
for _, tc := range cases {
85+
t.Run(tc.name, func(t *testing.T) {
86+
if _, _, _, err := compileRepositorySearch(tc.in); err == nil {
87+
t.Fatal("invalid search was accepted")
88+
}
89+
})
90+
}
91+
}
92+
93+
func TestCompileRepositorySearchPreservesExplicitZeroStarBound(t *testing.T) {
94+
t.Parallel()
95+
zero := 0
96+
query, _, _, err := compileRepositorySearch(mcpcontract.SearchGitHubRepositoriesInput{StarsMax: &zero})
97+
if err != nil {
98+
t.Fatal(err)
99+
}
100+
if query != "stars:<=0" {
101+
t.Fatalf("query = %q, want stars:<=0", query)
102+
}
103+
}
104+
105+
func TestRepositorySearchValidationExamplesAreUsable(t *testing.T) {
106+
t.Parallel()
107+
_, _, _, err := compileRepositorySearch(mcpcontract.SearchGitHubRepositoriesInput{})
108+
var toolErr *mcpcontract.ToolError
109+
if !errors.As(err, &toolErr) {
110+
t.Fatalf("error = %v, want ToolError", err)
111+
}
112+
if toolErr.Example["text"] != "GitHub contribution research" || !reflect.DeepEqual(toolErr.Example["match_fields"], []string{"name", "description"}) {
113+
t.Fatalf("empty-search example = %#v", toolErr.Example)
114+
}
115+
116+
_, _, _, err = compileRepositorySearch(mcpcontract.SearchGitHubRepositoriesInput{RawQuery: "language:go", Language: "Go"})
117+
if !errors.As(err, &toolErr) || toolErr.Example["raw_query"] != "is:public language:go stars:>=100" {
118+
t.Fatalf("ambiguous-search example = %#v, error=%v", toolErr.Example, err)
119+
}
120+
}
121+
122+
func TestCompileRepositorySearchWarnsAboutRawReadmeQueries(t *testing.T) {
123+
t.Parallel()
124+
query, interpretation, warnings, err := compileRepositorySearch(mcpcontract.SearchGitHubRepositoriesInput{RawQuery: "attention in:readme"})
125+
if err != nil {
126+
t.Fatal(err)
127+
}
128+
if query != "attention in:readme" || !strings.Contains(interpretation, "advanced raw query") || len(warnings) != 1 || warnings[0].Code != "broad_readme_match" {
129+
t.Fatalf("raw query context = %q %q %+v", query, interpretation, warnings)
130+
}
131+
}
132+
133+
func TestCompileRepositorySearchWarnsAboutStructuredReadmeMatching(t *testing.T) {
134+
t.Parallel()
135+
query, _, warnings, err := compileRepositorySearch(mcpcontract.SearchGitHubRepositoriesInput{Text: "attention", MatchFields: []string{"name", "readme"}})
136+
if err != nil {
137+
t.Fatal(err)
138+
}
139+
if query != "attention in:name,readme" || len(warnings) != 1 || warnings[0].Code != "broad_readme_match" {
140+
t.Fatalf("structured README warning = %q %+v", query, warnings)
141+
}
142+
}
143+
144+
func TestRepositorySearchDetailedFormatPreservesSecondaryFacts(t *testing.T) {
145+
t.Parallel()
146+
archived := true
147+
remote := github.Repository{Owner: "acme", Name: "rocket", Description: "fast", Stars: 42, Watchers: 9, Forks: 3, OpenIssues: 7, Archived: archived, Topics: []string{"cuda"}}
148+
match := liveRepositorySearchMatch(remote, mcpcontract.RepositoryMetadataOutput{Status: "complete"}, detailedResponse)
149+
if match.Ref != "repository:acme/rocket" || match.Watchers == nil || *match.Watchers != 9 || match.Archived == nil || !*match.Archived || len(match.Topics) != 1 {
150+
t.Fatalf("detailed match = %+v", match)
151+
}
152+
}

0 commit comments

Comments
 (0)