package commit import ( "context" "io" "testing" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/internal/git" "gitlab.com/gitlab-org/gitaly/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/internal/gitaly/service/ref" "gitlab.com/gitlab-org/gitaly/internal/testhelper" "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb" "google.golang.org/grpc/codes" ) var ( defaultFiles = [][]byte{ []byte(".gitattributes"), []byte(".gitignore"), []byte(".gitmodules"), []byte("CHANGELOG"), []byte("CONTRIBUTING.md"), []byte("Gemfile.zip"), []byte("LICENSE"), []byte("MAINTENANCE.md"), []byte("PROCESS.md"), []byte("README"), []byte("README.md"), []byte("VERSION"), []byte("bar/branch-test.txt"), []byte("custom-highlighting/test.gitlab-custom"), []byte("encoding/feature-1.txt"), []byte("encoding/feature-2.txt"), []byte("encoding/hotfix-1.txt"), []byte("encoding/hotfix-2.txt"), []byte("encoding/iso8859.txt"), []byte("encoding/russian.rb"), []byte("encoding/test.txt"), []byte("encoding/テスト.txt"), []byte("encoding/テスト.xls"), []byte("files/html/500.html"), []byte("files/images/6049019_460s.jpg"), []byte("files/images/logo-black.png"), []byte("files/images/logo-white.png"), []byte("files/images/wm.svg"), []byte("files/js/application.js"), []byte("files/js/commit.coffee"), []byte("files/lfs/lfs_object.iso"), []byte("files/markdown/ruby-style-guide.md"), []byte("files/ruby/popen.rb"), []byte("files/ruby/regex.rb"), []byte("files/ruby/version_info.rb"), []byte("files/whitespace"), []byte("foo/bar/.gitkeep"), []byte("gitaly/file-with-multiple-chunks"), []byte("gitaly/logo-white.png"), []byte("gitaly/mode-file"), []byte("gitaly/mode-file-with-mods"), []byte("gitaly/no-newline-at-the-end"), []byte("gitaly/renamed-file"), []byte("gitaly/renamed-file-with-mods"), []byte("gitaly/symlink-to-be-regular"), []byte("gitaly/tab\tnewline\n file"), []byte("gitaly/テスト.txt"), []byte("with space/README.md"), } ) func TestListFiles_success(t *testing.T) { defaultBranchName = func(context.Context, git.CommandFactory, *gitalypb.Repository) ([]byte, error) { return []byte("test-do-not-touch"), nil } defer func() { defaultBranchName = ref.DefaultBranchName }() _, repo, _, client := setupCommitServiceWithRepo(t, true) tests := []struct { desc string revision string files [][]byte }{ { desc: "valid object ID", revision: "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51", files: [][]byte{ []byte(".gitignore"), []byte(".gitmodules"), []byte("CHANGELOG"), []byte("CONTRIBUTING.md"), []byte("Gemfile.zip"), []byte("LICENSE"), []byte("MAINTENANCE.md"), []byte("PROCESS.md"), []byte("README"), []byte("README.md"), []byte("VERSION"), []byte("encoding/feature-1.txt"), []byte("encoding/feature-2.txt"), []byte("encoding/hotfix-1.txt"), []byte("encoding/hotfix-2.txt"), []byte("encoding/iso8859.txt"), []byte("encoding/russian.rb"), []byte("encoding/test.txt"), []byte("encoding/テスト.txt"), []byte("encoding/テスト.xls"), []byte("files/html/500.html"), []byte("files/images/6049019_460s.jpg"), []byte("files/images/logo-black.png"), []byte("files/images/logo-white.png"), []byte("files/images/wm.svg"), []byte("files/js/application.js"), []byte("files/js/commit.js.coffee"), []byte("files/lfs/lfs_object.iso"), []byte("files/markdown/ruby-style-guide.md"), []byte("files/ruby/popen.rb"), []byte("files/ruby/regex.rb"), []byte("files/ruby/version_info.rb"), []byte("files/whitespace"), []byte("foo/bar/.gitkeep"), }, }, { desc: "valid branch", revision: "test-do-not-touch", files: defaultFiles, }, { desc: "valid fully qualified branch", revision: "refs/heads/test-do-not-touch", files: defaultFiles, }, { desc: "missing object ID uses default branch", revision: "", files: defaultFiles, }, { desc: "invalid object ID", revision: "1234123412341234", files: [][]byte{}, }, { desc: "invalid branch", revision: "non/existing", files: [][]byte{}, }, { desc: "nonexisting fully qualified branch", revision: "refs/heads/foobar", files: [][]byte{}, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { rpcRequest := gitalypb.ListFilesRequest{ Repository: repo, Revision: []byte(tc.revision), } ctx, cancel := testhelper.Context() defer cancel() c, err := client.ListFiles(ctx, &rpcRequest) require.NoError(t, err) var files [][]byte for { resp, err := c.Recv() if err == io.EOF { break } require.NoError(t, err) files = append(files, resp.GetPaths()...) } require.ElementsMatch(t, files, tc.files) }) } } func TestListFiles_unbornBranch(t *testing.T) { cfg, _, _, client := setupCommitServiceWithRepo(t, true) repo, _, _ := gittest.InitBareRepoAt(t, cfg.Storages[0]) tests := []struct { desc string revision string code codes.Code }{ { desc: "HEAD", revision: "HEAD", }, { desc: "unborn branch", revision: "refs/heads/master", }, { desc: "nonexisting branch", revision: "i-dont-exist", }, { desc: "nonexisting fully qualified branch", revision: "refs/heads/i-dont-exist", }, { desc: "missing revision without default branch", revision: "", code: codes.FailedPrecondition, }, { desc: "valid object ID", revision: "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51", }, { desc: "invalid object ID", revision: "1234123412341234", }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { rpcRequest := gitalypb.ListFilesRequest{ Repository: repo, Revision: []byte(tc.revision), } ctx, cancel := testhelper.Context() defer cancel() c, err := client.ListFiles(ctx, &rpcRequest) require.NoError(t, err) var files [][]byte for { var resp *gitalypb.ListFilesResponse resp, err = c.Recv() if err != nil { break } require.NoError(t, err) files = append(files, resp.GetPaths()...) } if tc.code != codes.OK { testhelper.RequireGrpcError(t, err, tc.code) } else { require.Equal(t, err, io.EOF) } require.Empty(t, files) }) } } func TestListFiles_failure(t *testing.T) { _, _, _, client := setupCommitServiceWithRepo(t, true) tests := []struct { desc string repo *gitalypb.Repository code codes.Code }{ { desc: "nil repo", repo: nil, code: codes.InvalidArgument, }, { desc: "empty repo object", repo: &gitalypb.Repository{}, code: codes.InvalidArgument, }, { desc: "non-existing repo", repo: &gitalypb.Repository{StorageName: "foo", RelativePath: "bar"}, code: codes.InvalidArgument, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { rpcRequest := gitalypb.ListFilesRequest{ Repository: tc.repo, Revision: []byte("master"), } ctx, cancel := testhelper.Context() defer cancel() c, err := client.ListFiles(ctx, &rpcRequest) require.NoError(t, err) err = drainListFilesResponse(c) testhelper.RequireGrpcError(t, err, tc.code) }) } } func drainListFilesResponse(c gitalypb.CommitService_ListFilesClient) error { var err error for err == nil { _, err = c.Recv() } if err == io.EOF { return nil } return err } func TestListFiles_invalidRevision(t *testing.T) { _, repo, _, client := setupCommitServiceWithRepo(t, true) ctx, cancel := testhelper.Context() defer cancel() stream, err := client.ListFiles(ctx, &gitalypb.ListFilesRequest{ Repository: repo, Revision: []byte("--output=/meow"), }) require.NoError(t, err) _, err = stream.Recv() testhelper.RequireGrpcError(t, err, codes.InvalidArgument) }