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/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}