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