fork of https://github.com/sourcegraph/zoekt
1package main
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net/url"
8 "os"
9 "os/exec"
10 "path/filepath"
11 "strings"
12 "testing"
13 "time"
14
15 "github.com/google/go-cmp/cmp"
16 "github.com/google/go-cmp/cmp/cmpopts"
17 "github.com/sourcegraph/log/logtest"
18 "github.com/stretchr/testify/require"
19 "google.golang.org/grpc"
20 "google.golang.org/protobuf/testing/protocmp"
21 "google.golang.org/protobuf/types/known/timestamppb"
22
23 "slices"
24
25 "github.com/sourcegraph/zoekt"
26 configv1 "github.com/sourcegraph/zoekt/cmd/zoekt-sourcegraph-indexserver/grpc/protos/sourcegraph/zoekt/configuration/v1"
27 "github.com/sourcegraph/zoekt/internal/ctags"
28)
29
30func TestIterateIndexOptions_Fingerprint(t *testing.T) {
31 fingerprintV0 := &configv1.Fingerprint{
32 Identifier: 100,
33 GeneratedAt: timestamppb.New(time.Unix(100, 0)),
34 }
35
36 fingerprintV1 := &configv1.Fingerprint{
37 Identifier: 101,
38 GeneratedAt: timestamppb.New(time.Unix(101, 0)),
39 }
40
41 fingerprintV2 := &configv1.Fingerprint{
42 Identifier: 102,
43 GeneratedAt: timestamppb.New(time.Unix(102, 0)),
44 }
45
46 mkSearchConfigurationResponse := func(fingerprint *configv1.Fingerprint, repoIDs ...int32) *configv1.SearchConfigurationResponse {
47 repositories := make([]*configv1.ZoektIndexOptions, 0, len(repoIDs))
48 for _, repoID := range repoIDs {
49 repositories = append(repositories, &configv1.ZoektIndexOptions{
50 RepoId: repoID,
51 })
52 }
53
54 return &configv1.SearchConfigurationResponse{
55 UpdatedOptions: repositories,
56 Fingerprint: fingerprint,
57 }
58 }
59
60 grpcClient := &mockGRPCClient{
61 mockList: func(_ context.Context, in *configv1.ListRequest, opts ...grpc.CallOption) (*configv1.ListResponse, error) {
62 return &configv1.ListResponse{
63 RepoIds: []int32{1, 2, 3},
64 }, nil
65 },
66 }
67
68 clientOpts := []SourcegraphClientOption{
69 WithBatchSize(1),
70 }
71
72 testURL := url.URL{Scheme: "http", Host: "does.not.matter", Path: "/"}
73 sg := newSourcegraphClient(&testURL, "", grpcClient, clientOpts...)
74
75 type step struct {
76 name string
77
78 wantFingerprint *configv1.Fingerprint
79 returnFingerprint *configv1.Fingerprint
80 returnErr error
81 skipCheckingRepoIDs bool
82 }
83
84 for _, step := range []step{
85 {
86 name: "first call",
87 wantFingerprint: nil,
88 returnFingerprint: fingerprintV0,
89 },
90 {
91 name: "second call (should provide fingerprint from last time)",
92 wantFingerprint: fingerprintV0,
93 returnFingerprint: fingerprintV1,
94 },
95 {
96 name: "error",
97 wantFingerprint: fingerprintV1,
98 returnFingerprint: fingerprintV2,
99
100 returnErr: fmt.Errorf("boom"),
101 skipCheckingRepoIDs: true, // don't bother checking repoIDs if we expect an error
102 },
103 {
104 name: "call after error (should ignore fingerprint from last time, and provide the older one)",
105 wantFingerprint: fingerprintV1,
106 returnFingerprint: fingerprintV2,
107 },
108 } {
109 t.Run(step.name, func(t *testing.T) {
110 called := false
111 grpcClient.mockSearchConfiguration = func(_ context.Context, in *configv1.SearchConfigurationRequest, opts ...grpc.CallOption) (*configv1.SearchConfigurationResponse, error) {
112 called = true
113
114 diff := cmp.Diff(step.wantFingerprint, in.GetFingerprint(), protocmp.Transform())
115 if diff != "" {
116 t.Fatalf("unexpected fingerprint (-want +got):\n%s", diff)
117 }
118
119 return mkSearchConfigurationResponse(step.returnFingerprint, in.RepoIds...), step.returnErr
120 }
121
122 result, err := sg.List(context.Background(), nil)
123 if err != nil {
124 t.Fatalf("unexpected error from List: %v", err)
125 }
126
127 var iteratedIDs []uint32
128 result.IterateIndexOptions(func(options IndexOptions) {
129 iteratedIDs = append(iteratedIDs, options.RepoID)
130 })
131
132 if !called {
133 t.Fatal("expected SearchConfiguration to be called")
134 }
135
136 if step.skipCheckingRepoIDs {
137 return
138 }
139
140 slices.Sort(iteratedIDs)
141
142 expectedIDs := []uint32{1, 2, 3}
143 slices.Sort(expectedIDs)
144
145 if diff := cmp.Diff(expectedIDs, iteratedIDs); diff != "" {
146 t.Fatalf("unexpected repo ids (-want +got):\n%s", diff)
147 }
148 })
149 }
150}
151
152func TestGetIndexOptions(t *testing.T) {
153
154 type testCase struct {
155 name string
156 response *configv1.SearchConfigurationResponse
157 want *IndexOptions
158 wantErr string
159 }
160
161 for _, tc := range []testCase{
162 {
163 name: "symbols, large files",
164 response: &configv1.SearchConfigurationResponse{
165 UpdatedOptions: []*configv1.ZoektIndexOptions{
166 {
167 Symbols: true,
168 LargeFiles: []string{"foo", "bar"},
169 },
170 },
171 },
172 want: &IndexOptions{
173 Symbols: true,
174 LargeFiles: []string{"foo", "bar"},
175 },
176 },
177 {
178 name: "no symbols , large files",
179 response: &configv1.SearchConfigurationResponse{
180 UpdatedOptions: []*configv1.ZoektIndexOptions{
181 {
182 Symbols: true,
183 LargeFiles: []string{"foo", "bar"},
184 },
185 },
186 },
187 want: &IndexOptions{
188 Symbols: true,
189 LargeFiles: []string{"foo", "bar"},
190 },
191 },
192
193 {
194 name: "empty",
195 response: nil,
196 want: nil,
197 },
198
199 {
200 name: "symbols",
201 response: &configv1.SearchConfigurationResponse{
202 UpdatedOptions: []*configv1.ZoektIndexOptions{
203 {
204 Symbols: true,
205 },
206 },
207 },
208 want: &IndexOptions{
209 Symbols: true,
210 },
211 },
212 {
213 name: "repoID",
214 response: &configv1.SearchConfigurationResponse{
215 UpdatedOptions: []*configv1.ZoektIndexOptions{
216 {
217 RepoId: 123,
218 },
219 },
220 },
221 want: &IndexOptions{
222 RepoID: 123,
223 },
224 },
225 {
226 name: "error",
227 response: &configv1.SearchConfigurationResponse{
228 UpdatedOptions: []*configv1.ZoektIndexOptions{
229 {
230 Error: "boom",
231 },
232 },
233 },
234 want: nil,
235 wantErr: "boom",
236 },
237 } {
238 called := false
239 mockClient := &mockGRPCClient{
240 mockSearchConfiguration: func(_ context.Context, _ *configv1.SearchConfigurationRequest, _ ...grpc.CallOption) (*configv1.SearchConfigurationResponse, error) {
241 called = true
242 return tc.response, nil
243 },
244 }
245
246 testURL := &url.URL{
247 Scheme: "http",
248 Host: "does.not.matter",
249 Path: "/",
250 }
251
252 sg := newSourcegraphClient(
253 testURL,
254 "",
255 mockClient,
256 )
257
258 var got IndexOptions
259 var err error
260 sg.ForceIterateIndexOptions(func(o IndexOptions) {
261 got = o
262 }, func(_ uint32, e error) {
263 err = e
264 }, 123)
265
266 if !called {
267 t.Fatal("expected mock to be called")
268 }
269
270 if err != nil {
271 if tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr) {
272 t.Fatalf("unexpected error: %v", err)
273 }
274 }
275
276 if tc.want == nil {
277 continue
278 }
279
280 tc.want.CloneURL = sg.getCloneURL(got.Name)
281
282 if diff := cmp.Diff(tc.want, &got, cmpopts.EquateEmpty()); diff != "" {
283 t.Errorf("mismatch (-want +got):\n%s", diff)
284 }
285 }
286
287 // Mimic our fingerprint API, which doesn't return anything if the
288 // repo hasn't changed.
289 t.Run("unchanged", func(t *testing.T) {
290
291 called := false
292 mockClient := &mockGRPCClient{
293 mockSearchConfiguration: func(_ context.Context, _ *configv1.SearchConfigurationRequest, _ ...grpc.CallOption) (*configv1.SearchConfigurationResponse, error) {
294 called = true
295 return nil, nil
296 },
297 }
298
299 testURL := &url.URL{
300 Scheme: "http",
301 Host: "does.not.matter",
302 Path: "/",
303 }
304
305 sg := newSourcegraphClient(
306 testURL,
307 "",
308 mockClient,
309 )
310 gotAtLeastOneOption := false
311 var err error
312 sg.ForceIterateIndexOptions(func(_ IndexOptions) {
313 gotAtLeastOneOption = true
314 }, func(_ uint32, e error) {
315 err = e
316 }, 123)
317
318 if !called {
319 t.Fatal("expected mock to be called")
320 }
321
322 if err != nil {
323 t.Fatalf("unexpected error: %v", err)
324 }
325
326 if gotAtLeastOneOption {
327 t.Fatalf("expected no options, got %v", gotAtLeastOneOption)
328 }
329 })
330
331 var response *configv1.SearchConfigurationResponse
332 mockClient := &mockGRPCClient{
333 mockSearchConfiguration: func(_ context.Context, req *configv1.SearchConfigurationRequest, _ ...grpc.CallOption) (*configv1.SearchConfigurationResponse, error) {
334 if len(req.GetRepoIds()) == 0 || req.GetRepoIds()[0] != 123 {
335 return nil, errors.New("invalid repo id")
336 }
337 return response, nil
338 },
339 }
340
341 sg := newSourcegraphClient(&url.URL{Path: "/"}, "", mockClient, WithBatchSize(0))
342
343 cases := []struct {
344 Response *configv1.SearchConfigurationResponse
345 *IndexOptions
346 }{
347 {
348 Response: &configv1.SearchConfigurationResponse{
349 UpdatedOptions: []*configv1.ZoektIndexOptions{
350 {
351 Symbols: true,
352 LargeFiles: []string{"foo", "bar"},
353 },
354 },
355 },
356 IndexOptions: &IndexOptions{
357 Symbols: true,
358 LargeFiles: []string{"foo", "bar"},
359 Branches: []zoekt.RepositoryBranch{},
360 LanguageMap: map[string]ctags.CTagsParserType{},
361 },
362 },
363
364 {
365 Response: &configv1.SearchConfigurationResponse{
366 UpdatedOptions: []*configv1.ZoektIndexOptions{
367 {
368 Symbols: false,
369 LargeFiles: []string{"foo", "bar"},
370 },
371 },
372 },
373 IndexOptions: &IndexOptions{
374 LargeFiles: []string{"foo", "bar"},
375 Branches: []zoekt.RepositoryBranch{},
376 LanguageMap: map[string]ctags.CTagsParserType{},
377 },
378 },
379
380 {
381 Response: &configv1.SearchConfigurationResponse{},
382 },
383
384 {
385 Response: &configv1.SearchConfigurationResponse{
386 UpdatedOptions: []*configv1.ZoektIndexOptions{
387 {
388 Symbols: true,
389 },
390 },
391 },
392 IndexOptions: &IndexOptions{
393 Symbols: true,
394 Branches: []zoekt.RepositoryBranch{},
395 LanguageMap: map[string]ctags.CTagsParserType{},
396 },
397 },
398
399 {
400 Response: &configv1.SearchConfigurationResponse{
401 UpdatedOptions: []*configv1.ZoektIndexOptions{
402 {
403 RepoId: 123,
404 },
405 },
406 },
407 IndexOptions: &IndexOptions{
408 RepoID: 123,
409 Branches: []zoekt.RepositoryBranch{},
410 LanguageMap: map[string]ctags.CTagsParserType{},
411 },
412 },
413
414 {
415 Response: &configv1.SearchConfigurationResponse{
416 UpdatedOptions: []*configv1.ZoektIndexOptions{
417 {
418 Error: "boom",
419 },
420 },
421 },
422 },
423 }
424
425 for _, tc := range cases {
426 response = tc.Response
427
428 var got IndexOptions
429 var err error
430 sg.ForceIterateIndexOptions(func(o IndexOptions) {
431 got = o
432 }, func(_ uint32, e error) {
433 err = e
434 }, 123)
435
436 if err != nil && tc.IndexOptions != nil {
437 t.Fatalf("unexpected error: %v", err)
438 }
439 if tc.IndexOptions == nil {
440 continue
441 }
442
443 tc.IndexOptions.CloneURL = sg.getCloneURL(got.Name)
444
445 if d := cmp.Diff(*tc.IndexOptions, got); d != "" {
446 t.Errorf("mismatch (-want +got):\n%s", d)
447 }
448 }
449
450 // Special case our fingerprint API which doesn't return anything if the
451 // repo hasn't changed.
452 t.Run("unchanged", func(t *testing.T) {
453 response = &configv1.SearchConfigurationResponse{}
454
455 got := false
456 var err error
457 sg.ForceIterateIndexOptions(func(_ IndexOptions) {
458 got = true
459 }, func(_ uint32, e error) {
460 err = e
461 }, 123)
462 if err != nil {
463 t.Fatalf("unexpected error: %v", err)
464 }
465
466 if got {
467 t.Fatalf("expected no options, got %v", got)
468 }
469 })
470}
471
472func TestIndex(t *testing.T) {
473 cases := []struct {
474 name string
475 args indexArgs
476 mockRepositoryMetadata *zoekt.Repository
477 want []string
478 }{{
479 name: "minimal",
480 args: indexArgs{
481 IndexOptions: IndexOptions{
482 Name: "test/repo",
483 CloneURL: "http://api.test/.internal/git/test/repo",
484 Branches: []zoekt.RepositoryBranch{{Name: "HEAD", Version: "deadbeef"}},
485 TenantID: 42,
486 },
487 },
488 want: []string{
489 "git -c init.defaultBranch=nonExistentBranchBB0FOFCH32 init --bare $TMPDIR/test%2Frepo.git",
490 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Actor-UID: internal",
491 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Tenant-ID: 42",
492 "git -C $TMPDIR/test%2Frepo.git -c protocol.version=2 fetch --depth=1 --no-tags --filter=blob:limit=1048577 http://api.test/.internal/git/test/repo deadbeef",
493 "git -C $TMPDIR/test%2Frepo.git update-ref HEAD deadbeef",
494 "git -C $TMPDIR/test%2Frepo.git config zoekt.archived 0",
495 "git -C $TMPDIR/test%2Frepo.git config zoekt.fork 0",
496 "git -C $TMPDIR/test%2Frepo.git config zoekt.latestCommitDate 1",
497 "git -C $TMPDIR/test%2Frepo.git config zoekt.name test/repo",
498 "git -C $TMPDIR/test%2Frepo.git config zoekt.priority 0",
499 "git -C $TMPDIR/test%2Frepo.git config zoekt.public 0",
500 "git -C $TMPDIR/test%2Frepo.git config zoekt.repoid 0",
501 "git -C $TMPDIR/test%2Frepo.git config zoekt.tenantID 42",
502 "zoekt-git-index -submodules=false -incremental=false -branches HEAD -file_limit 1048576 -disable_ctags $TMPDIR/test%2Frepo.git",
503 },
504 }, {
505 name: "minimal-id",
506 args: indexArgs{
507 IndexOptions: IndexOptions{
508 Name: "test/repo",
509 CloneURL: "http://api.test/.internal/git/test/repo",
510 Branches: []zoekt.RepositoryBranch{{Name: "HEAD", Version: "deadbeef"}},
511 RepoID: 123,
512 TenantID: 1,
513 },
514 },
515 want: []string{
516 "git -c init.defaultBranch=nonExistentBranchBB0FOFCH32 init --bare $TMPDIR/test%2Frepo.git",
517 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Actor-UID: internal",
518 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Tenant-ID: 1",
519 "git -C $TMPDIR/test%2Frepo.git -c protocol.version=2 fetch --depth=1 --no-tags --filter=blob:limit=1048577 http://api.test/.internal/git/test/repo deadbeef",
520 "git -C $TMPDIR/test%2Frepo.git update-ref HEAD deadbeef",
521 "git -C $TMPDIR/test%2Frepo.git config zoekt.archived 0",
522 "git -C $TMPDIR/test%2Frepo.git config zoekt.fork 0",
523 "git -C $TMPDIR/test%2Frepo.git config zoekt.latestCommitDate 1",
524 "git -C $TMPDIR/test%2Frepo.git config zoekt.name test/repo",
525 "git -C $TMPDIR/test%2Frepo.git config zoekt.priority 0",
526 "git -C $TMPDIR/test%2Frepo.git config zoekt.public 0",
527 "git -C $TMPDIR/test%2Frepo.git config zoekt.repoid 123",
528 "git -C $TMPDIR/test%2Frepo.git config zoekt.tenantID 1",
529 "zoekt-git-index -submodules=false -incremental=false -branches HEAD -file_limit 1048576 -disable_ctags $TMPDIR/test%2Frepo.git",
530 },
531 }, {
532 name: "all",
533 args: indexArgs{
534 Incremental: true,
535 IndexDir: "/data/index",
536 Parallelism: 4,
537 IndexOptions: IndexOptions{
538 Name: "test/repo",
539 CloneURL: "http://api.test/.internal/git/test/repo",
540 LargeFiles: []string{"foo", "bar"},
541 Symbols: true,
542 Branches: []zoekt.RepositoryBranch{
543 {Name: "HEAD", Version: "deadbeef"},
544 {Name: "dev", Version: "feebdaed"}, // ignored for archive
545 },
546 TenantID: 1,
547 },
548 },
549 want: []string{
550 "git -c init.defaultBranch=nonExistentBranchBB0FOFCH32 init --bare $TMPDIR/test%2Frepo.git",
551 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Actor-UID: internal",
552 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Tenant-ID: 1",
553 "git -C $TMPDIR/test%2Frepo.git -c protocol.version=2 fetch --depth=1 --no-tags http://api.test/.internal/git/test/repo deadbeef feebdaed",
554 "git -C $TMPDIR/test%2Frepo.git update-ref HEAD deadbeef",
555 "git -C $TMPDIR/test%2Frepo.git update-ref refs/heads/dev feebdaed",
556 "git -C $TMPDIR/test%2Frepo.git config zoekt.archived 0",
557 "git -C $TMPDIR/test%2Frepo.git config zoekt.fork 0",
558 "git -C $TMPDIR/test%2Frepo.git config zoekt.latestCommitDate 1",
559 "git -C $TMPDIR/test%2Frepo.git config zoekt.name test/repo",
560 "git -C $TMPDIR/test%2Frepo.git config zoekt.priority 0",
561 "git -C $TMPDIR/test%2Frepo.git config zoekt.public 0",
562 "git -C $TMPDIR/test%2Frepo.git config zoekt.repoid 0",
563 "git -C $TMPDIR/test%2Frepo.git config zoekt.tenantID 1",
564 "zoekt-git-index -submodules=false -incremental=true -branches HEAD,dev " +
565 "-file_limit 1048576 -parallelism 4 -index /data/index -require_ctags -large_file foo -large_file bar " +
566 "$TMPDIR/test%2Frepo.git",
567 },
568 }, {
569 name: "delta",
570 args: indexArgs{
571 Incremental: true,
572 IndexDir: "/data/index",
573 Parallelism: 4,
574 UseDelta: true,
575 IndexOptions: IndexOptions{
576 RepoID: 0,
577 Name: "test/repo",
578 CloneURL: "http://api.test/.internal/git/test/repo",
579 LargeFiles: []string{"foo", "bar"},
580 Symbols: true,
581 Branches: []zoekt.RepositoryBranch{
582 {Name: "HEAD", Version: "deadbeef"},
583 {Name: "dev", Version: "feebdaed"},
584 {Name: "release", Version: "12345678"},
585 },
586 TenantID: 1,
587 },
588 DeltaShardNumberFallbackThreshold: 22,
589 },
590 mockRepositoryMetadata: &zoekt.Repository{
591 ID: 0,
592 Name: "test/repo",
593 Branches: []zoekt.RepositoryBranch{
594 {Name: "HEAD", Version: "oldhead"},
595 {Name: "dev", Version: "olddev"},
596 {Name: "release", Version: "oldrelease"},
597 },
598 },
599 want: []string{
600 "git -c init.defaultBranch=nonExistentBranchBB0FOFCH32 init --bare $TMPDIR/test%2Frepo.git",
601 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Actor-UID: internal",
602 "git -C $TMPDIR/test%2Frepo.git config --add http.extraHeader X-Sourcegraph-Tenant-ID: 1",
603 "git -C $TMPDIR/test%2Frepo.git -c protocol.version=2 fetch --depth=1 --no-tags http://api.test/.internal/git/test/repo deadbeef feebdaed 12345678 oldhead olddev oldrelease",
604 "git -C $TMPDIR/test%2Frepo.git update-ref HEAD deadbeef",
605 "git -C $TMPDIR/test%2Frepo.git update-ref refs/heads/dev feebdaed",
606 "git -C $TMPDIR/test%2Frepo.git update-ref refs/heads/release 12345678",
607 "git -C $TMPDIR/test%2Frepo.git config zoekt.archived 0",
608 "git -C $TMPDIR/test%2Frepo.git config zoekt.fork 0",
609 "git -C $TMPDIR/test%2Frepo.git config zoekt.latestCommitDate 1",
610 "git -C $TMPDIR/test%2Frepo.git config zoekt.name test/repo",
611 "git -C $TMPDIR/test%2Frepo.git config zoekt.priority 0",
612 "git -C $TMPDIR/test%2Frepo.git config zoekt.public 0",
613 "git -C $TMPDIR/test%2Frepo.git config zoekt.repoid 0",
614 "git -C $TMPDIR/test%2Frepo.git config zoekt.tenantID 1",
615 "zoekt-git-index -submodules=false -incremental=true -branches HEAD,dev,release " +
616 "-delta -delta_threshold 22 -file_limit 1048576 -parallelism 4 -index /data/index -require_ctags -large_file foo -large_file bar " +
617 "$TMPDIR/test%2Frepo.git",
618 },
619 }}
620
621 for _, tc := range cases {
622 t.Run(tc.name, func(t *testing.T) {
623 var got []string
624 runCmd := func(c *exec.Cmd) error {
625 if c.Env != nil {
626 t.Fatal("expected nil Env for command. Tenant Enforcement relies on us inheritting the parent process environment.")
627 }
628
629 cmd := strings.Join(c.Args, " ")
630 cmd = strings.ReplaceAll(cmd, filepath.Clean(os.TempDir()), "$TMPDIR")
631 got = append(got, cmd)
632 return nil
633 }
634
635 findRepositoryMetadata := func(args *indexArgs) (repository *zoekt.Repository, metadata *zoekt.IndexMetadata, ok bool, err error) {
636 if tc.mockRepositoryMetadata == nil {
637 return args.BuildOptions().FindRepositoryMetadata()
638 }
639
640 return tc.mockRepositoryMetadata, &zoekt.IndexMetadata{}, true, nil
641 }
642
643 c := gitIndexConfig{
644 runCmd: runCmd,
645 findRepositoryMetadata: findRepositoryMetadata,
646 }
647
648 if err := gitIndex(context.Background(), c, &tc.args, sourcegraphNop{}, logtest.Scoped(t)); err != nil {
649 t.Fatal(err)
650 }
651 if !cmp.Equal(got, tc.want) {
652 t.Errorf("git mismatch (-want +got):\n%s", cmp.Diff(tc.want, got, splitargs))
653 }
654 })
655 }
656}
657
658var splitargs = cmpopts.AcyclicTransformer("splitargs", func(cmd string) []string {
659 return strings.Split(cmd, " ")
660})
661
662type mockGRPCClient struct {
663 mockSearchConfiguration func(context.Context, *configv1.SearchConfigurationRequest, ...grpc.CallOption) (*configv1.SearchConfigurationResponse, error)
664 mockList func(context.Context, *configv1.ListRequest, ...grpc.CallOption) (*configv1.ListResponse, error)
665 mockUpdateIndexStatus func(context.Context, *configv1.UpdateIndexStatusRequest, ...grpc.CallOption) (*configv1.UpdateIndexStatusResponse, error)
666}
667
668func (m *mockGRPCClient) SearchConfiguration(ctx context.Context, in *configv1.SearchConfigurationRequest, opts ...grpc.CallOption) (*configv1.SearchConfigurationResponse, error) {
669 if m.mockSearchConfiguration != nil {
670 return m.mockSearchConfiguration(ctx, in, opts...)
671 }
672
673 return nil, fmt.Errorf("mock RPC SearchConfiguration not implemented")
674}
675
676func (m *mockGRPCClient) List(ctx context.Context, in *configv1.ListRequest, opts ...grpc.CallOption) (*configv1.ListResponse, error) {
677 if m.mockList != nil {
678 return m.mockList(ctx, in, opts...)
679 }
680
681 return nil, fmt.Errorf("mock RPC List not implemented")
682}
683
684func (m *mockGRPCClient) UpdateIndexStatus(ctx context.Context, in *configv1.UpdateIndexStatusRequest, opts ...grpc.CallOption) (*configv1.UpdateIndexStatusResponse, error) {
685 if m.mockUpdateIndexStatus != nil {
686 return m.mockUpdateIndexStatus(ctx, in, opts...)
687 }
688
689 return nil, fmt.Errorf("mock RPC UpdateIndexStatus not implemented")
690}
691
692var _ configv1.ZoektConfigurationServiceClient = &mockGRPCClient{}
693
694// Tests whether we can set git config values without error.
695func TestSetZoektConfig(t *testing.T) {
696 dir := t.TempDir()
697
698 // init git dir
699 script := `mkdir repo
700cd repo
701git init -b main
702`
703 cmd := exec.Command("/bin/sh", "-euxc", script)
704 cmd.Dir = dir
705 _, err := cmd.CombinedOutput()
706 require.NoError(t, err)
707
708 var out []byte
709 c := gitIndexConfig{
710 runCmd: func(cmd *exec.Cmd) error {
711 var err error
712 out, err = cmd.CombinedOutput()
713 return err
714 },
715 }
716
717 err = setZoektConfig(context.Background(), filepath.Join(dir, "repo"), &indexArgs{}, c)
718 require.NoError(t, err, string(out))
719}