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