diff --git a/api/handlers/buildpack.go b/api/handlers/buildpack.go index 275c6c427..1d4bd444d 100644 --- a/api/handlers/buildpack.go +++ b/api/handlers/buildpack.go @@ -4,7 +4,6 @@ import ( "context" "net/http" "net/url" - "sort" "code.cloudfoundry.org/korifi/api/authorization" apierrors "code.cloudfoundry.org/korifi/api/errors" @@ -22,7 +21,7 @@ const ( //counterfeiter:generate -o fake -fake-name BuildpackRepository . BuildpackRepository type BuildpackRepository interface { - ListBuildpacks(ctx context.Context, authInfo authorization.Info) ([]repositories.BuildpackRecord, error) + ListBuildpacks(ctx context.Context, authInfo authorization.Info, message repositories.ListBuildpacksMessage) ([]repositories.BuildpackRecord, error) } type Buildpack struct { @@ -47,40 +46,19 @@ func (h *Buildpack) list(r *http.Request) (*routing.Response, error) { authInfo, _ := authorization.InfoFromContext(r.Context()) logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.build.list") - buildpackListFilter := new(payloads.BuildpackList) - if err := h.requestValidator.DecodeAndValidateURLValues(r, buildpackListFilter); err != nil { + payload := new(payloads.BuildpackList) + if err := h.requestValidator.DecodeAndValidateURLValues(r, payload); err != nil { return nil, apierrors.LogAndReturn(logger, err, "Unable to parse request query parameters") } - buildpacks, err := h.buildpackRepo.ListBuildpacks(r.Context(), authInfo) + buildpacks, err := h.buildpackRepo.ListBuildpacks(r.Context(), authInfo, payload.ToMessage()) if err != nil { return nil, apierrors.LogAndReturn(logger, err, "Failed to fetch buildpacks from Kubernetes") } - h.sortList(buildpacks, buildpackListFilter.OrderBy) - return routing.NewResponse(http.StatusOK).WithBody(presenter.ForList(presenter.ForBuildpack, buildpacks, h.serverURL, *r.URL)), nil } -// nolint:dupl -func (h *Buildpack) sortList(bpList []repositories.BuildpackRecord, order string) { - switch order { - case "": - case "created_at": - sort.Slice(bpList, func(i, j int) bool { return timePtrAfter(&bpList[j].CreatedAt, &bpList[i].CreatedAt) }) - case "-created_at": - sort.Slice(bpList, func(i, j int) bool { return timePtrAfter(&bpList[i].CreatedAt, &bpList[j].CreatedAt) }) - case "updated_at": - sort.Slice(bpList, func(i, j int) bool { return timePtrAfter(bpList[j].UpdatedAt, bpList[i].UpdatedAt) }) - case "-updated_at": - sort.Slice(bpList, func(i, j int) bool { return timePtrAfter(bpList[i].UpdatedAt, bpList[j].UpdatedAt) }) - case "position": - sort.Slice(bpList, func(i, j int) bool { return bpList[i].Position < bpList[j].Position }) - case "-position": - sort.Slice(bpList, func(i, j int) bool { return bpList[i].Position > bpList[j].Position }) - } -} - func (h *Buildpack) UnauthenticatedRoutes() []routing.Route { return nil } diff --git a/api/handlers/buildpack_test.go b/api/handlers/buildpack_test.go index c71628f11..c2c330e5a 100644 --- a/api/handlers/buildpack_test.go +++ b/api/handlers/buildpack_test.go @@ -3,12 +3,10 @@ package handlers_test import ( "errors" "net/http" - "net/http/httptest" "time" . "code.cloudfoundry.org/korifi/api/handlers" "code.cloudfoundry.org/korifi/api/handlers/fake" - "code.cloudfoundry.org/korifi/api/payloads" "code.cloudfoundry.org/korifi/api/repositories" . "code.cloudfoundry.org/korifi/tests/matchers" "code.cloudfoundry.org/korifi/tools" @@ -62,7 +60,7 @@ var _ = Describe("Buildpack", func() { It("returns the buildpacks for the default builder", func() { Expect(buildpackRepo.ListBuildpacksCallCount()).To(Equal(1)) - _, actualAuthInfo := buildpackRepo.ListBuildpacksArgsForCall(0) + _, actualAuthInfo, _ := buildpackRepo.ListBuildpacksArgsForCall(0) Expect(actualAuthInfo).To(Equal(authInfo)) Expect(rr).To(HaveHTTPStatus(http.StatusOK)) @@ -75,45 +73,6 @@ var _ = Describe("Buildpack", func() { ))) }) - Describe("Order results", func() { - BeforeEach(func() { - buildpackRepo.ListBuildpacksReturns([]repositories.BuildpackRecord{ - { - CreatedAt: time.UnixMilli(1000), - UpdatedAt: tools.PtrTo(time.UnixMilli(5000)), - Position: 1, - }, - { - CreatedAt: time.UnixMilli(3000), - UpdatedAt: tools.PtrTo(time.UnixMilli(4000)), - Position: 2, - }, - { - CreatedAt: time.UnixMilli(2000), - UpdatedAt: tools.PtrTo(time.UnixMilli(6000)), - Position: 3, - }, - }, nil) - }) - - DescribeTable("ordering results", func(orderBy string, expectedOrder ...any) { - req = createHttpRequest("GET", "/v3/buildpacks?order_by=not-used", nil) - requestValidator.DecodeAndValidateURLValuesStub = decodeAndValidateURLValuesStub(&payloads.BuildpackList{ - OrderBy: orderBy, - }) - rr = httptest.NewRecorder() - routerBuilder.Build().ServeHTTP(rr, req) - Expect(rr).To(HaveHTTPBody(MatchJSONPath("$.resources[*].position", expectedOrder))) - }, - Entry("created_at ASC", "created_at", 1.0, 3.0, 2.0), - Entry("created_at DESC", "-created_at", 2.0, 3.0, 1.0), - Entry("updated_at ASC", "updated_at", 2.0, 1.0, 3.0), - Entry("updated_at DESC", "-updated_at", 3.0, 1.0, 2.0), - Entry("position ASC", "position", 1.0, 2.0, 3.0), - Entry("position DESC", "-position", 3.0, 2.0, 1.0), - ) - }) - When("request is invalid", func() { BeforeEach(func() { requestValidator.DecodeAndValidateURLValuesReturns(errors.New("foo")) diff --git a/api/handlers/fake/buildpack_repository.go b/api/handlers/fake/buildpack_repository.go index 63e1bece5..15dff4236 100644 --- a/api/handlers/fake/buildpack_repository.go +++ b/api/handlers/fake/buildpack_repository.go @@ -11,11 +11,12 @@ import ( ) type BuildpackRepository struct { - ListBuildpacksStub func(context.Context, authorization.Info) ([]repositories.BuildpackRecord, error) + ListBuildpacksStub func(context.Context, authorization.Info, repositories.ListBuildpacksMessage) ([]repositories.BuildpackRecord, error) listBuildpacksMutex sync.RWMutex listBuildpacksArgsForCall []struct { arg1 context.Context arg2 authorization.Info + arg3 repositories.ListBuildpacksMessage } listBuildpacksReturns struct { result1 []repositories.BuildpackRecord @@ -29,19 +30,20 @@ type BuildpackRepository struct { invocationsMutex sync.RWMutex } -func (fake *BuildpackRepository) ListBuildpacks(arg1 context.Context, arg2 authorization.Info) ([]repositories.BuildpackRecord, error) { +func (fake *BuildpackRepository) ListBuildpacks(arg1 context.Context, arg2 authorization.Info, arg3 repositories.ListBuildpacksMessage) ([]repositories.BuildpackRecord, error) { fake.listBuildpacksMutex.Lock() ret, specificReturn := fake.listBuildpacksReturnsOnCall[len(fake.listBuildpacksArgsForCall)] fake.listBuildpacksArgsForCall = append(fake.listBuildpacksArgsForCall, struct { arg1 context.Context arg2 authorization.Info - }{arg1, arg2}) + arg3 repositories.ListBuildpacksMessage + }{arg1, arg2, arg3}) stub := fake.ListBuildpacksStub fakeReturns := fake.listBuildpacksReturns - fake.recordInvocation("ListBuildpacks", []interface{}{arg1, arg2}) + fake.recordInvocation("ListBuildpacks", []interface{}{arg1, arg2, arg3}) fake.listBuildpacksMutex.Unlock() if stub != nil { - return stub(arg1, arg2) + return stub(arg1, arg2, arg3) } if specificReturn { return ret.result1, ret.result2 @@ -55,17 +57,17 @@ func (fake *BuildpackRepository) ListBuildpacksCallCount() int { return len(fake.listBuildpacksArgsForCall) } -func (fake *BuildpackRepository) ListBuildpacksCalls(stub func(context.Context, authorization.Info) ([]repositories.BuildpackRecord, error)) { +func (fake *BuildpackRepository) ListBuildpacksCalls(stub func(context.Context, authorization.Info, repositories.ListBuildpacksMessage) ([]repositories.BuildpackRecord, error)) { fake.listBuildpacksMutex.Lock() defer fake.listBuildpacksMutex.Unlock() fake.ListBuildpacksStub = stub } -func (fake *BuildpackRepository) ListBuildpacksArgsForCall(i int) (context.Context, authorization.Info) { +func (fake *BuildpackRepository) ListBuildpacksArgsForCall(i int) (context.Context, authorization.Info, repositories.ListBuildpacksMessage) { fake.listBuildpacksMutex.RLock() defer fake.listBuildpacksMutex.RUnlock() argsForCall := fake.listBuildpacksArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2 + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 } func (fake *BuildpackRepository) ListBuildpacksReturns(result1 []repositories.BuildpackRecord, result2 error) { diff --git a/api/main.go b/api/main.go index fcb3f7ab0..f3422197f 100644 --- a/api/main.go +++ b/api/main.go @@ -209,6 +209,7 @@ func main() { buildpackRepo := repositories.NewBuildpackRepository(cfg.BuilderName, userClientFactory, cfg.RootNamespace, + repositories.NewBuildpackSorter(), ) roleRepo := repositories.NewRoleRepo( userClientFactory, diff --git a/api/payloads/buildpack.go b/api/payloads/buildpack.go index 47da2a5e3..6b81454ce 100644 --- a/api/payloads/buildpack.go +++ b/api/payloads/buildpack.go @@ -4,6 +4,7 @@ import ( "net/url" "code.cloudfoundry.org/korifi/api/payloads/validation" + "code.cloudfoundry.org/korifi/api/repositories" jellidation "github.com/jellydator/validation" ) @@ -11,6 +12,12 @@ type BuildpackList struct { OrderBy string } +func (b BuildpackList) ToMessage() repositories.ListBuildpacksMessage { + return repositories.ListBuildpacksMessage{ + OrderBy: b.OrderBy, + } +} + func (d BuildpackList) SupportedKeys() []string { return []string{"order_by", "per_page", "page"} } diff --git a/api/payloads/buildpack_test.go b/api/payloads/buildpack_test.go index 0e060bd44..e32508481 100644 --- a/api/payloads/buildpack_test.go +++ b/api/payloads/buildpack_test.go @@ -5,9 +5,10 @@ import ( . "github.com/onsi/gomega" "code.cloudfoundry.org/korifi/api/payloads" + "code.cloudfoundry.org/korifi/api/repositories" ) -var _ = Describe("Buildpack", func() { +var _ = Describe("BuildpackList", func() { Describe("Validation", func() { DescribeTable("valid query", func(query string, expectedBuildpackList payloads.BuildpackList) { @@ -34,4 +35,13 @@ var _ = Describe("Buildpack", func() { Entry("invalid order_by", "order_by=foo", "value must be one of"), ) }) + + DescribeTable("ToMessage", + func(buildpackList payloads.BuildpackList, expectedListBuildpacksMessage repositories.ListBuildpacksMessage) { + actualListBuildpacksMessage := buildpackList.ToMessage() + + Expect(actualListBuildpacksMessage).To(Equal(expectedListBuildpacksMessage)) + }, + Entry("created_at", payloads.BuildpackList{OrderBy: "created_at"}, repositories.ListBuildpacksMessage{OrderBy: "created_at"}), + ) }) diff --git a/api/repositories/buildpack_repository.go b/api/repositories/buildpack_repository.go index 4db4167f3..9618d5136 100644 --- a/api/repositories/buildpack_repository.go +++ b/api/repositories/buildpack_repository.go @@ -8,7 +8,9 @@ import ( "code.cloudfoundry.org/korifi/api/authorization" apierrors "code.cloudfoundry.org/korifi/api/errors" + "code.cloudfoundry.org/korifi/api/repositories/compare" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" + "code.cloudfoundry.org/korifi/tools" "github.com/BooleanCat/go-functional/v2/it" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -23,6 +25,7 @@ type BuildpackRepository struct { builderName string userClientFactory authorization.UserK8sClientFactory rootNamespace string + sorter BuildpackSorter } type BuildpackRecord struct { @@ -34,15 +37,64 @@ type BuildpackRecord struct { UpdatedAt *time.Time } -func NewBuildpackRepository(builderName string, userClientFactory authorization.UserK8sClientFactory, rootNamespace string) *BuildpackRepository { +//counterfeiter:generate -o fake -fake-name BuildpackSorter . BuildpackSorter +type BuildpackSorter interface { + Sort(records []BuildpackRecord, order string) []BuildpackRecord +} + +type buildpackSorter struct { + sorter *compare.Sorter[BuildpackRecord] +} + +func NewBuildpackSorter() *buildpackSorter { + return &buildpackSorter{ + sorter: compare.NewSorter(BuildpackComparator), + } +} + +func (s *buildpackSorter) Sort(records []BuildpackRecord, order string) []BuildpackRecord { + return s.sorter.Sort(records, order) +} + +func BuildpackComparator(fieldName string) func(BuildpackRecord, BuildpackRecord) int { + return func(b1, b2 BuildpackRecord) int { + switch fieldName { + case "created_at": + return tools.CompareTimePtr(&b1.CreatedAt, &b2.CreatedAt) + case "-created_at": + return tools.CompareTimePtr(&b2.CreatedAt, &b1.CreatedAt) + case "updated_at": + return tools.CompareTimePtr(b1.UpdatedAt, b2.UpdatedAt) + case "-updated_at": + return tools.CompareTimePtr(b2.UpdatedAt, b1.UpdatedAt) + case "position": + return b1.Position - b2.Position + case "-position": + return b2.Position - b1.Position + } + return 0 + } +} + +type ListBuildpacksMessage struct { + OrderBy string +} + +func NewBuildpackRepository( + builderName string, + userClientFactory authorization.UserK8sClientFactory, + rootNamespace string, + sorter BuildpackSorter, +) *BuildpackRepository { return &BuildpackRepository{ builderName: builderName, userClientFactory: userClientFactory, rootNamespace: rootNamespace, + sorter: sorter, } } -func (r *BuildpackRepository) ListBuildpacks(ctx context.Context, authInfo authorization.Info) ([]BuildpackRecord, error) { +func (r *BuildpackRepository) ListBuildpacks(ctx context.Context, authInfo authorization.Info, message ListBuildpacksMessage) ([]BuildpackRecord, error) { var builderInfo korifiv1alpha1.BuilderInfo userClient, err := r.userClientFactory.BuildClient(authInfo) @@ -81,7 +133,7 @@ func (r *BuildpackRepository) ListBuildpacks(ctx context.Context, authInfo autho return nil, apierrors.NewResourceNotReadyError(fmt.Errorf("BuilderInfo %q not ready: %s", r.builderName, conditionNotReadyMessage)) } - return builderInfoToBuildpackRecords(builderInfo), nil + return r.sorter.Sort(builderInfoToBuildpackRecords(builderInfo), message.OrderBy), nil } func builderInfoToBuildpackRecords(info korifiv1alpha1.BuilderInfo) []BuildpackRecord { diff --git a/api/repositories/buildpack_repository_test.go b/api/repositories/buildpack_repository_test.go index c5fc7f6c1..02e94ba71 100644 --- a/api/repositories/buildpack_repository_test.go +++ b/api/repositories/buildpack_repository_test.go @@ -8,35 +8,56 @@ import ( "k8s.io/apimachinery/pkg/api/meta" . "code.cloudfoundry.org/korifi/api/repositories" + "code.cloudfoundry.org/korifi/api/repositories/fake" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" + "code.cloudfoundry.org/korifi/tools" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gstruct" + gomega_types "github.com/onsi/gomega/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var _ = Describe("BuildpackRepository", func() { - var buildpackRepo *BuildpackRepository + var ( + buildpackRepo *BuildpackRepository + sorter *fake.BuildpackSorter + ) BeforeEach(func() { - buildpackRepo = NewBuildpackRepository(builderName, userClientFactory, rootNamespace) + sorter = new(fake.BuildpackSorter) + sorter.SortStub = func(records []BuildpackRecord, _ string) []BuildpackRecord { + return records + } + + buildpackRepo = NewBuildpackRepository(builderName, userClientFactory, rootNamespace, sorter) }) Describe("ListBuildpacks", func() { + var message ListBuildpacksMessage + + BeforeEach(func() { + message = ListBuildpacksMessage{OrderBy: "foo"} + }) + When("a controller with the configured BuilderName exists", func() { + var buildpacks []BuildpackRecord + BeforeEach(func() { createBuilderInfoWithCleanup(ctx, builderName, "io.buildpacks.stacks.bionic", []buildpackInfo{ {name: "paketo-buildpacks/buildpack-1-1", version: "1.1"}, {name: "paketo-buildpacks/buildpack-2-1", version: "2.1"}, {name: "paketo-buildpacks/buildpack-3-1", version: "3.1"}, }) + + var err error + buildpacks, err = buildpackRepo.ListBuildpacks(context.Background(), authInfo, message) + Expect(err).NotTo(HaveOccurred()) }) It("returns all buildpacks", func() { - buildpackRecords, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo) - Expect(err).NotTo(HaveOccurred()) - Expect(buildpackRecords).To(ConsistOf( + Expect(buildpacks).To(ConsistOf( MatchFields(IgnoreExtras, Fields{ "Name": Equal("paketo-buildpacks/buildpack-1-1"), "Position": Equal(1), @@ -57,11 +78,28 @@ var _ = Describe("BuildpackRepository", func() { }), )) }) + + It("sorts the buildpacks", func() { + Expect(sorter.SortCallCount()).To(Equal(1)) + sortedBuildpacks, field := sorter.SortArgsForCall(0) + Expect(field).To(Equal("foo")) + Expect(sortedBuildpacks).To(ConsistOf( + MatchFields(IgnoreExtras, Fields{ + "Name": Equal("paketo-buildpacks/buildpack-1-1"), + }), + MatchFields(IgnoreExtras, Fields{ + "Name": Equal("paketo-buildpacks/buildpack-2-1"), + }), + MatchFields(IgnoreExtras, Fields{ + "Name": Equal("paketo-buildpacks/buildpack-3-1"), + }), + )) + }) }) When("no build reconcilers exist", func() { It("errors", func() { - _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo) + _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo, message) Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("BuilderInfo %q not found in namespace %q", builderName, rootNamespace)))) }) }) @@ -77,7 +115,7 @@ var _ = Describe("BuildpackRepository", func() { }) It("errors", func() { - _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo) + _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo, message) Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("BuilderInfo %q not found in namespace %q", builderName, rootNamespace)))) }) }) @@ -105,7 +143,7 @@ var _ = Describe("BuildpackRepository", func() { }) It("returns an error with the ready condition message", func() { - _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo) + _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo, message) Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("BuilderInfo %q not ready: this is a test", builderName)))) }) }) @@ -122,7 +160,7 @@ var _ = Describe("BuildpackRepository", func() { }) It("returns an error with a generic message", func() { - _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo) + _, err := buildpackRepo.ListBuildpacks(context.Background(), authInfo, message) Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("BuilderInfo %q not ready: resource not reconciled", builderName)))) }) }) @@ -175,3 +213,45 @@ func createBuilderInfoWithCleanup(ctx context.Context, name, stack string, build Expect(k8sClient.Status().Update(ctx, builderInfo)).To(Succeed()) return builderInfo } + +var _ = DescribeTable("BuildpackSorter", + func(p1, p2 BuildpackRecord, field string, match gomega_types.GomegaMatcher) { + Expect(BuildpackComparator(field)(p1, p2)).To(match) + }, + Entry("created_at", + BuildpackRecord{CreatedAt: time.UnixMilli(1)}, + BuildpackRecord{CreatedAt: time.UnixMilli(2)}, + "created_at", + BeNumerically("<", 0), + ), + Entry("-created_at", + BuildpackRecord{CreatedAt: time.UnixMilli(1)}, + BuildpackRecord{CreatedAt: time.UnixMilli(2)}, + "-created_at", + BeNumerically(">", 0), + ), + Entry("updated_at", + BuildpackRecord{UpdatedAt: tools.PtrTo(time.UnixMilli(1))}, + BuildpackRecord{UpdatedAt: tools.PtrTo(time.UnixMilli(2))}, + "updated_at", + BeNumerically("<", 0), + ), + Entry("-updated_at", + BuildpackRecord{UpdatedAt: tools.PtrTo(time.UnixMilli(1))}, + BuildpackRecord{UpdatedAt: tools.PtrTo(time.UnixMilli(2))}, + "-updated_at", + BeNumerically(">", 0), + ), + Entry("position", + BuildpackRecord{Position: 1}, + BuildpackRecord{Position: 2}, + "position", + BeNumerically("<", 0), + ), + Entry("-position", + BuildpackRecord{Position: 1}, + BuildpackRecord{Position: 2}, + "-position", + BeNumerically(">", 0), + ), +) diff --git a/api/repositories/fake/buildpack_sorter.go b/api/repositories/fake/buildpack_sorter.go new file mode 100644 index 000000000..2b6814e2c --- /dev/null +++ b/api/repositories/fake/buildpack_sorter.go @@ -0,0 +1,118 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package fake + +import ( + "sync" + + "code.cloudfoundry.org/korifi/api/repositories" +) + +type BuildpackSorter struct { + SortStub func([]repositories.BuildpackRecord, string) []repositories.BuildpackRecord + sortMutex sync.RWMutex + sortArgsForCall []struct { + arg1 []repositories.BuildpackRecord + arg2 string + } + sortReturns struct { + result1 []repositories.BuildpackRecord + } + sortReturnsOnCall map[int]struct { + result1 []repositories.BuildpackRecord + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *BuildpackSorter) Sort(arg1 []repositories.BuildpackRecord, arg2 string) []repositories.BuildpackRecord { + var arg1Copy []repositories.BuildpackRecord + if arg1 != nil { + arg1Copy = make([]repositories.BuildpackRecord, len(arg1)) + copy(arg1Copy, arg1) + } + fake.sortMutex.Lock() + ret, specificReturn := fake.sortReturnsOnCall[len(fake.sortArgsForCall)] + fake.sortArgsForCall = append(fake.sortArgsForCall, struct { + arg1 []repositories.BuildpackRecord + arg2 string + }{arg1Copy, arg2}) + stub := fake.SortStub + fakeReturns := fake.sortReturns + fake.recordInvocation("Sort", []interface{}{arg1Copy, arg2}) + fake.sortMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *BuildpackSorter) SortCallCount() int { + fake.sortMutex.RLock() + defer fake.sortMutex.RUnlock() + return len(fake.sortArgsForCall) +} + +func (fake *BuildpackSorter) SortCalls(stub func([]repositories.BuildpackRecord, string) []repositories.BuildpackRecord) { + fake.sortMutex.Lock() + defer fake.sortMutex.Unlock() + fake.SortStub = stub +} + +func (fake *BuildpackSorter) SortArgsForCall(i int) ([]repositories.BuildpackRecord, string) { + fake.sortMutex.RLock() + defer fake.sortMutex.RUnlock() + argsForCall := fake.sortArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *BuildpackSorter) SortReturns(result1 []repositories.BuildpackRecord) { + fake.sortMutex.Lock() + defer fake.sortMutex.Unlock() + fake.SortStub = nil + fake.sortReturns = struct { + result1 []repositories.BuildpackRecord + }{result1} +} + +func (fake *BuildpackSorter) SortReturnsOnCall(i int, result1 []repositories.BuildpackRecord) { + fake.sortMutex.Lock() + defer fake.sortMutex.Unlock() + fake.SortStub = nil + if fake.sortReturnsOnCall == nil { + fake.sortReturnsOnCall = make(map[int]struct { + result1 []repositories.BuildpackRecord + }) + } + fake.sortReturnsOnCall[i] = struct { + result1 []repositories.BuildpackRecord + }{result1} +} + +func (fake *BuildpackSorter) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.sortMutex.RLock() + defer fake.sortMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *BuildpackSorter) 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 _ repositories.BuildpackSorter = new(BuildpackSorter)