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