From 54518abe7697ec2b2a70c32061a9d6f3996aaa40 Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sat, 4 Jun 2022 18:44:11 +0900 Subject: [PATCH 01/10] test: rename package to bump_test and exrpot stuffs for test --- bump/bumper.go | 12 +++++------- bump/bumper_test.go | 23 ++++++++++++----------- bump/export_test.go | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 bump/export_test.go diff --git a/bump/bumper.go b/bump/bumper.go index d0712f6..b73c81a 100644 --- a/bump/bumper.go +++ b/bump/bumper.go @@ -7,8 +7,6 @@ import ( "github.com/Masterminds/semver/v3" "github.com/manifoldco/promptui" - - "github.com/johnmanjiro13/gh-bump/cmd" ) type Gh interface { @@ -43,7 +41,7 @@ type bumper struct { title string } -func New(gh Gh) cmd.Bumper { +func New(gh Gh) *bumper { return &bumper{gh: gh} } @@ -109,13 +107,13 @@ func (b *bumper) Bump() error { if isInitial { nextVer = current } else { - nextVer, err = b.nextVersion(current) + nextVer, err = nextVersion(current) if err != nil { return err } } - ok, err := b.approve(nextVer) + ok, err := approve(nextVer) if err != nil { return err } @@ -194,7 +192,7 @@ func newVersion() (*semver.Version, error) { return semver.NewVersion(result) } -func (b *bumper) nextVersion(current *semver.Version) (*semver.Version, error) { +func nextVersion(current *semver.Version) (*semver.Version, error) { prompt := promptui.Select{ Label: fmt.Sprintf("Select next version. current: %s", current.Original()), Items: []string{"patch", "minor", "major"}, @@ -218,7 +216,7 @@ func (b *bumper) nextVersion(current *semver.Version) (*semver.Version, error) { return &next, nil } -func (b *bumper) approve(next *semver.Version) (bool, error) { +func approve(next *semver.Version) (bool, error) { validate := func(input string) error { if input != "y" && input != "yes" && input != "n" && input != "no" { return fmt.Errorf("invalid character. press y/n") diff --git a/bump/bumper_test.go b/bump/bumper_test.go index 568240f..deef777 100644 --- a/bump/bumper_test.go +++ b/bump/bumper_test.go @@ -1,10 +1,11 @@ -package bump +package bump_test import ( "bytes" "fmt" "testing" + "github.com/johnmanjiro13/gh-bump/bump" "github.com/stretchr/testify/assert" ) @@ -30,7 +31,7 @@ func (g *mockGh) ViewRelease(repo string, isCurrent bool) (sout, eout bytes.Buff return } -func (g *mockGh) CreateRelease(version string, repo string, isCurrent bool, option *ReleaseOption) (sout, eout bytes.Buffer, err error) { +func (g *mockGh) CreateRelease(version string, repo string, isCurrent bool, option *bump.ReleaseOption) (sout, eout bytes.Buffer, err error) { sout.WriteString(version) return } @@ -55,32 +56,32 @@ func TestBumper_WithRepository(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { - b := &bumper{gh: &mockGh{}} + b := bump.New(&mockGh{}) assert.NoError(t, b.WithRepository(tt.repository)) - assert.Equal(t, tt.wantRepository, b.repository) - assert.Equal(t, tt.wantIsCurrent, b.isCurrent) + assert.Equal(t, tt.wantRepository, b.Repository()) + assert.Equal(t, tt.wantIsCurrent, b.IsCurrent()) }) } } func TestBumper_ResolveRepository(t *testing.T) { - b := &bumper{gh: &mockGh{}} - got, err := b.resolveRepository() + b := bump.New(&mockGh{}) + got, err := bump.ResolveRepository(b) assert.NoError(t, err) assert.Equal(t, "johnmanjiro13/gh-bump", got) } func TestBumper_listReleases(t *testing.T) { - b := &bumper{gh: &mockGh{}} - got, err := b.listReleases() + b := bump.New(&mockGh{}) + got, err := bump.ListReleases(b) assert.NoError(t, err) assert.Equal(t, fmt.Sprintf("Tags:\n%s", tagList), got) } func TestBumper_createRelease(t *testing.T) { - b := &bumper{gh: &mockGh{}} - got, err := b.createRelease("v1.0.0") + b := bump.New(&mockGh{}) + got, err := bump.CreateRelease(b, "v1.0.0") assert.NoError(t, err) assert.Equal(t, "v1.0.0", got) } diff --git a/bump/export_test.go b/bump/export_test.go new file mode 100644 index 0000000..b620136 --- /dev/null +++ b/bump/export_test.go @@ -0,0 +1,15 @@ +package bump + +var ( + ResolveRepository = (*bumper).resolveRepository + ListReleases = (*bumper).listReleases + CreateRelease = (*bumper).createRelease +) + +func (b *bumper) Repository() string { + return b.repository +} + +func (b *bumper) IsCurrent() bool { + return b.isCurrent +} From a371578e817c4e223af1feb1a69b73ac2b458ebb Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sat, 4 Jun 2022 19:07:43 +0900 Subject: [PATCH 02/10] fix: return pointer type --- bump/bumper.go | 8 ++++---- gh/gh.go | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bump/bumper.go b/bump/bumper.go index b73c81a..6a34cf5 100644 --- a/bump/bumper.go +++ b/bump/bumper.go @@ -10,10 +10,10 @@ import ( ) type Gh interface { - ViewRepository() (sout, eout bytes.Buffer, err error) - ListRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, err error) - ViewRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, err error) - CreateRelease(version string, repo string, isCurrent bool, option *ReleaseOption) (sout, eout bytes.Buffer, err error) + ViewRepository() (sout, eout *bytes.Buffer, err error) + ListRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) + ViewRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) + CreateRelease(version string, repo string, isCurrent bool, option *ReleaseOption) (sout, eout *bytes.Buffer, err error) } type ReleaseOption struct { diff --git a/gh/gh.go b/gh/gh.go index e518e25..aef9639 100644 --- a/gh/gh.go +++ b/gh/gh.go @@ -12,15 +12,15 @@ import ( type gh struct{} -func New() bump.Gh { +func New() *gh { return &gh{} } -func (g *gh) ViewRepository() (sout, eout bytes.Buffer, err error) { +func (g *gh) ViewRepository() (sout, eout *bytes.Buffer, err error) { return runGh("repo", "view") } -func (g *gh) ListRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, err error) { +func (g *gh) ListRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) { if isCurrent { sout, eout, err = runGh("release", "list") } else { @@ -29,7 +29,7 @@ func (g *gh) ListRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, return } -func (g *gh) ViewRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, err error) { +func (g *gh) ViewRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) { if isCurrent { sout, eout, err = runGh("release", "view") } else { @@ -38,7 +38,7 @@ func (g *gh) ViewRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, return } -func (g *gh) CreateRelease(version string, repo string, isCurrent bool, option *bump.ReleaseOption) (sout, eout bytes.Buffer, err error) { +func (g *gh) CreateRelease(version string, repo string, isCurrent bool, option *bump.ReleaseOption) (sout, eout *bytes.Buffer, err error) { args := []string{"release", "create", version} if !isCurrent { args = append(args, []string{"-R", repo}...) @@ -71,7 +71,7 @@ func (g *gh) CreateRelease(version string, repo string, isCurrent bool, option * return } -func runGh(args ...string) (sout, eout bytes.Buffer, err error) { +func runGh(args ...string) (sout, eout *bytes.Buffer, err error) { ghBin, err := safeexec.LookPath("gh") if err != nil { err = fmt.Errorf("could not find gh. err: %w", err) @@ -79,8 +79,8 @@ func runGh(args ...string) (sout, eout bytes.Buffer, err error) { } cmd := exec.Command(ghBin, args...) - cmd.Stdout = &sout - cmd.Stderr = &eout + cmd.Stdout = sout + cmd.Stderr = eout err = cmd.Run() if err != nil { From a8c4ef5968d2a87fe7da2107f5f4bdd5df2900bb Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sat, 4 Jun 2022 19:07:47 +0900 Subject: [PATCH 03/10] test: test bumper with gomock --- Makefile | 7 +++ bump/bumper_test.go | 64 +++++++++++++---------- bump/mock_bump/mock_bump.go | 100 ++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 24 +++++++++ 5 files changed, 169 insertions(+), 27 deletions(-) create mode 100644 Makefile create mode 100644 bump/mock_bump/mock_bump.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..081ba32 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: test + +test: + go test -cover ./... + +mockgen: + mockgen -destination bump/mock_bump/mock_bump.go github.com/johnmanjiro13/gh-bump/bump Gh diff --git a/bump/bumper_test.go b/bump/bumper_test.go index deef777..306c280 100644 --- a/bump/bumper_test.go +++ b/bump/bumper_test.go @@ -5,8 +5,11 @@ import ( "fmt" "testing" - "github.com/johnmanjiro13/gh-bump/bump" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + + "github.com/johnmanjiro13/gh-bump/bump" + "github.com/johnmanjiro13/gh-bump/bump/mock_bump" ) const ( @@ -15,27 +18,6 @@ description: gh extension for bumping version of a repository` tagList = `v0.2.1 Latest v0.2.1 2021-12-08T04:19:16Z` ) -type mockGh struct{} - -func (g *mockGh) ViewRepository() (sout, eout bytes.Buffer, err error) { - sout.WriteString(repoDocs) - return -} - -func (g *mockGh) ListRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, err error) { - sout.WriteString(tagList) - return -} - -func (g *mockGh) ViewRelease(repo string, isCurrent bool) (sout, eout bytes.Buffer, err error) { - return -} - -func (g *mockGh) CreateRelease(version string, repo string, isCurrent bool, option *bump.ReleaseOption) (sout, eout bytes.Buffer, err error) { - sout.WriteString(version) - return -} - func TestBumper_WithRepository(t *testing.T) { tests := map[string]struct { repository string @@ -54,9 +36,15 @@ func TestBumper_WithRepository(t *testing.T) { }, } + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), &bytes.Buffer{}, nil) + for name, tt := range tests { t.Run(name, func(t *testing.T) { - b := bump.New(&mockGh{}) + b := bump.New(gh) assert.NoError(t, b.WithRepository(tt.repository)) assert.Equal(t, tt.wantRepository, b.Repository()) @@ -66,22 +54,44 @@ func TestBumper_WithRepository(t *testing.T) { } func TestBumper_ResolveRepository(t *testing.T) { - b := bump.New(&mockGh{}) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), &bytes.Buffer{}, nil) + + b := bump.New(gh) got, err := bump.ResolveRepository(b) assert.NoError(t, err) assert.Equal(t, "johnmanjiro13/gh-bump", got) } func TestBumper_listReleases(t *testing.T) { - b := bump.New(&mockGh{}) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + gh.EXPECT().ListRelease(b.Repository(), b.IsCurrent()). + Return(bytes.NewBufferString(tagList), &bytes.Buffer{}, nil) + got, err := bump.ListReleases(b) assert.NoError(t, err) assert.Equal(t, fmt.Sprintf("Tags:\n%s", tagList), got) } func TestBumper_createRelease(t *testing.T) { - b := bump.New(&mockGh{}) - got, err := bump.CreateRelease(b, "v1.0.0") + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + + const version = "v1.0.0" + gh.EXPECT().CreateRelease(version, b.Repository(), b.IsCurrent(), &bump.ReleaseOption{}). + Return(bytes.NewBufferString(version), &bytes.Buffer{}, nil) + + got, err := bump.CreateRelease(b, version) assert.NoError(t, err) assert.Equal(t, "v1.0.0", got) } diff --git a/bump/mock_bump/mock_bump.go b/bump/mock_bump/mock_bump.go new file mode 100644 index 0000000..ab9e060 --- /dev/null +++ b/bump/mock_bump/mock_bump.go @@ -0,0 +1,100 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/johnmanjiro13/gh-bump/bump (interfaces: Gh) + +// Package mock_bump is a generated GoMock package. +package mock_bump + +import ( + bytes "bytes" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + bump "github.com/johnmanjiro13/gh-bump/bump" +) + +// MockGh is a mock of Gh interface. +type MockGh struct { + ctrl *gomock.Controller + recorder *MockGhMockRecorder +} + +// MockGhMockRecorder is the mock recorder for MockGh. +type MockGhMockRecorder struct { + mock *MockGh +} + +// NewMockGh creates a new mock instance. +func NewMockGh(ctrl *gomock.Controller) *MockGh { + mock := &MockGh{ctrl: ctrl} + mock.recorder = &MockGhMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGh) EXPECT() *MockGhMockRecorder { + return m.recorder +} + +// CreateRelease mocks base method. +func (m *MockGh) CreateRelease(arg0, arg1 string, arg2 bool, arg3 *bump.ReleaseOption) (*bytes.Buffer, *bytes.Buffer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateRelease", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*bytes.Buffer) + ret1, _ := ret[1].(*bytes.Buffer) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// CreateRelease indicates an expected call of CreateRelease. +func (mr *MockGhMockRecorder) CreateRelease(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRelease", reflect.TypeOf((*MockGh)(nil).CreateRelease), arg0, arg1, arg2, arg3) +} + +// ListRelease mocks base method. +func (m *MockGh) ListRelease(arg0 string, arg1 bool) (*bytes.Buffer, *bytes.Buffer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListRelease", arg0, arg1) + ret0, _ := ret[0].(*bytes.Buffer) + ret1, _ := ret[1].(*bytes.Buffer) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListRelease indicates an expected call of ListRelease. +func (mr *MockGhMockRecorder) ListRelease(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRelease", reflect.TypeOf((*MockGh)(nil).ListRelease), arg0, arg1) +} + +// ViewRelease mocks base method. +func (m *MockGh) ViewRelease(arg0 string, arg1 bool) (*bytes.Buffer, *bytes.Buffer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ViewRelease", arg0, arg1) + ret0, _ := ret[0].(*bytes.Buffer) + ret1, _ := ret[1].(*bytes.Buffer) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ViewRelease indicates an expected call of ViewRelease. +func (mr *MockGhMockRecorder) ViewRelease(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewRelease", reflect.TypeOf((*MockGh)(nil).ViewRelease), arg0, arg1) +} + +// ViewRepository mocks base method. +func (m *MockGh) ViewRepository() (*bytes.Buffer, *bytes.Buffer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ViewRepository") + ret0, _ := ret[0].(*bytes.Buffer) + ret1, _ := ret[1].(*bytes.Buffer) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ViewRepository indicates an expected call of ViewRepository. +func (mr *MockGhMockRecorder) ViewRepository() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewRepository", reflect.TypeOf((*MockGh)(nil).ViewRepository)) +} diff --git a/go.mod b/go.mod index 7f5a21a..ea0cde4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/Masterminds/semver/v3 v3.1.1 github.com/cli/safeexec v1.0.0 + github.com/golang/mock v1.6.0 github.com/manifoldco/promptui v0.9.0 github.com/spf13/cobra v1.4.0 github.com/stretchr/testify v1.7.1 diff --git a/go.sum b/go.sum index 7f8a2bf..a137db9 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= @@ -33,9 +35,31 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From dad585eea273aeb9ff0fa5c7bfa6d2f29a43f23f Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sat, 4 Jun 2022 19:37:06 +0900 Subject: [PATCH 04/10] test: add test for with options --- bump/bumper_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++ bump/export_test.go | 32 +++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/bump/bumper_test.go b/bump/bumper_test.go index 306c280..c3a0307 100644 --- a/bump/bumper_test.go +++ b/bump/bumper_test.go @@ -53,6 +53,94 @@ func TestBumper_WithRepository(t *testing.T) { } } +func TestBumper_WithDraft(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithDraft() + + assert.True(t, b.IsDraft()) +} + +func TestBumper_WithPrerelease(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithPrerelease() + + assert.True(t, b.IsPrerelease()) +} + +func TestBumper_WithDiscussionCategory(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithDiscussionCategory("test") + + assert.Equal(t, "test", b.DiscussionCategory()) +} + +func TestBumper_WithGenerateNotes(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithGenerateNotes() + + assert.True(t, b.GenerateNotes()) +} + +func TestBumper_WithNotes(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithNotes("note") + + assert.Equal(t, "note", b.Notes()) +} + +func TestBumper_WithNotesFile(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithNotesFile("filename") + + assert.Equal(t, "filename", b.NotesFilename()) +} + +func TestBumper_WithTarget(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithTarget("target") + + assert.Equal(t, "target", b.Target()) +} + +func TestBumper_WithTitle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + b.WithTitle("title") + + assert.Equal(t, "title", b.Title()) +} + func TestBumper_ResolveRepository(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/bump/export_test.go b/bump/export_test.go index b620136..5bf720e 100644 --- a/bump/export_test.go +++ b/bump/export_test.go @@ -13,3 +13,35 @@ func (b *bumper) Repository() string { func (b *bumper) IsCurrent() bool { return b.isCurrent } + +func (b *bumper) IsDraft() bool { + return b.isDraft +} + +func (b *bumper) IsPrerelease() bool { + return b.isPrerelease +} + +func (b *bumper) DiscussionCategory() string { + return b.discussionCategory +} + +func (b *bumper) GenerateNotes() bool { + return b.generateNotes +} + +func (b *bumper) Notes() string { + return b.notes +} + +func (b *bumper) NotesFilename() string { + return b.notesFilename +} + +func (b *bumper) Target() string { + return b.target +} + +func (b *bumper) Title() string { + return b.title +} From 6952572e21d8f4fe4e6272cebcae0bdae8251eeb Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sat, 4 Jun 2022 19:58:15 +0900 Subject: [PATCH 05/10] test: test increment version --- bump/bumper.go | 4 +++ bump/bumper_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++--- bump/export_test.go | 2 ++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/bump/bumper.go b/bump/bumper.go index 6a34cf5..8a17ffd 100644 --- a/bump/bumper.go +++ b/bump/bumper.go @@ -202,6 +202,10 @@ func nextVersion(current *semver.Version) (*semver.Version, error) { return nil, fmt.Errorf("failed to prompt. err: %w", err) } + return incrementVersion(current, bumpType) +} + +func incrementVersion(current *semver.Version, bumpType string) (*semver.Version, error) { var next semver.Version switch bumpType { case "major": diff --git a/bump/bumper_test.go b/bump/bumper_test.go index c3a0307..566e7cd 100644 --- a/bump/bumper_test.go +++ b/bump/bumper_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/Masterminds/semver/v3" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" @@ -15,7 +16,9 @@ import ( const ( repoDocs = `name: johnmanjiro13/gh-bump description: gh extension for bumping version of a repository` - tagList = `v0.2.1 Latest v0.2.1 2021-12-08T04:19:16Z` + tagList = `v0.2.1 Latest v0.2.1 2021-12-08T04:19:16Z` + releaseView = `title: v0.1.0 +tag: v0.1.0` ) func TestBumper_WithRepository(t *testing.T) { @@ -40,7 +43,7 @@ func TestBumper_WithRepository(t *testing.T) { defer ctrl.Finish() gh := mock_bump.NewMockGh(ctrl) - gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), &bytes.Buffer{}, nil) + gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), nil, nil) for name, tt := range tests { t.Run(name, func(t *testing.T) { @@ -146,7 +149,7 @@ func TestBumper_ResolveRepository(t *testing.T) { defer ctrl.Finish() gh := mock_bump.NewMockGh(ctrl) - gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), &bytes.Buffer{}, nil) + gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), nil, nil) b := bump.New(gh) got, err := bump.ResolveRepository(b) @@ -161,13 +164,62 @@ func TestBumper_listReleases(t *testing.T) { gh := mock_bump.NewMockGh(ctrl) b := bump.New(gh) gh.EXPECT().ListRelease(b.Repository(), b.IsCurrent()). - Return(bytes.NewBufferString(tagList), &bytes.Buffer{}, nil) + Return(bytes.NewBufferString(tagList), nil, nil) got, err := bump.ListReleases(b) assert.NoError(t, err) assert.Equal(t, fmt.Sprintf("Tags:\n%s", tagList), got) } +func TestBumper_currentVersion(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gh := mock_bump.NewMockGh(ctrl) + b := bump.New(gh) + + t.Run("new version", func(t *testing.T) { + gh.EXPECT().ViewRelease(b.Repository(), b.IsCurrent()). + Return(bytes.NewBufferString(releaseView), nil, nil) + + got, isInitial, err := bump.CurrentVersion(b) + assert.NoError(t, err) + assert.False(t, isInitial) + want := semver.MustParse("v0.1.0") + assert.Equal(t, want, got) + }) +} + +func TestIncrementVersion(t *testing.T) { + current := semver.MustParse("v0.1.0") + + tests := map[string]struct { + bumpType string + want *semver.Version + }{ + "major": { + bumpType: "major", + want: semver.MustParse("v1.0.0"), + }, + "minor": { + bumpType: "minor", + want: semver.MustParse("v0.2.0"), + }, + "patch": { + bumpType: "patch", + want: semver.MustParse("v0.1.1"), + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + got, err := bump.IncrementVersion(current, tt.bumpType) + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + func TestBumper_createRelease(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/bump/export_test.go b/bump/export_test.go index 5bf720e..b3a2f4e 100644 --- a/bump/export_test.go +++ b/bump/export_test.go @@ -4,6 +4,8 @@ var ( ResolveRepository = (*bumper).resolveRepository ListReleases = (*bumper).listReleases CreateRelease = (*bumper).createRelease + CurrentVersion = (*bumper).currentVersion + IncrementVersion = incrementVersion ) func (b *bumper) Repository() string { From c4bc9b803c025f40d6dd1bcac9909baf66f97cbc Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sun, 5 Jun 2022 09:33:22 +0900 Subject: [PATCH 06/10] fix: create new bytes.Buffer --- gh/gh.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gh/gh.go b/gh/gh.go index aef9639..5fb117c 100644 --- a/gh/gh.go +++ b/gh/gh.go @@ -72,6 +72,8 @@ func (g *gh) CreateRelease(version string, repo string, isCurrent bool, option * } func runGh(args ...string) (sout, eout *bytes.Buffer, err error) { + sout = new(bytes.Buffer) + eout = new(bytes.Buffer) ghBin, err := safeexec.LookPath("gh") if err != nil { err = fmt.Errorf("could not find gh. err: %w", err) From 90e62afe26fc47f6019ec1726b17f231b0052547 Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sun, 5 Jun 2022 10:13:12 +0900 Subject: [PATCH 07/10] test: add tests for nextVersion and approve --- bump/bumper.go | 18 ++++++++++----- bump/bumper_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ bump/export_test.go | 2 ++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/bump/bumper.go b/bump/bumper.go index 8a17ffd..35ed903 100644 --- a/bump/bumper.go +++ b/bump/bumper.go @@ -3,6 +3,8 @@ package bump import ( "bytes" "fmt" + "io" + "os" "strings" "github.com/Masterminds/semver/v3" @@ -107,13 +109,13 @@ func (b *bumper) Bump() error { if isInitial { nextVer = current } else { - nextVer, err = nextVersion(current) + nextVer, err = nextVersion(current, os.Stdin, os.Stdout) if err != nil { return err } } - ok, err := approve(nextVer) + ok, err := approve(nextVer, os.Stdin, os.Stdout) if err != nil { return err } @@ -192,10 +194,12 @@ func newVersion() (*semver.Version, error) { return semver.NewVersion(result) } -func nextVersion(current *semver.Version) (*semver.Version, error) { +func nextVersion(current *semver.Version, sin io.ReadCloser, sout io.WriteCloser) (*semver.Version, error) { prompt := promptui.Select{ - Label: fmt.Sprintf("Select next version. current: %s", current.Original()), - Items: []string{"patch", "minor", "major"}, + Label: fmt.Sprintf("Select next version. current: %s", current.Original()), + Items: []string{"patch", "minor", "major"}, + Stdin: sin, + Stdout: sout, } _, bumpType, err := prompt.Run() if err != nil { @@ -220,7 +224,7 @@ func incrementVersion(current *semver.Version, bumpType string) (*semver.Version return &next, nil } -func approve(next *semver.Version) (bool, error) { +func approve(next *semver.Version, sin io.ReadCloser, sout io.WriteCloser) (bool, error) { validate := func(input string) error { if input != "y" && input != "yes" && input != "n" && input != "no" { return fmt.Errorf("invalid character. press y/n") @@ -230,6 +234,8 @@ func approve(next *semver.Version) (bool, error) { prompt := promptui.Prompt{ Label: fmt.Sprintf("Create release %s ? [y/n]", next.Original()), Validate: validate, + Stdin: sin, + Stdout: sout, } result, err := prompt.Run() if err != nil { diff --git a/bump/bumper_test.go b/bump/bumper_test.go index 566e7cd..ab8079b 100644 --- a/bump/bumper_test.go +++ b/bump/bumper_test.go @@ -3,6 +3,8 @@ package bump_test import ( "bytes" "fmt" + "io" + "strings" "testing" "github.com/Masterminds/semver/v3" @@ -21,6 +23,8 @@ description: gh extension for bumping version of a repository` tag: v0.1.0` ) +var arrowDownAndEnter = []byte{14, 10} + func TestBumper_WithRepository(t *testing.T) { tests := map[string]struct { repository string @@ -190,6 +194,58 @@ func TestBumper_currentVersion(t *testing.T) { }) } +type mockWriteCloser struct { + bytes.Buffer +} + +func (m *mockWriteCloser) Close() error { + return nil +} + +func TestNextVersion(t *testing.T) { + sin := io.NopCloser(strings.NewReader(string(arrowDownAndEnter))) + sout := &mockWriteCloser{bytes.Buffer{}} + current := semver.MustParse("v0.1.0") + nextVer, err := bump.NextVersion(current, sin, sout) + fmt.Println(sout.String()) + assert.NoError(t, err) + assert.Equal(t, semver.MustParse("v0.2.0"), nextVer) +} + +func TestApprove(t *testing.T) { + tests := map[string]struct { + text string + want bool + }{ + "approve with yes": { + text: "yes\n", + want: true, + }, + "approve with y": { + text: "y\n", + want: true, + }, + "disapprove with no": { + text: "no\n", + want: false, + }, + "disapprove with n": { + text: "n\n", + want: false, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + sin := io.NopCloser(strings.NewReader(tt.text)) + sout := &mockWriteCloser{bytes.Buffer{}} + got, err := bump.Approve(semver.MustParse("v0.1.0"), sin, sout) + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + func TestIncrementVersion(t *testing.T) { current := semver.MustParse("v0.1.0") diff --git a/bump/export_test.go b/bump/export_test.go index b3a2f4e..d9f391e 100644 --- a/bump/export_test.go +++ b/bump/export_test.go @@ -5,7 +5,9 @@ var ( ListReleases = (*bumper).listReleases CreateRelease = (*bumper).createRelease CurrentVersion = (*bumper).currentVersion + NextVersion = nextVersion IncrementVersion = incrementVersion + Approve = approve ) func (b *bumper) Repository() string { From 39e3818be5a728273832c38b205e8058a49f9dfb Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sun, 5 Jun 2022 10:23:59 +0900 Subject: [PATCH 08/10] ci: remove -race option --- .github/workflows/ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b54db2c..7b396ab 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,6 +27,7 @@ jobs: - name: build run: go build . - name: test - run: go test -v -race -coverprofile="coverage.txt" -covermode=atomic ./... + # We would like to use -race option but promptui has a data race bug, so we don't use it. + run: go test -v -coverprofile="coverage.txt" -covermode=atomic ./... - name: upload coverage to codecov uses: codecov/codecov-action@v2 From fbd69a2af49d538bf616657e8acd20a99dbc0e67 Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sun, 5 Jun 2022 10:41:16 +0900 Subject: [PATCH 09/10] test: add a test for newVersion --- bump/bumper.go | 6 ++++-- bump/bumper_test.go | 15 ++++++++++++++- bump/export_test.go | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/bump/bumper.go b/bump/bumper.go index 35ed903..d924caf 100644 --- a/bump/bumper.go +++ b/bump/bumper.go @@ -156,7 +156,7 @@ func (b *bumper) currentVersion() (*semver.Version, bool, error) { sout, eout, err := b.gh.ViewRelease(b.repository, b.isCurrent) if err != nil { if strings.Contains(eout.String(), "HTTP 404: Not Found") { - current, err := newVersion() + current, err := newVersion(os.Stdin, os.Stdout) if err != nil { return nil, isInitial, err } @@ -174,7 +174,7 @@ func (b *bumper) currentVersion() (*semver.Version, bool, error) { return current, isInitial, nil } -func newVersion() (*semver.Version, error) { +func newVersion(sin io.ReadCloser, sout io.WriteCloser) (*semver.Version, error) { validate := func(input string) error { _, err := semver.NewVersion(input) if err != nil { @@ -186,6 +186,8 @@ func newVersion() (*semver.Version, error) { prompt := promptui.Prompt{ Label: "New version", Validate: validate, + Stdin: sin, + Stdout: sout, } result, err := prompt.Run() if err != nil { diff --git a/bump/bumper_test.go b/bump/bumper_test.go index ab8079b..96d0bec 100644 --- a/bump/bumper_test.go +++ b/bump/bumper_test.go @@ -202,6 +202,14 @@ func (m *mockWriteCloser) Close() error { return nil } +func TestNewVersion(t *testing.T) { + sin := io.NopCloser(strings.NewReader("v0.1.0\n")) + sout := &mockWriteCloser{bytes.Buffer{}} + newVer, err := bump.NewVersion(sin, sout) + assert.NoError(t, err) + assert.Equal(t, semver.MustParse("v0.1.0"), newVer) +} + func TestNextVersion(t *testing.T) { sin := io.NopCloser(strings.NewReader(string(arrowDownAndEnter))) sout := &mockWriteCloser{bytes.Buffer{}} @@ -252,6 +260,7 @@ func TestIncrementVersion(t *testing.T) { tests := map[string]struct { bumpType string want *semver.Version + wantErr error }{ "major": { bumpType: "major", @@ -265,12 +274,16 @@ func TestIncrementVersion(t *testing.T) { bumpType: "patch", want: semver.MustParse("v0.1.1"), }, + "invalid": { + bumpType: "invalid", + wantErr: fmt.Errorf("invalid type"), + }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { got, err := bump.IncrementVersion(current, tt.bumpType) - assert.NoError(t, err) + assert.Equal(t, tt.wantErr, err) assert.Equal(t, tt.want, got) }) } diff --git a/bump/export_test.go b/bump/export_test.go index d9f391e..c6c2389 100644 --- a/bump/export_test.go +++ b/bump/export_test.go @@ -5,6 +5,7 @@ var ( ListReleases = (*bumper).listReleases CreateRelease = (*bumper).createRelease CurrentVersion = (*bumper).currentVersion + NewVersion = newVersion NextVersion = nextVersion IncrementVersion = incrementVersion Approve = approve From 01b8f3518184cf61528853e5d3d7dc7c7dd7c8e2 Mon Sep 17 00:00:00 2001 From: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com> Date: Sun, 5 Jun 2022 10:46:34 +0900 Subject: [PATCH 10/10] refactor: use bool literal for isInisital --- bump/bumper.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/bump/bumper.go b/bump/bumper.go index d924caf..13da3d5 100644 --- a/bump/bumper.go +++ b/bump/bumper.go @@ -151,27 +151,25 @@ func (b *bumper) listReleases() (string, error) { return fmt.Sprintf("Tags:\n%s", sout.String()), nil } -func (b *bumper) currentVersion() (*semver.Version, bool, error) { - var isInitial bool +func (b *bumper) currentVersion() (current *semver.Version, isInitial bool, err error) { sout, eout, err := b.gh.ViewRelease(b.repository, b.isCurrent) if err != nil { if strings.Contains(eout.String(), "HTTP 404: Not Found") { - current, err := newVersion(os.Stdin, os.Stdout) + current, err = newVersion(os.Stdin, os.Stdout) if err != nil { - return nil, isInitial, err + return nil, false, err } - isInitial = true - return current, isInitial, nil + return current, true, nil } - return nil, isInitial, err + return nil, false, err } viewOut := strings.Split(sout.String(), "\n")[1] tag := strings.TrimSpace(strings.Split(viewOut, ":")[1]) - current, err := semver.NewVersion(tag) + current, err = semver.NewVersion(tag) if err != nil { - return nil, isInitial, fmt.Errorf("invalid version. err: %w", err) + return nil, false, fmt.Errorf("invalid version. err: %w", err) } - return current, isInitial, nil + return current, false, nil } func newVersion(sin io.ReadCloser, sout io.WriteCloser) (*semver.Version, error) {