diff --git a/pkg/api/parser.go b/pkg/api/parser.go index d2360c2a..f08c41ef 100755 --- a/pkg/api/parser.go +++ b/pkg/api/parser.go @@ -81,7 +81,7 @@ func ParseWithMetadata(b []byte, allTags []string, nTags int, targetBranch strin err error tags []string ) - if tags, err = getLastNVersions(allTags, nTags); err != nil { + if tags, err = GetLastNVersions(allTags, nTags); err != nil { return nil, err } versionList := make([]string, 0) @@ -93,7 +93,7 @@ func ParseWithMetadata(b []byte, allTags []string, nTags int, targetBranch strin return Parse(b) } -func getLastNVersions(tags []string, n int) ([]string, error) { +func GetLastNVersions(tags []string, n int) ([]string, error) { if n < 0 { return nil, fmt.Errorf("n can't be negative") } else if n == 0 { @@ -133,7 +133,7 @@ func getLastNVersions(tags []string, n int) ([]string, error) { } } if n > len(latestVersions) { - return nil, fmt.Errorf("number of tags is greater than the actual number of tags with latest patch:requested %d actual %d", n, len(latestVersions)) + return nil, fmt.Errorf("number of tags is greater than the actual number of all tags: wanted - %d, actual - %d", n, len(latestVersions)) } return latestVersions, nil } diff --git a/pkg/api/parserGinkgo_test.go b/pkg/api/parserGinkgo_test.go new file mode 100644 index 00000000..4add073a --- /dev/null +++ b/pkg/api/parserGinkgo_test.go @@ -0,0 +1,255 @@ +// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 +package api_test + +import ( + "testing" + + "github.com/gardener/docforge/pkg/api" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestParser(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Parser Suite") +} + +var _ = Describe("Parser", func() { + Describe("Version processing", func() { + var ( + tags []string + n int + outputTags []string + err error + ) + JustBeforeEach(func() { + outputTags, err = api.GetLastNVersions(tags, n) + }) + Context("given general use case input", func() { + BeforeEach(func() { + tags = []string{"v1.2.3", "v1.2.8", "v1.1.5", "v1.1.0", "v1.1.3", "v2.0.1", "v2.0.8", "v2.1.0", "v2.0.6"} + n = 4 + }) + + It("should process them correctly", func() { + Expect(outputTags).To(Equal([]string{"v2.1.0", "v2.0.8", "v1.2.8", "v1.1.5"})) + Expect(err).NotTo(HaveOccurred()) + }) + }) + Context("given versions without the v prefix", func() { + BeforeEach(func() { + tags = []string{"1.2.3", "1.2.8", "1.1.5", "1.1.0", "1.1.3", "2.0.1", "2.0.8", "2.1.0", "2.0.6"} + n = 4 + }) + + It("should process them correctly", func() { + Expect(outputTags).To(Equal([]string{"2.1.0", "2.0.8", "1.2.8", "1.1.5"})) + Expect(err).NotTo(HaveOccurred()) + }) + }) + Context("given a tag string with less versions as requested", func() { + BeforeEach(func() { + tags = []string{"v1.2.3", "v1.2.8", "v1.1.5", "v1.1.0", "v1.1.3", "v2.0.1", "v2.0.8", "v2.1.0", "v2.0.6"} + n = 5 + }) + It("should return the appropriate error", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("number of tags is greater than the actual number of all tags: wanted - 5, actual - 4")) + }) + }) + Context("given a unparsable version", func() { + BeforeEach(func() { + tags = []string{"v1.2.3", "v1.2.8.0"} + n = 1 + }) + It("should return appropriate error", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("Error parsing version: v1.2.8.0")) + }) + }) + + Context("given negative number", func() { + BeforeEach(func() { + tags = nil + n = -7 + }) + + It("should throw error", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("n can't be negative")) + }) + }) + Context("given empty version array", func() { + BeforeEach(func() { + tags = []string{} + n = 0 + }) + Context("and no num tags", func() { + BeforeEach(func() { + n = 0 + }) + It("should not return error", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(outputTags).To(Equal([]string{})) + }) + }) + Context("and some num tags", func() { + BeforeEach(func() { + n = 2 + }) + It("should return error that the number of tags is greater than the actual number of all tags", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("number of tags is greater than the actual number of all tags: wanted - 2, actual - 0")) + }) + }) + }) + }) + Describe("Parsing with metadata", func() { + var ( + manifest []byte + tags []string + nVersions int + targetBranch string + url string + got *api.Documentation + err error + ) + JustBeforeEach(func() { + v := map[string]int{} + vars := map[string]string{} + + api.SetFlagsVariables(vars) + v[url] = len(tags) + api.SetNVersions(v, v) + got, err = api.ParseWithMetadata(manifest, tags, nVersions, targetBranch) + }) + + Context("given a general use case", func() { + BeforeEach(func() { + manifest = []byte(`structure: +- name: community + source: https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md +{{- $vers := Split .versions "," -}} +{{- range $i, $version := $vers -}} +{{- if eq $i 0 }} +- name: docs +{{- else }} +- name: {{$version}} +{{- end }} + source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md +{{- end }}`) + tags = []string{"v4.9", "v5.7", "v5.7.5", "v6.1", "v7.7"} + nVersions = 4 + targetBranch = "master" + url = "https://github.com/Kostov6/documentation/blob/master/.docforge/test.yamls" + }) + + It("should work as expected", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(&api.Documentation{ + Structure: []*api.Node{ + &api.Node{ + Name: "community", + Source: "https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "docs", + Source: "https://github.com/gardener/docforge/blob/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v7.7", + Source: "https://github.com/gardener/docforge/blob/v7.7/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v6.1", + Source: "https://github.com/gardener/docforge/blob/v6.1/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v5.7.5", + Source: "https://github.com/gardener/docforge/blob/v5.7.5/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v4.9", + Source: "https://github.com/gardener/docforge/blob/v4.9/integration-test/tested-doc/merge-test/testFile.md", + }, + }, + })) + }) + Context("and no versions are wanted", func() { + BeforeEach(func() { + nVersions = 0 + }) + It("should only use target branch", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(&api.Documentation{ + Structure: []*api.Node{ + &api.Node{ + Name: "community", + Source: "https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "docs", + Source: "https://github.com/gardener/docforge/blob/master/integration-test/tested-doc/merge-test/testFile.md", + }, + }, + })) + }) + }) + + Context("but more versions are requested than provided", func() { + BeforeEach(func() { + nVersions = 5 + }) + It("should return error that the number of tags is greater than the actual number of all tags", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("number of tags is greater than the actual number of all tags: wanted - 5, actual - 4")) + + }) + }) + + Context("but with broken yaml manifest", func() { + BeforeEach(func() { + manifest = []byte(`structure: +-name: community + source: https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md +{{- $vers := Split .versions "," -}} +{{- range $i, $version := $vers -}} +{{- if eq $i 0 }} +- name: docs +{{- else }} +- name: {{$version}} +{{- end }} + source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md +{{- end }}`) + }) + It("should register the yaml error", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("yaml: line 3: mapping values are not allowed in this context")) + }) + }) + }) + + Context("but with broken template format", func() { + BeforeEach(func() { + manifest = []byte(`structure: +- name: community +source: https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md +{{- $vers := Split .versions "," -}} +{{- range $i, $version := $vers -}} +{{- if eq $i 0 }} +- name: docs +{{- else }} +- name: {{$version}} +{{- end }} +source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md`) + }) + It("should register the template error", func() { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("template: :11: unexpected EOF")) + }) + }) + }) + +}) diff --git a/pkg/api/parser_test.go b/pkg/api/parser_test.go index bc637f73..6bf140e6 100755 --- a/pkg/api/parser_test.go +++ b/pkg/api/parser_test.go @@ -268,7 +268,7 @@ func TestGetLastNVersions(t *testing.T) { inputTags: []string{"v1.2.3", "v1.2.8", "v1.1.5", "v1.1.0", "v1.1.3", "v2.0.1", "v2.0.8", "v2.1.0", "v2.0.6"}, inputN: 5, outputTags: nil, - err: fmt.Errorf("number of tags is greater than the actual number of tags with latest patch:requested %d actual %d", 5, 4), + err: fmt.Errorf("number of tags is greater than the actual number of all tags: wanted - %d, actual - %d", 5, 4), }, { inputTags: []string{"1.2.3", "1.2.8", "1.1.5", "1.1.0", "1.1.3", "2.0.1", "2.0.8", "2.1.0", "2.0.6"}, inputN: 4, @@ -282,7 +282,7 @@ func TestGetLastNVersions(t *testing.T) { }, } for _, test := range tests { - result, resultErr := getLastNVersions(test.inputTags, test.inputN) + result, resultErr := GetLastNVersions(test.inputTags, test.inputN) if !reflect.DeepEqual(result, test.outputTags) { t.Errorf("Expected and actual result differ respectively: %s %s", test.outputTags, result) @@ -299,7 +299,6 @@ func TestParseWithMetadata(t *testing.T) { tags []string nVersions int b []byte - uri string want *Documentation err error }{ @@ -318,7 +317,6 @@ func TestParseWithMetadata(t *testing.T) { {{- end }} source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md {{- end }}`), - "https://github.com/Kostov6/documentation/blob/master/.docforge/test.yamls", &Documentation{ Structure: []*Node{ &Node{ @@ -370,7 +368,6 @@ func TestParseWithMetadata(t *testing.T) { {{- end }} source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md {{- end }}`), - "https://github.com/Kostov6/documentation/blob/master/.docforge/test.yamls", &Documentation{ Structure: []*Node{ &Node{ @@ -412,7 +409,6 @@ func TestParseWithMetadata(t *testing.T) { {{- end }} source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md {{- end }}`), - "https://github.com/Kostov6/documentation/blob/master/.docforge/test.yamls", &Documentation{ Structure: []*Node{ &Node{ diff --git a/pkg/git/git.go b/pkg/git/git.go index 89f001a1..05d6804d 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -4,6 +4,8 @@ package git +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate + import ( "context" @@ -12,12 +14,14 @@ import ( ) // Git interface defines gogit git API +//counterfeiter:generate . Git type Git interface { PlainOpen(path string) (Repository, error) PlainCloneContext(ctx context.Context, path string, isBare bool, o *gogit.CloneOptions) (Repository, error) } // Repository interface defines gogit repository API +//counterfeiter:generate . Repository type Repository interface { FetchContext(ctx context.Context, o *gogit.FetchOptions) error Worktree() (RepositoryWorktree, error) @@ -26,6 +30,7 @@ type Repository interface { } // RepositoryWorktree interface defines gogit worktree API +//counterfeiter:generate . RepositoryWorktree type RepositoryWorktree interface { Checkout(opts *gogit.CheckoutOptions) error } diff --git a/pkg/git/gitfakes/fake_git.go b/pkg/git/gitfakes/fake_git.go new file mode 100644 index 00000000..79e540c0 --- /dev/null +++ b/pkg/git/gitfakes/fake_git.go @@ -0,0 +1,203 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package gitfakes + +import ( + "context" + "sync" + + "github.com/gardener/docforge/pkg/git" + gita "github.com/go-git/go-git/v5" +) + +type FakeGit struct { + PlainCloneContextStub func(context.Context, string, bool, *gita.CloneOptions) (git.Repository, error) + plainCloneContextMutex sync.RWMutex + plainCloneContextArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 bool + arg4 *gita.CloneOptions + } + plainCloneContextReturns struct { + result1 git.Repository + result2 error + } + plainCloneContextReturnsOnCall map[int]struct { + result1 git.Repository + result2 error + } + PlainOpenStub func(string) (git.Repository, error) + plainOpenMutex sync.RWMutex + plainOpenArgsForCall []struct { + arg1 string + } + plainOpenReturns struct { + result1 git.Repository + result2 error + } + plainOpenReturnsOnCall map[int]struct { + result1 git.Repository + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeGit) PlainCloneContext(arg1 context.Context, arg2 string, arg3 bool, arg4 *gita.CloneOptions) (git.Repository, error) { + fake.plainCloneContextMutex.Lock() + ret, specificReturn := fake.plainCloneContextReturnsOnCall[len(fake.plainCloneContextArgsForCall)] + fake.plainCloneContextArgsForCall = append(fake.plainCloneContextArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 bool + arg4 *gita.CloneOptions + }{arg1, arg2, arg3, arg4}) + stub := fake.PlainCloneContextStub + fakeReturns := fake.plainCloneContextReturns + fake.recordInvocation("PlainCloneContext", []interface{}{arg1, arg2, arg3, arg4}) + fake.plainCloneContextMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeGit) PlainCloneContextCallCount() int { + fake.plainCloneContextMutex.RLock() + defer fake.plainCloneContextMutex.RUnlock() + return len(fake.plainCloneContextArgsForCall) +} + +func (fake *FakeGit) PlainCloneContextCalls(stub func(context.Context, string, bool, *gita.CloneOptions) (git.Repository, error)) { + fake.plainCloneContextMutex.Lock() + defer fake.plainCloneContextMutex.Unlock() + fake.PlainCloneContextStub = stub +} + +func (fake *FakeGit) PlainCloneContextArgsForCall(i int) (context.Context, string, bool, *gita.CloneOptions) { + fake.plainCloneContextMutex.RLock() + defer fake.plainCloneContextMutex.RUnlock() + argsForCall := fake.plainCloneContextArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 +} + +func (fake *FakeGit) PlainCloneContextReturns(result1 git.Repository, result2 error) { + fake.plainCloneContextMutex.Lock() + defer fake.plainCloneContextMutex.Unlock() + fake.PlainCloneContextStub = nil + fake.plainCloneContextReturns = struct { + result1 git.Repository + result2 error + }{result1, result2} +} + +func (fake *FakeGit) PlainCloneContextReturnsOnCall(i int, result1 git.Repository, result2 error) { + fake.plainCloneContextMutex.Lock() + defer fake.plainCloneContextMutex.Unlock() + fake.PlainCloneContextStub = nil + if fake.plainCloneContextReturnsOnCall == nil { + fake.plainCloneContextReturnsOnCall = make(map[int]struct { + result1 git.Repository + result2 error + }) + } + fake.plainCloneContextReturnsOnCall[i] = struct { + result1 git.Repository + result2 error + }{result1, result2} +} + +func (fake *FakeGit) PlainOpen(arg1 string) (git.Repository, error) { + fake.plainOpenMutex.Lock() + ret, specificReturn := fake.plainOpenReturnsOnCall[len(fake.plainOpenArgsForCall)] + fake.plainOpenArgsForCall = append(fake.plainOpenArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.PlainOpenStub + fakeReturns := fake.plainOpenReturns + fake.recordInvocation("PlainOpen", []interface{}{arg1}) + fake.plainOpenMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeGit) PlainOpenCallCount() int { + fake.plainOpenMutex.RLock() + defer fake.plainOpenMutex.RUnlock() + return len(fake.plainOpenArgsForCall) +} + +func (fake *FakeGit) PlainOpenCalls(stub func(string) (git.Repository, error)) { + fake.plainOpenMutex.Lock() + defer fake.plainOpenMutex.Unlock() + fake.PlainOpenStub = stub +} + +func (fake *FakeGit) PlainOpenArgsForCall(i int) string { + fake.plainOpenMutex.RLock() + defer fake.plainOpenMutex.RUnlock() + argsForCall := fake.plainOpenArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeGit) PlainOpenReturns(result1 git.Repository, result2 error) { + fake.plainOpenMutex.Lock() + defer fake.plainOpenMutex.Unlock() + fake.PlainOpenStub = nil + fake.plainOpenReturns = struct { + result1 git.Repository + result2 error + }{result1, result2} +} + +func (fake *FakeGit) PlainOpenReturnsOnCall(i int, result1 git.Repository, result2 error) { + fake.plainOpenMutex.Lock() + defer fake.plainOpenMutex.Unlock() + fake.PlainOpenStub = nil + if fake.plainOpenReturnsOnCall == nil { + fake.plainOpenReturnsOnCall = make(map[int]struct { + result1 git.Repository + result2 error + }) + } + fake.plainOpenReturnsOnCall[i] = struct { + result1 git.Repository + result2 error + }{result1, result2} +} + +func (fake *FakeGit) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.plainCloneContextMutex.RLock() + defer fake.plainCloneContextMutex.RUnlock() + fake.plainOpenMutex.RLock() + defer fake.plainOpenMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeGit) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ git.Git = new(FakeGit) diff --git a/pkg/git/gitfakes/fake_repository.go b/pkg/git/gitfakes/fake_repository.go new file mode 100644 index 00000000..07714b33 --- /dev/null +++ b/pkg/git/gitfakes/fake_repository.go @@ -0,0 +1,337 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package gitfakes + +import ( + "context" + "sync" + + "github.com/gardener/docforge/pkg/git" + gita "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" +) + +type FakeRepository struct { + FetchContextStub func(context.Context, *gita.FetchOptions) error + fetchContextMutex sync.RWMutex + fetchContextArgsForCall []struct { + arg1 context.Context + arg2 *gita.FetchOptions + } + fetchContextReturns struct { + result1 error + } + fetchContextReturnsOnCall map[int]struct { + result1 error + } + ReferenceStub func(plumbing.ReferenceName, bool) (*plumbing.Reference, error) + referenceMutex sync.RWMutex + referenceArgsForCall []struct { + arg1 plumbing.ReferenceName + arg2 bool + } + referenceReturns struct { + result1 *plumbing.Reference + result2 error + } + referenceReturnsOnCall map[int]struct { + result1 *plumbing.Reference + result2 error + } + TagsStub func() ([]string, error) + tagsMutex sync.RWMutex + tagsArgsForCall []struct { + } + tagsReturns struct { + result1 []string + result2 error + } + tagsReturnsOnCall map[int]struct { + result1 []string + result2 error + } + WorktreeStub func() (git.RepositoryWorktree, error) + worktreeMutex sync.RWMutex + worktreeArgsForCall []struct { + } + worktreeReturns struct { + result1 git.RepositoryWorktree + result2 error + } + worktreeReturnsOnCall map[int]struct { + result1 git.RepositoryWorktree + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRepository) FetchContext(arg1 context.Context, arg2 *gita.FetchOptions) error { + fake.fetchContextMutex.Lock() + ret, specificReturn := fake.fetchContextReturnsOnCall[len(fake.fetchContextArgsForCall)] + fake.fetchContextArgsForCall = append(fake.fetchContextArgsForCall, struct { + arg1 context.Context + arg2 *gita.FetchOptions + }{arg1, arg2}) + stub := fake.FetchContextStub + fakeReturns := fake.fetchContextReturns + fake.recordInvocation("FetchContext", []interface{}{arg1, arg2}) + fake.fetchContextMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeRepository) FetchContextCallCount() int { + fake.fetchContextMutex.RLock() + defer fake.fetchContextMutex.RUnlock() + return len(fake.fetchContextArgsForCall) +} + +func (fake *FakeRepository) FetchContextCalls(stub func(context.Context, *gita.FetchOptions) error) { + fake.fetchContextMutex.Lock() + defer fake.fetchContextMutex.Unlock() + fake.FetchContextStub = stub +} + +func (fake *FakeRepository) FetchContextArgsForCall(i int) (context.Context, *gita.FetchOptions) { + fake.fetchContextMutex.RLock() + defer fake.fetchContextMutex.RUnlock() + argsForCall := fake.fetchContextArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeRepository) FetchContextReturns(result1 error) { + fake.fetchContextMutex.Lock() + defer fake.fetchContextMutex.Unlock() + fake.FetchContextStub = nil + fake.fetchContextReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeRepository) FetchContextReturnsOnCall(i int, result1 error) { + fake.fetchContextMutex.Lock() + defer fake.fetchContextMutex.Unlock() + fake.FetchContextStub = nil + if fake.fetchContextReturnsOnCall == nil { + fake.fetchContextReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.fetchContextReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeRepository) Reference(arg1 plumbing.ReferenceName, arg2 bool) (*plumbing.Reference, error) { + fake.referenceMutex.Lock() + ret, specificReturn := fake.referenceReturnsOnCall[len(fake.referenceArgsForCall)] + fake.referenceArgsForCall = append(fake.referenceArgsForCall, struct { + arg1 plumbing.ReferenceName + arg2 bool + }{arg1, arg2}) + stub := fake.ReferenceStub + fakeReturns := fake.referenceReturns + fake.recordInvocation("Reference", []interface{}{arg1, arg2}) + fake.referenceMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRepository) ReferenceCallCount() int { + fake.referenceMutex.RLock() + defer fake.referenceMutex.RUnlock() + return len(fake.referenceArgsForCall) +} + +func (fake *FakeRepository) ReferenceCalls(stub func(plumbing.ReferenceName, bool) (*plumbing.Reference, error)) { + fake.referenceMutex.Lock() + defer fake.referenceMutex.Unlock() + fake.ReferenceStub = stub +} + +func (fake *FakeRepository) ReferenceArgsForCall(i int) (plumbing.ReferenceName, bool) { + fake.referenceMutex.RLock() + defer fake.referenceMutex.RUnlock() + argsForCall := fake.referenceArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeRepository) ReferenceReturns(result1 *plumbing.Reference, result2 error) { + fake.referenceMutex.Lock() + defer fake.referenceMutex.Unlock() + fake.ReferenceStub = nil + fake.referenceReturns = struct { + result1 *plumbing.Reference + result2 error + }{result1, result2} +} + +func (fake *FakeRepository) ReferenceReturnsOnCall(i int, result1 *plumbing.Reference, result2 error) { + fake.referenceMutex.Lock() + defer fake.referenceMutex.Unlock() + fake.ReferenceStub = nil + if fake.referenceReturnsOnCall == nil { + fake.referenceReturnsOnCall = make(map[int]struct { + result1 *plumbing.Reference + result2 error + }) + } + fake.referenceReturnsOnCall[i] = struct { + result1 *plumbing.Reference + result2 error + }{result1, result2} +} + +func (fake *FakeRepository) Tags() ([]string, error) { + fake.tagsMutex.Lock() + ret, specificReturn := fake.tagsReturnsOnCall[len(fake.tagsArgsForCall)] + fake.tagsArgsForCall = append(fake.tagsArgsForCall, struct { + }{}) + stub := fake.TagsStub + fakeReturns := fake.tagsReturns + fake.recordInvocation("Tags", []interface{}{}) + fake.tagsMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRepository) TagsCallCount() int { + fake.tagsMutex.RLock() + defer fake.tagsMutex.RUnlock() + return len(fake.tagsArgsForCall) +} + +func (fake *FakeRepository) TagsCalls(stub func() ([]string, error)) { + fake.tagsMutex.Lock() + defer fake.tagsMutex.Unlock() + fake.TagsStub = stub +} + +func (fake *FakeRepository) TagsReturns(result1 []string, result2 error) { + fake.tagsMutex.Lock() + defer fake.tagsMutex.Unlock() + fake.TagsStub = nil + fake.tagsReturns = struct { + result1 []string + result2 error + }{result1, result2} +} + +func (fake *FakeRepository) TagsReturnsOnCall(i int, result1 []string, result2 error) { + fake.tagsMutex.Lock() + defer fake.tagsMutex.Unlock() + fake.TagsStub = nil + if fake.tagsReturnsOnCall == nil { + fake.tagsReturnsOnCall = make(map[int]struct { + result1 []string + result2 error + }) + } + fake.tagsReturnsOnCall[i] = struct { + result1 []string + result2 error + }{result1, result2} +} + +func (fake *FakeRepository) Worktree() (git.RepositoryWorktree, error) { + fake.worktreeMutex.Lock() + ret, specificReturn := fake.worktreeReturnsOnCall[len(fake.worktreeArgsForCall)] + fake.worktreeArgsForCall = append(fake.worktreeArgsForCall, struct { + }{}) + stub := fake.WorktreeStub + fakeReturns := fake.worktreeReturns + fake.recordInvocation("Worktree", []interface{}{}) + fake.worktreeMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRepository) WorktreeCallCount() int { + fake.worktreeMutex.RLock() + defer fake.worktreeMutex.RUnlock() + return len(fake.worktreeArgsForCall) +} + +func (fake *FakeRepository) WorktreeCalls(stub func() (git.RepositoryWorktree, error)) { + fake.worktreeMutex.Lock() + defer fake.worktreeMutex.Unlock() + fake.WorktreeStub = stub +} + +func (fake *FakeRepository) WorktreeReturns(result1 git.RepositoryWorktree, result2 error) { + fake.worktreeMutex.Lock() + defer fake.worktreeMutex.Unlock() + fake.WorktreeStub = nil + fake.worktreeReturns = struct { + result1 git.RepositoryWorktree + result2 error + }{result1, result2} +} + +func (fake *FakeRepository) WorktreeReturnsOnCall(i int, result1 git.RepositoryWorktree, result2 error) { + fake.worktreeMutex.Lock() + defer fake.worktreeMutex.Unlock() + fake.WorktreeStub = nil + if fake.worktreeReturnsOnCall == nil { + fake.worktreeReturnsOnCall = make(map[int]struct { + result1 git.RepositoryWorktree + result2 error + }) + } + fake.worktreeReturnsOnCall[i] = struct { + result1 git.RepositoryWorktree + result2 error + }{result1, result2} +} + +func (fake *FakeRepository) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.fetchContextMutex.RLock() + defer fake.fetchContextMutex.RUnlock() + fake.referenceMutex.RLock() + defer fake.referenceMutex.RUnlock() + fake.tagsMutex.RLock() + defer fake.tagsMutex.RUnlock() + fake.worktreeMutex.RLock() + defer fake.worktreeMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRepository) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ git.Repository = new(FakeRepository) diff --git a/pkg/git/gitfakes/fake_repository_worktree.go b/pkg/git/gitfakes/fake_repository_worktree.go new file mode 100644 index 00000000..c95a1b20 --- /dev/null +++ b/pkg/git/gitfakes/fake_repository_worktree.go @@ -0,0 +1,112 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package gitfakes + +import ( + "sync" + + "github.com/gardener/docforge/pkg/git" + gita "github.com/go-git/go-git/v5" +) + +type FakeRepositoryWorktree struct { + CheckoutStub func(*gita.CheckoutOptions) error + checkoutMutex sync.RWMutex + checkoutArgsForCall []struct { + arg1 *gita.CheckoutOptions + } + checkoutReturns struct { + result1 error + } + checkoutReturnsOnCall map[int]struct { + result1 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRepositoryWorktree) Checkout(arg1 *gita.CheckoutOptions) error { + fake.checkoutMutex.Lock() + ret, specificReturn := fake.checkoutReturnsOnCall[len(fake.checkoutArgsForCall)] + fake.checkoutArgsForCall = append(fake.checkoutArgsForCall, struct { + arg1 *gita.CheckoutOptions + }{arg1}) + stub := fake.CheckoutStub + fakeReturns := fake.checkoutReturns + fake.recordInvocation("Checkout", []interface{}{arg1}) + fake.checkoutMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeRepositoryWorktree) CheckoutCallCount() int { + fake.checkoutMutex.RLock() + defer fake.checkoutMutex.RUnlock() + return len(fake.checkoutArgsForCall) +} + +func (fake *FakeRepositoryWorktree) CheckoutCalls(stub func(*gita.CheckoutOptions) error) { + fake.checkoutMutex.Lock() + defer fake.checkoutMutex.Unlock() + fake.CheckoutStub = stub +} + +func (fake *FakeRepositoryWorktree) CheckoutArgsForCall(i int) *gita.CheckoutOptions { + fake.checkoutMutex.RLock() + defer fake.checkoutMutex.RUnlock() + argsForCall := fake.checkoutArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeRepositoryWorktree) CheckoutReturns(result1 error) { + fake.checkoutMutex.Lock() + defer fake.checkoutMutex.Unlock() + fake.CheckoutStub = nil + fake.checkoutReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeRepositoryWorktree) CheckoutReturnsOnCall(i int, result1 error) { + fake.checkoutMutex.Lock() + defer fake.checkoutMutex.Unlock() + fake.CheckoutStub = nil + if fake.checkoutReturnsOnCall == nil { + fake.checkoutReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.checkoutReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeRepositoryWorktree) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.checkoutMutex.RLock() + defer fake.checkoutMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRepositoryWorktree) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ git.RepositoryWorktree = new(FakeRepositoryWorktree) diff --git a/pkg/resourcehandlers/git/gitGinko_resource_handler_test.go b/pkg/resourcehandlers/git/gitGinko_resource_handler_test.go new file mode 100644 index 00000000..6325905e --- /dev/null +++ b/pkg/resourcehandlers/git/gitGinko_resource_handler_test.go @@ -0,0 +1,761 @@ +// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 +package git_test + +import ( + "context" + "io/fs" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + + "github.com/gardener/docforge/pkg/api" + "github.com/gardener/docforge/pkg/git/gitfakes" + "github.com/gardener/docforge/pkg/resourcehandlers/git" + fakes2 "github.com/gardener/docforge/pkg/resourcehandlers/git/gitfakes" + ghub "github.com/gardener/docforge/pkg/resourcehandlers/github" + + "github.com/google/go-github/v32/github" +) + +const ( + // baseURLPath is a non-empty Client.BaseURL path to use during tests, + // to ensure relative URLs are used for all endpoints. See issue #752. + baseURLPath = "/api-v3" +) + +// setup sets up a test HTTP server along with a github.Client that is +// configured to talk to that test server. Tests should register handlers on +// mux which provide mock responses for the API method being tested. +func setup() (client *github.Client, mux *http.ServeMux, serverURL string, teardown func()) { + // mux is the HTTP request multiplexer used with the test server. + mux = http.NewServeMux() + + // We want to ensure that tests catch mistakes where the endpoint URL is + // specified as absolute rather than relative. It only makes a difference + // when there's a non-empty base URL path. So, use that. See issue #752. + apiHandler := http.NewServeMux() + apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux)) + apiHandler.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintln(os.Stderr, "FAIL: Client.BaseURL path prefix is not preserved in the request URL:") + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "\t"+req.URL.String()) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "\tDid you accidentally use an absolute endpoint URL rather than relative?") + fmt.Fprintln(os.Stderr, "\tSee https://github.com/google/go-github/issues/752 for information.") + http.Error(w, "Client.BaseURL path prefix is not preserved in the request URL.", http.StatusInternalServerError) + }) + + // server is a test HTTP server used to provide mock API responses. + server := httptest.NewServer(apiHandler) + + // client is the GitHub client being tested and is + // configured to use test server. + client = github.NewClient(nil) + url, _ := url.Parse(server.URL + baseURLPath + "/") + client.BaseURL = url + client.UploadURL = url + + return client, mux, server.URL, server.Close +} + +type mockFileInfo struct { + name string + size int64 + mode fs.FileMode + modTime time.Time + isDir bool + sys interface{} +} + +func (mfi mockFileInfo) Name() string { + return mfi.name +} +func (mfi mockFileInfo) Size() int64 { + return mfi.size +} +func (mfi mockFileInfo) Mode() fs.FileMode { + return mfi.mode +} +func (mfi mockFileInfo) ModTime() time.Time { + return mfi.modTime +} +func (mfi mockFileInfo) IsDir() bool { + return mfi.isDir +} +func (mfi mockFileInfo) Sys() interface{} { + return mfi.sys +} + +var ( + manifestData = []byte(fmt.Sprintf(`structure: +- name: community + source: https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md +{{- $vers := Split .versions "," -}} +{{- range $i, $version := $vers -}} +{{- if eq $i 0 }} +- name: docs +{{- else }} +- name: {{$version}} +{{- end }} + source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md +{{- end }}`)) + + mux = func(mux *http.ServeMux) { + mux.HandleFunc("/repos/testOrg/testRepo/git/trees/testMainBranch", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(` + { + "sha": "9fb037999f264ba9a7fc6274d15fa3ae2ab98312", + "url": "https://api.github.com/repos/testOrg/testRepo/git/trees/testMainBranch", + "tree": [ + { + "path": "testManifest.yaml", + "mode": "100644", + "type": "blob", + "size": 30, + "sha": "testSha", + "url": "https://api.github.com/repos/testOrg/testRepo/git/trees/testMainBranch/testManifest.yaml" + } + ], + "truncated": false + } + `))) + }) + mux.HandleFunc("/repos/testOrg/testRepo", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(` + { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "forks": 9, + "stargazers_count": 80, + "watchers_count": 80, + "watchers": 80, + "size": 108, + "default_branch": "testMainBranch", + "open_issues_count": 0, + "open_issues": 0, + "is_template": false, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "pull": true, + "push": false, + "admin": false + }, + "allow_rebase_merge": true, + "template_repository": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World-Template", + "full_name": "octocat/Hello-World-Template", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World-Template", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World-Template", + "archive_url": "https://api.github.com/repos/octocat/Hello-World-Template/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World-Template/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World-Template/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World-Template/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World-Template/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World-Template/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World-Template/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World-Template/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World-Template/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World-Template/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World-Template/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World-Template/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World-Template/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World-Template.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World-Template/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World-Template/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World-Template/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World-Template/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World-Template/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World-Template/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World-Template/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World-Template/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World-Template/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World-Template/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World-Template/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World-Template.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World-Template/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World-Template/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World-Template/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World-Template/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World-Template/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World-Template/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World-Template.git", + "mirror_url": "git:git.example.com/octocat/Hello-World-Template", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World-Template/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World-Template", + "homepage": "https://github.com", + "language": null, + "forks": 9, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "watchers": 80, + "size": 108, + "default_branch": "master", + "open_issues": 0, + "open_issues_count": 0, + "is_template": true, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://api.github.com/licenses/mit" + }, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0 + }, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "spdx_id": "MIT", + "url": "https://api.github.com/licenses/mit", + "node_id": "MDc6TGljZW5zZW1pdA==" + }, + "organization": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "Organization", + "site_admin": false + }, + "parent": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://api.github.com/licenses/mit" + }, + "forks": 1, + "open_issues": 1, + "watchers": 1 + }, + "source": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://api.github.com/licenses/mit" + }, + "forks": 1, + "open_issues": 1, + "watchers": 1 + } + } + `))) + + }) + } +) + +var _ = Describe("Git", func() { + + Describe("resolving documentation", func() { + var ( + repositoryPath string + tags []string + + uri string + fakeGit gitfakes.FakeGit + fakeFileSystem fakes2.FakeFileReader + got *api.Documentation + err error + ) + + JustBeforeEach(func() { + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + client, muxRes, _, teardown := setup() + repo := make(map[string]*git.Repository) + + repo[repositoryPath] = &git.Repository{ + State: git.Prepared, + Git: &fakeGit, + LocalPath: repositoryPath, + } + + gh := git.NewResourceHandlerExtended("", nil, "", client, nil, nil, nil, &fakeGit, repo, &fakeFileSystem) + + defer teardown() + if mux != nil { + mux(muxRes) + } + var s map[string]int = make(map[string]int) + s[uri] = len(tags) + api.SetNVersions(s, s) + api.SetFlagsVariables(make(map[string]string)) + //clear default branch cache + ghub.ClearDefaultBranchesCache() + got, err = gh.ResolveDocumentation(ctx, uri) + }) + Context("given the general use case", func() { + BeforeEach(func() { + repositoryPath = "github.com/testOrg/testRepo/testMainBranch" + // manifestName = "testManifest.yaml" + tags = []string{"v4.9", "v5.7", "v6.1", "v7.7"} + uri = "https://github.com/testOrg/testRepo/blob/DEFAULT_BRANCH/testManifest.yaml" + var ( + fakeRepository gitfakes.FakeRepository + fakeWorktree *gitfakes.FakeRepositoryWorktree + ) + + //fakeGit.PlainCloneContextReturns(&fakeRepository, nil) + fakeGit.PlainOpenReturns(&fakeRepository, nil) + fakeRepository.TagsReturns(tags, nil) + fakeRepository.WorktreeReturns(fakeWorktree, nil) + + fakeFileSystem.IsNotExistReturns(false) + fakeFileSystem.ReadFileReturns(manifestData, nil) + fakeFileSystem.StatReturns(mockFileInfo{isDir: false}, nil) + }) + + It("should process it correctly", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(&api.Documentation{ + Structure: []*api.Node{ + &api.Node{ + Name: "community", + Source: "https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "docs", + Source: "https://github.com/gardener/docforge/blob/testMainBranch/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v7.7", + Source: "https://github.com/gardener/docforge/blob/v7.7/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v6.1", + Source: "https://github.com/gardener/docforge/blob/v6.1/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v5.7", + Source: "https://github.com/gardener/docforge/blob/v5.7/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v4.9", + Source: "https://github.com/gardener/docforge/blob/v4.9/integration-test/tested-doc/merge-test/testFile.md", + }, + }, + })) + }) + Context("and no versions", func() { + BeforeEach(func() { + tags = []string{} + }) + It("should apply only the main branch", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(&api.Documentation{ + Structure: []*api.Node{ + &api.Node{ + Name: "community", + Source: "https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "docs", + Source: "https://github.com/gardener/docforge/blob/testMainBranch/integration-test/tested-doc/merge-test/testFile.md", + }, + }, + })) + }) + }) + }) + }) +}) diff --git a/pkg/resourcehandlers/git/git_resource_handler.go b/pkg/resourcehandlers/git/git_resource_handler.go index 0d68fb40..a2bbe46f 100644 --- a/pkg/resourcehandlers/git/git_resource_handler.go +++ b/pkg/resourcehandlers/git/git_resource_handler.go @@ -7,6 +7,14 @@ package git import ( "context" "fmt" + nethttp "net/http" + "net/url" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + "github.com/gardener/docforge/pkg/api" "github.com/gardener/docforge/pkg/git" "github.com/gardener/docforge/pkg/resourcehandlers" @@ -15,13 +23,6 @@ import ( "github.com/gardener/docforge/pkg/util/urls" "github.com/go-git/go-git/v5/plumbing/transport/http" ghclient "github.com/google/go-github/v32/github" - nethttp "net/http" - "net/url" - "os" - "path/filepath" - "regexp" - "strings" - "sync" ) // CacheDir is the name of repository cache directory @@ -33,6 +34,7 @@ var ( ) // FileReader defines interface for reading file attributes and content +//counterfeiter:generate . FileReader type FileReader interface { ReadFile(string) ([]byte, error) Stat(name string) (os.FileInfo, error) @@ -69,6 +71,21 @@ type Git struct { fileReader FileReader } +// NewResourceHandler creates new GitHub ResourceHandler objects given more arguments. Used when testing +func NewResourceHandlerExtended(gitRepositoriesAbsPath string, user *string, oauthToken string, githubOAuthClient *ghclient.Client, httpClient *nethttp.Client, acceptedHosts []string, localMappings map[string]string, gitArg git.Git, prepRepos map[string]*Repository, fileR FileReader) resourcehandlers.ResourceHandler { + return &Git{ + client: githubOAuthClient, + httpClient: httpClient, + gitAuth: buildAuthMethod(user, oauthToken), + localMappings: localMappings, + gitRepositoriesAbsPath: gitRepositoriesAbsPath, + acceptedHosts: acceptedHosts, + git: gitArg, + fileReader: fileR, + preparedRepos: prepRepos, + } +} + // NewResourceHandler creates new GitHub ResourceHandler objects func NewResourceHandler(gitRepositoriesAbsPath string, user *string, oauthToken string, githubOAuthClient *ghclient.Client, httpClient *nethttp.Client, acceptedHosts []string, localMappings map[string]string) resourcehandlers.ResourceHandler { return &Git{ diff --git a/pkg/resourcehandlers/git/gitfakes/fake_file_reader.go b/pkg/resourcehandlers/git/gitfakes/fake_file_reader.go new file mode 100644 index 00000000..f1de0338 --- /dev/null +++ b/pkg/resourcehandlers/git/gitfakes/fake_file_reader.go @@ -0,0 +1,270 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package gitfakes + +import ( + "io/fs" + "sync" + + "github.com/gardener/docforge/pkg/resourcehandlers/git" +) + +type FakeFileReader struct { + IsNotExistStub func(error) bool + isNotExistMutex sync.RWMutex + isNotExistArgsForCall []struct { + arg1 error + } + isNotExistReturns struct { + result1 bool + } + isNotExistReturnsOnCall map[int]struct { + result1 bool + } + ReadFileStub func(string) ([]byte, error) + readFileMutex sync.RWMutex + readFileArgsForCall []struct { + arg1 string + } + readFileReturns struct { + result1 []byte + result2 error + } + readFileReturnsOnCall map[int]struct { + result1 []byte + result2 error + } + StatStub func(string) (fs.FileInfo, error) + statMutex sync.RWMutex + statArgsForCall []struct { + arg1 string + } + statReturns struct { + result1 fs.FileInfo + result2 error + } + statReturnsOnCall map[int]struct { + result1 fs.FileInfo + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeFileReader) IsNotExist(arg1 error) bool { + fake.isNotExistMutex.Lock() + ret, specificReturn := fake.isNotExistReturnsOnCall[len(fake.isNotExistArgsForCall)] + fake.isNotExistArgsForCall = append(fake.isNotExistArgsForCall, struct { + arg1 error + }{arg1}) + stub := fake.IsNotExistStub + fakeReturns := fake.isNotExistReturns + fake.recordInvocation("IsNotExist", []interface{}{arg1}) + fake.isNotExistMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeFileReader) IsNotExistCallCount() int { + fake.isNotExistMutex.RLock() + defer fake.isNotExistMutex.RUnlock() + return len(fake.isNotExistArgsForCall) +} + +func (fake *FakeFileReader) IsNotExistCalls(stub func(error) bool) { + fake.isNotExistMutex.Lock() + defer fake.isNotExistMutex.Unlock() + fake.IsNotExistStub = stub +} + +func (fake *FakeFileReader) IsNotExistArgsForCall(i int) error { + fake.isNotExistMutex.RLock() + defer fake.isNotExistMutex.RUnlock() + argsForCall := fake.isNotExistArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeFileReader) IsNotExistReturns(result1 bool) { + fake.isNotExistMutex.Lock() + defer fake.isNotExistMutex.Unlock() + fake.IsNotExistStub = nil + fake.isNotExistReturns = struct { + result1 bool + }{result1} +} + +func (fake *FakeFileReader) IsNotExistReturnsOnCall(i int, result1 bool) { + fake.isNotExistMutex.Lock() + defer fake.isNotExistMutex.Unlock() + fake.IsNotExistStub = nil + if fake.isNotExistReturnsOnCall == nil { + fake.isNotExistReturnsOnCall = make(map[int]struct { + result1 bool + }) + } + fake.isNotExistReturnsOnCall[i] = struct { + result1 bool + }{result1} +} + +func (fake *FakeFileReader) ReadFile(arg1 string) ([]byte, error) { + fake.readFileMutex.Lock() + ret, specificReturn := fake.readFileReturnsOnCall[len(fake.readFileArgsForCall)] + fake.readFileArgsForCall = append(fake.readFileArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.ReadFileStub + fakeReturns := fake.readFileReturns + fake.recordInvocation("ReadFile", []interface{}{arg1}) + fake.readFileMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeFileReader) ReadFileCallCount() int { + fake.readFileMutex.RLock() + defer fake.readFileMutex.RUnlock() + return len(fake.readFileArgsForCall) +} + +func (fake *FakeFileReader) ReadFileCalls(stub func(string) ([]byte, error)) { + fake.readFileMutex.Lock() + defer fake.readFileMutex.Unlock() + fake.ReadFileStub = stub +} + +func (fake *FakeFileReader) ReadFileArgsForCall(i int) string { + fake.readFileMutex.RLock() + defer fake.readFileMutex.RUnlock() + argsForCall := fake.readFileArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeFileReader) ReadFileReturns(result1 []byte, result2 error) { + fake.readFileMutex.Lock() + defer fake.readFileMutex.Unlock() + fake.ReadFileStub = nil + fake.readFileReturns = struct { + result1 []byte + result2 error + }{result1, result2} +} + +func (fake *FakeFileReader) ReadFileReturnsOnCall(i int, result1 []byte, result2 error) { + fake.readFileMutex.Lock() + defer fake.readFileMutex.Unlock() + fake.ReadFileStub = nil + if fake.readFileReturnsOnCall == nil { + fake.readFileReturnsOnCall = make(map[int]struct { + result1 []byte + result2 error + }) + } + fake.readFileReturnsOnCall[i] = struct { + result1 []byte + result2 error + }{result1, result2} +} + +func (fake *FakeFileReader) Stat(arg1 string) (fs.FileInfo, error) { + fake.statMutex.Lock() + ret, specificReturn := fake.statReturnsOnCall[len(fake.statArgsForCall)] + fake.statArgsForCall = append(fake.statArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.StatStub + fakeReturns := fake.statReturns + fake.recordInvocation("Stat", []interface{}{arg1}) + fake.statMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeFileReader) StatCallCount() int { + fake.statMutex.RLock() + defer fake.statMutex.RUnlock() + return len(fake.statArgsForCall) +} + +func (fake *FakeFileReader) StatCalls(stub func(string) (fs.FileInfo, error)) { + fake.statMutex.Lock() + defer fake.statMutex.Unlock() + fake.StatStub = stub +} + +func (fake *FakeFileReader) StatArgsForCall(i int) string { + fake.statMutex.RLock() + defer fake.statMutex.RUnlock() + argsForCall := fake.statArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeFileReader) StatReturns(result1 fs.FileInfo, result2 error) { + fake.statMutex.Lock() + defer fake.statMutex.Unlock() + fake.StatStub = nil + fake.statReturns = struct { + result1 fs.FileInfo + result2 error + }{result1, result2} +} + +func (fake *FakeFileReader) StatReturnsOnCall(i int, result1 fs.FileInfo, result2 error) { + fake.statMutex.Lock() + defer fake.statMutex.Unlock() + fake.StatStub = nil + if fake.statReturnsOnCall == nil { + fake.statReturnsOnCall = make(map[int]struct { + result1 fs.FileInfo + result2 error + }) + } + fake.statReturnsOnCall[i] = struct { + result1 fs.FileInfo + result2 error + }{result1, result2} +} + +func (fake *FakeFileReader) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.isNotExistMutex.RLock() + defer fake.isNotExistMutex.RUnlock() + fake.readFileMutex.RLock() + defer fake.readFileMutex.RUnlock() + fake.statMutex.RLock() + defer fake.statMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeFileReader) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ git.FileReader = new(FakeFileReader) diff --git a/pkg/resourcehandlers/github/githubGinko_resource_handler_test.go b/pkg/resourcehandlers/github/githubGinko_resource_handler_test.go new file mode 100644 index 00000000..3b0ec3da --- /dev/null +++ b/pkg/resourcehandlers/github/githubGinko_resource_handler_test.go @@ -0,0 +1,823 @@ +// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 +package github_test + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" + "time" + + "github.com/gardener/docforge/pkg/api" + "github.com/gardener/docforge/pkg/resourcehandlers/github" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + githubApi "github.com/google/go-github/v32/github" +) + +func TestGithub(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Github Suite") +} + +const ( + // baseURLPath is a non-empty Client.BaseURL path to use during tests, + // to ensure relative URLs are used for all endpoints. See issue #752. + baseURLPath = "/api-v3" +) + +// setup sets up a test HTTP server along with a github.Client that is +// configured to talk to that test server. Tests should register handlers on +// mux which provide mock responses for the API method being tested. +func setup() (client *githubApi.Client, mux *http.ServeMux, serverURL string, teardown func()) { + // mux is the HTTP request multiplexer used with the test server. + mux = http.NewServeMux() + + // We want to ensure that tests catch mistakes where the endpoint URL is + // specified as absolute rather than relative. It only makes a difference + // when there's a non-empty base URL path. So, use that. See issue #752. + apiHandler := http.NewServeMux() + apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux)) + apiHandler.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintln(os.Stderr, "FAIL: Client.BaseURL path prefix is not preserved in the request URL:") + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "\t"+req.URL.String()) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "\tDid you accidentally use an absolute endpoint URL rather than relative?") + fmt.Fprintln(os.Stderr, "\tSee https://github.com/google/go-github/issues/752 for information.") + http.Error(w, "Client.BaseURL path prefix is not preserved in the request URL.", http.StatusInternalServerError) + }) + + // server is a test HTTP server used to provide mock API responses. + server := httptest.NewServer(apiHandler) + + // client is the GitHub client being tested and is + // configured to use test server. + client = githubApi.NewClient(nil) + url, _ := url.Parse(server.URL + baseURLPath + "/") + client.BaseURL = url + client.UploadURL = url + + return client, mux, server.URL, server.Close +} + +var _ = Describe("Github", func() { + Describe("getting all tags", func() { + var ( + rl *github.ResourceLocator + mux func(mux *http.ServeMux) + got []string + err error + ) + JustBeforeEach(func() { + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + gh := &github.GitHub{} + client, muxGot, _, teardown := setup() + defer teardown() + if mux != nil { + mux(muxGot) + } + gh.Client = client + got, err = gh.GetAllTags(ctx, rl) + }) + Context("given the general use case", func() { + BeforeEach(func() { + rl = &github.ResourceLocator{ + "https", + "github.com", + "gardener", + "gardener", + "", + github.Blob, + "", + "master", + false, + } + mux = func(mux *http.ServeMux) { + mux.HandleFunc("/repos/gardener/gardener/git/matching-refs/tags", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(` + [ + { + "ref": "refs/tags/v0.0.1", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4wLjE=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v0.0.1", + "object": { + "sha": "c5391f5187af434160c8056f47fbeeaed3670a9d", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/c5391f5187af434160c8056f47fbeeaed3670a9d" + } + }, + { + "ref": "refs/tags/v0.1.0", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4xLjA=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v0.1.0", + "object": { + "sha": "6bd668f2353f7ae6cddab09ef1434defe6431b89", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/6bd668f2353f7ae6cddab09ef1434defe6431b89" + } + }, + { + "ref": "refs/tags/v0.2.0", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4yLjA=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v0.2.0", + "object": { + "sha": "183554163eb56886860ba40af0c4b121379d4459", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/183554163eb56886860ba40af0c4b121379d4459" + } + } + ]`))) + }) + } + + }) + + It("should work as expected", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal([]string{"v0.0.1", "v0.1.0", "v0.2.0"})) + }) + }) + }) + + Describe("resolving documentation", func() { + var ( + uri string + nDefaultVersions int + mux func(mux *http.ServeMux) + got *api.Documentation + err error + ) + + JustBeforeEach(func() { + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + client, muxFromSetup, _, teardown := setup() + gh := github.NewResourceHandler(client, nil, nil) + defer teardown() + if mux != nil { + mux(muxFromSetup) + } + var s map[string]int = make(map[string]int) + s["default"] = nDefaultVersions + api.SetNVersions(s, s) + api.SetFlagsVariables(make(map[string]string)) + got, err = gh.ResolveDocumentation(ctx, uri) + }) + Context("given the general use case", func() { + BeforeEach(func() { + nDefaultVersions = 4 + uri = "https://github.com/testOrg/testRepo/blob/DEFAULT_BRANCH/testManifest.yaml" + mux = func(mux *http.ServeMux) { + mux.HandleFunc("/repos/testOrg/testRepo/git/trees/testMainBranch", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(` + { + "sha": "9fb037999f264ba9a7fc6274d15fa3ae2ab98312", + "url": "https://api.github.com/repos/testOrg/testRepo/git/trees/testMainBranch", + "tree": [ + { + "path": "testManifest.yaml", + "mode": "100644", + "type": "blob", + "size": 30, + "sha": "testSha", + "url": "https://api.github.com/repos/testOrg/testRepo/git/trees/testMainBranch/testManifest.yaml" + } + ], + "truncated": false + } + `))) + }) + mux.HandleFunc("/repos/testOrg/testRepo/git/blobs/testSha", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(`structure: +- name: community + source: https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md +{{- $vers := Split .versions "," -}} +{{- range $i, $version := $vers -}} +{{- if eq $i 0 }} +- name: docs +{{- else }} +- name: {{$version}} +{{- end }} + source: https://github.com/gardener/docforge/blob/{{$version}}/integration-test/tested-doc/merge-test/testFile.md +{{- end }}`))) + }) + mux.HandleFunc("/repos/testOrg/testRepo/git/matching-refs/tags", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(` + [ + { + "ref": "refs/tags/v4.9", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4wLjE=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v4.9", + "object": { + "sha": "c5391f5187af434160c8056f47fbeeaed3670a9d", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/c5391f5187af434160c8056f47fbeeaed3670a9d" + } + }, + { + "ref": "refs/tags/v5.7", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4xLjA=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v5.7", + "object": { + "sha": "6bd668f2353f7ae6cddab09ef1434defe6431b89", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/6bd668f2353f7ae6cddab09ef1434defe6431b89" + } + }, + { + "ref": "refs/tags/v6.1", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4yLjA=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v6.1", + "object": { + "sha": "183554163eb56886860ba40af0c4b121379d4459", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/183554163eb56886860ba40af0c4b121379d4459" + } + }, + { + "ref": "refs/tags/v7.7", + "node_id": "MDM6UmVmMjc3ODAyNDY2OnJlZnMvdGFncy92MC4yLjA=", + "url": "https://api.github.com/repos/gardener/docforge/git/refs/tags/v7.7", + "object": { + "sha": "183554163eb56886860ba40af0c4b121379d4459", + "type": "commit", + "url": "https://api.github.com/repos/gardener/docforge/git/commits/183554163eb56886860ba40af0c4b121379d4459" + } + } + ]`))) + }) + mux.HandleFunc("/repos/testOrg/testRepo", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(` + { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "forks": 9, + "stargazers_count": 80, + "watchers_count": 80, + "watchers": 80, + "size": 108, + "default_branch": "testMainBranch", + "open_issues_count": 0, + "open_issues": 0, + "is_template": false, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "pull": true, + "push": false, + "admin": false + }, + "allow_rebase_merge": true, + "template_repository": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World-Template", + "full_name": "octocat/Hello-World-Template", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World-Template", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World-Template", + "archive_url": "https://api.github.com/repos/octocat/Hello-World-Template/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World-Template/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World-Template/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World-Template/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World-Template/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World-Template/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World-Template/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World-Template/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World-Template/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World-Template/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World-Template/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World-Template/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World-Template/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World-Template.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World-Template/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World-Template/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World-Template/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World-Template/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World-Template/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World-Template/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World-Template/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World-Template/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World-Template/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World-Template/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World-Template/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World-Template.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World-Template/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World-Template/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World-Template/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World-Template/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World-Template/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World-Template/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World-Template/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World-Template.git", + "mirror_url": "git:git.example.com/octocat/Hello-World-Template", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World-Template/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World-Template", + "homepage": "https://github.com", + "language": null, + "forks": 9, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "watchers": 80, + "size": 108, + "default_branch": "master", + "open_issues": 0, + "open_issues_count": 0, + "is_template": true, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://api.github.com/licenses/mit" + }, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0 + }, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "spdx_id": "MIT", + "url": "https://api.github.com/licenses/mit", + "node_id": "MDc6TGljZW5zZW1pdA==" + }, + "organization": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "Organization", + "site_admin": false + }, + "parent": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://api.github.com/licenses/mit" + }, + "forks": 1, + "open_issues": 1, + "watchers": 1 + }, + "source": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "temp_clone_token": "dummy", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://api.github.com/licenses/mit" + }, + "forks": 1, + "open_issues": 1, + "watchers": 1 + } + }`))) + }) + + } + }) + It("should process it correctly", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(&api.Documentation{ + Structure: []*api.Node{ + &api.Node{ + Name: "community", + Source: "https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "docs", + Source: "https://github.com/gardener/docforge/blob/testMainBranch/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v7.7", + Source: "https://github.com/gardener/docforge/blob/v7.7/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v6.1", + Source: "https://github.com/gardener/docforge/blob/v6.1/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v5.7", + Source: "https://github.com/gardener/docforge/blob/v5.7/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "v4.9", + Source: "https://github.com/gardener/docforge/blob/v4.9/integration-test/tested-doc/merge-test/testFile.md", + }, + }, + })) + }) + Context("and no versions", func() { + BeforeEach(func() { + nDefaultVersions = 0 + }) + It("should apply only the main branch", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(&api.Documentation{ + Structure: []*api.Node{ + &api.Node{ + Name: "community", + Source: "https://github.com/gardener/docforge/edit/master/integration-test/tested-doc/merge-test/testFile.md", + }, + &api.Node{ + Name: "docs", + Source: "https://github.com/gardener/docforge/blob/testMainBranch/integration-test/tested-doc/merge-test/testFile.md", + }, + }, + })) + }) + }) + }) + }) +}) diff --git a/pkg/resourcehandlers/github/github_resource_handler.go b/pkg/resourcehandlers/github/github_resource_handler.go index 0e1b48dd..147f4b12 100644 --- a/pkg/resourcehandlers/github/github_resource_handler.go +++ b/pkg/resourcehandlers/github/github_resource_handler.go @@ -7,18 +7,19 @@ package github import ( "context" "fmt" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "strings" + "github.com/gardener/docforge/pkg/api" "github.com/gardener/docforge/pkg/markdown" "github.com/gardener/docforge/pkg/resourcehandlers" "github.com/gardener/docforge/pkg/util/httpclient" "github.com/gardener/docforge/pkg/util/urls" "github.com/google/go-github/v32/github" - "io/ioutil" "k8s.io/klog/v2" - "net/http" - "net/url" - "regexp" - "strings" ) // TreeEntryToGitHubLocator creates a ResourceLocator from a github.TreeEntry and shaAlias. @@ -319,7 +320,7 @@ func (gh *GitHub) ResolveDocumentation(ctx context.Context, path string) (*api.D rl.SHAAlias = api.ChooseTargetBranch(path, rl.SHAAlias) //getting nVersions based on configuration nVersions := api.ChooseNVersions(path) - tags, err := gh.getAllTags(ctx, rl) + tags, err := gh.GetAllTags(ctx, rl) if err != nil { return nil, err } @@ -330,7 +331,7 @@ func (gh *GitHub) ResolveDocumentation(ctx context.Context, path string) (*api.D return api.ParseWithMetadata(blob, tags, nVersions, rl.SHAAlias) } -func (gh *GitHub) getAllTags(ctx context.Context, rl *ResourceLocator) ([]string, error) { +func (gh *GitHub) GetAllTags(ctx context.Context, rl *ResourceLocator) ([]string, error) { refs, _, err := gh.Client.Git.ListMatchingRefs(ctx, rl.Owner, rl.Repo, &github.ReferenceListOptions{Ref: "tags"}) if err != nil { return nil, err diff --git a/pkg/resourcehandlers/github/github_resource_handler_test.go b/pkg/resourcehandlers/github/github_resource_handler_test.go index 669cac85..328d9a3a 100644 --- a/pkg/resourcehandlers/github/github_resource_handler_test.go +++ b/pkg/resourcehandlers/github/github_resource_handler_test.go @@ -991,7 +991,7 @@ func TestGetAllTags(t *testing.T) { c.mux(mux) } gh.Client = client - got, gotErr := gh.getAllTags(ctx, c.rl) + got, gotErr := gh.GetAllTags(ctx, c.rl) assert.Equal(t, c.err, gotErr) assert.Equal(t, c.want, got)