fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

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