Path: blob/main/components/content-service/pkg/git/git_test.go
2500 views
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package git56import (7"context"8"os"9"path/filepath"10"strings"11"testing"12"time"1314"github.com/google/go-cmp/cmp"15"golang.org/x/xerrors"16)1718const (19notEmpty = "not-empty"20)2122func TestGitStatus(t *testing.T) {23tests := []struct {24Name string25Prep func(context.Context, *Client) error26Result *Status27Error error28}{29{30"no commits",31func(ctx context.Context, c *Client) error {32if err := c.Git(ctx, "init"); err != nil {33return err34}35return nil36},37&Status{38porcelainStatus: porcelainStatus{39BranchOID: "(initial)",40BranchHead: "master",41},42},43nil,44},45{46"clean copy",47func(ctx context.Context, c *Client) error {48if err := initFromRemote(ctx, c); err != nil {49return err50}51return nil52},53&Status{54porcelainStatus: porcelainStatus{55BranchHead: "master",56BranchOID: notEmpty,57},58LatestCommit: notEmpty,59},60nil,61},62{63"untracked files",64func(ctx context.Context, c *Client) error {65if err := initFromRemote(ctx, c); err != nil {66return err67}68if err := os.WriteFile(filepath.Join(c.Location, "another-file"), []byte{}, 0755); err != nil {69return err70}71return nil72},73&Status{74porcelainStatus: porcelainStatus{75BranchHead: "master",76BranchOID: notEmpty,77UntrackedFiles: []string{"another-file"},78},79LatestCommit: notEmpty,80},81nil,82},83{84"uncommitted files",85func(ctx context.Context, c *Client) error {86if err := initFromRemote(ctx, c); err != nil {87return err88}89if err := os.WriteFile(filepath.Join(c.Location, "first-file"), []byte("foobar"), 0755); err != nil {90return err91}92return nil93},94&Status{95porcelainStatus: porcelainStatus{96BranchHead: "master",97BranchOID: notEmpty,98UncommitedFiles: []string{"first-file"},99},100LatestCommit: notEmpty,101},102nil,103},104{105"unpushed commits",106func(ctx context.Context, c *Client) error {107if err := initFromRemote(ctx, c); err != nil {108return err109}110if err := os.WriteFile(filepath.Join(c.Location, "first-file"), []byte("foobar"), 0755); err != nil {111return err112}113if err := c.Git(ctx, "commit", "-a", "-m", "foo"); err != nil {114return err115}116return nil117},118&Status{119porcelainStatus: porcelainStatus{120BranchHead: "master",121BranchOID: notEmpty,122},123UnpushedCommits: []string{notEmpty},124LatestCommit: notEmpty,125},126nil,127},128{129"unpushed commits in new branch",130func(ctx context.Context, c *Client) error {131if err := initFromRemote(ctx, c); err != nil {132return err133}134if err := c.Git(ctx, "checkout", "-b", "otherbranch"); err != nil {135return err136}137if err := os.WriteFile(filepath.Join(c.Location, "first-file"), []byte("foobar"), 0755); err != nil {138return err139}140if err := c.Git(ctx, "commit", "-a", "-m", "foo"); err != nil {141return err142}143return nil144},145&Status{146porcelainStatus: porcelainStatus{147BranchHead: "otherbranch",148BranchOID: notEmpty,149},150UnpushedCommits: []string{notEmpty},151LatestCommit: notEmpty,152},153nil,154},155156{157"pending in sub-dir files",158func(ctx context.Context, c *Client) error {159if err := initFromRemote(ctx, c); err != nil {160return err161}162if err := os.MkdirAll(filepath.Join(c.Location, "this/is/a/nested/test"), 0755); err != nil {163return err164}165if err := os.WriteFile(filepath.Join(c.Location, "this/is/a/nested/test/first-file"), []byte("foobar"), 0755); err != nil {166return err167}168return nil169},170&Status{171porcelainStatus: porcelainStatus{172BranchHead: "master",173BranchOID: notEmpty,174UntrackedFiles: []string{"this/is/a/nested/test/first-file"},175},176LatestCommit: notEmpty,177},178nil,179},180}181182for _, test := range tests {183t.Run(test.Name, func(t *testing.T) {184ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)185defer cancel()186187client, err := newGitClient(ctx)188if err != nil {189t.Errorf("cannot prep %s: %v", test.Name, err)190return191}192193err = test.Prep(ctx, client)194if err != nil {195t.Errorf("cannot prep %s: %v", test.Name, err)196return197}198199status, err := client.Status(ctx)200if err != test.Error {201t.Errorf("expected error does not match for %s: %v != %v", test.Name, err, test.Error)202return203}204205if status != nil {206if test.Result.BranchOID == notEmpty && status.LatestCommit != "" {207test.Result.BranchOID = status.LatestCommit208}209if test.Result.LatestCommit == notEmpty && status.LatestCommit != "" {210test.Result.LatestCommit = status.LatestCommit211}212for _, c := range test.Result.UnpushedCommits {213if c == notEmpty {214if len(status.UnpushedCommits) == 0 {215t.Errorf("expected unpushed commits")216}217218test.Result.UnpushedCommits = status.UnpushedCommits219break220}221}222}223224if diff := cmp.Diff(test.Result, status, cmp.AllowUnexported(Status{})); diff != "" {225t.Errorf("unexpected status (-want +got):\n%s", diff)226}227})228}229}230231func TestGitStatusFromFiles(t *testing.T) {232tests := []struct {233Name string234Prep func(context.Context, *Client) error235Result *Status236Error error237}{238{239"no commits",240func(ctx context.Context, c *Client) error {241if err := c.Git(ctx, "init"); err != nil {242return err243}244return nil245},246&Status{247porcelainStatus: porcelainStatus{248BranchOID: "(initial)",249BranchHead: "master",250},251},252nil,253},254{255"clean copy",256func(ctx context.Context, c *Client) error {257if err := initFromRemote(ctx, c); err != nil {258return err259}260return nil261},262&Status{263porcelainStatus: porcelainStatus{264BranchHead: "master",265BranchOID: notEmpty,266},267LatestCommit: notEmpty,268},269nil,270},271{272"untracked files",273func(ctx context.Context, c *Client) error {274if err := initFromRemote(ctx, c); err != nil {275return err276}277if err := os.WriteFile(filepath.Join(c.Location, "another-file"), []byte{}, 0755); err != nil {278return err279}280return nil281},282&Status{283porcelainStatus: porcelainStatus{284BranchHead: "master",285BranchOID: notEmpty,286UntrackedFiles: []string{"another-file"},287},288LatestCommit: notEmpty,289},290nil,291},292{293"uncommitted files",294func(ctx context.Context, c *Client) error {295if err := initFromRemote(ctx, c); err != nil {296return err297}298if err := os.WriteFile(filepath.Join(c.Location, "first-file"), []byte("foobar"), 0755); err != nil {299return err300}301return nil302},303&Status{304porcelainStatus: porcelainStatus{305BranchHead: "master",306BranchOID: notEmpty,307UncommitedFiles: []string{"first-file"},308},309LatestCommit: notEmpty,310},311nil,312},313{314"unpushed commits",315func(ctx context.Context, c *Client) error {316if err := initFromRemote(ctx, c); err != nil {317return err318}319if err := os.WriteFile(filepath.Join(c.Location, "first-file"), []byte("foobar"), 0755); err != nil {320return err321}322if err := c.Git(ctx, "commit", "-a", "-m", "foo"); err != nil {323return err324}325return nil326},327&Status{328porcelainStatus: porcelainStatus{329BranchHead: "master",330BranchOID: notEmpty,331},332UnpushedCommits: []string{notEmpty},333LatestCommit: notEmpty,334},335nil,336},337{338"unpushed commits in new branch",339func(ctx context.Context, c *Client) error {340if err := initFromRemote(ctx, c); err != nil {341return err342}343if err := c.Git(ctx, "checkout", "-b", "otherbranch"); err != nil {344return err345}346if err := os.WriteFile(filepath.Join(c.Location, "first-file"), []byte("foobar"), 0755); err != nil {347return err348}349if err := c.Git(ctx, "commit", "-a", "-m", "foo"); err != nil {350return err351}352return nil353},354&Status{355porcelainStatus: porcelainStatus{356BranchHead: "otherbranch",357BranchOID: notEmpty,358},359UnpushedCommits: []string{notEmpty},360LatestCommit: notEmpty,361},362nil,363},364365{366"pending in sub-dir files",367func(ctx context.Context, c *Client) error {368if err := initFromRemote(ctx, c); err != nil {369return err370}371if err := os.MkdirAll(filepath.Join(c.Location, "this/is/a/nested/test"), 0755); err != nil {372return err373}374if err := os.WriteFile(filepath.Join(c.Location, "this/is/a/nested/test/first-file"), []byte("foobar"), 0755); err != nil {375return err376}377return nil378},379&Status{380porcelainStatus: porcelainStatus{381BranchHead: "master",382BranchOID: notEmpty,383UntrackedFiles: []string{"this/is/a/nested/test/first-file"},384},385LatestCommit: notEmpty,386},387nil,388},389}390391for _, test := range tests {392t.Run(test.Name, func(t *testing.T) {393ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)394defer cancel()395396statusLocation, err := os.MkdirTemp("", "git-status")397if err != nil {398t.Errorf("cannot create temporal directory: %v", err)399return400}401402defer func() {403os.RemoveAll(statusLocation)404}()405406client, err := newGitClient(ctx)407if err != nil {408t.Errorf("cannot prep %s: %v", test.Name, err)409return410}411412err = test.Prep(ctx, client)413if err != nil {414t.Errorf("cannot prep %s: %v", test.Name, err)415return416}417418gitout, err := client.GitWithOutput(ctx, nil, "status", "--porcelain=v2", "--branch", "-uall")419if err != nil {420t.Errorf("error calling GitWithOutput: %v", err)421return422}423if err := os.WriteFile(filepath.Join(statusLocation, "git_status.txt"), gitout, 0755); err != nil {424t.Errorf("error creating file: %v", err)425return426}427428gitout, err = client.GitWithOutput(ctx, &errNoCommitsYet, "log", "--pretty=%h: %s", "--branches", "--not", "--remotes")429if err != nil {430t.Errorf("error calling GitWithOutput: %v", err)431return432}433if err := os.WriteFile(filepath.Join(statusLocation, "git_log_1.txt"), gitout, 0755); err != nil {434t.Errorf("error creating file: %v", err)435return436}437438gitout, err = client.GitWithOutput(ctx, &errNoCommitsYet, "log", "--pretty=%H", "-n", "1")439if err != nil && !strings.Contains(err.Error(), "fatal: your current branch 'master' does not have any commits yet") {440t.Errorf("error calling GitWithOutput: %v", err)441return442}443if err := os.WriteFile(filepath.Join(statusLocation, "git_log_2.txt"), gitout, 0755); err != nil {444t.Errorf("error creating file: %v", err)445return446}447448status, err := GitStatusFromFiles(ctx, statusLocation)449if err != test.Error {450t.Errorf("expected error does not match for %s: %v != %v", test.Name, err, test.Error)451return452}453454if status != nil {455if test.Result.BranchOID == notEmpty && status.LatestCommit != "" {456test.Result.BranchOID = status.LatestCommit457}458if test.Result.LatestCommit == notEmpty && status.LatestCommit != "" {459test.Result.LatestCommit = status.LatestCommit460}461for _, c := range test.Result.UnpushedCommits {462if c == notEmpty {463if len(status.UnpushedCommits) == 0 {464t.Errorf("expected unpushed commits")465}466467test.Result.UnpushedCommits = status.UnpushedCommits468break469}470}471}472473if diff := cmp.Diff(test.Result, status, cmp.AllowUnexported(Status{})); diff != "" {474t.Errorf("unexpected status (-want +got):\n%s", diff)475}476477})478}479}480481func newGitClient(ctx context.Context) (*Client, error) {482loc, err := os.MkdirTemp("", "gittest")483if err != nil {484return nil, err485}486487return &Client{488Location: loc,489Config: map[string]string{490"user.email": "[email protected]",491"user.name": "tester",492},493}, nil494}495496func initFromRemote(ctx context.Context, c *Client) error {497remote, err := newGitClient(ctx)498if err != nil {499return xerrors.Errorf("cannot add remote: %w", err)500}501if err := remote.Git(ctx, "init"); err != nil {502return err503}504if err := remote.Git(ctx, "config", "--local", "user.email", "[email protected]"); err != nil {505return err506}507if err := remote.Git(ctx, "config", "--local", "user.name", "foo bar"); err != nil {508return err509}510if err := os.WriteFile(filepath.Join(remote.Location, "first-file"), []byte{}, 0755); err != nil {511return err512}513if err := remote.Git(ctx, "add", "first-file"); err != nil {514return err515}516if err := remote.Git(ctx, "commit", "-m", "foo"); err != nil {517return err518}519520c.RemoteURI = remote.Location521if err := c.Clone(ctx); err != nil {522return err523}524if err := c.Git(ctx, "config", "--local", "user.email", "[email protected]"); err != nil {525return err526}527if err := c.Git(ctx, "config", "--local", "user.name", "foo bar"); err != nil {528return err529}530531return nil532}533534535