Skip to content

Commit

Permalink
feat(list): add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ericwenn committed Aug 15, 2021
1 parent 14bd33e commit 40bc47a
Show file tree
Hide file tree
Showing 4 changed files with 484 additions and 0 deletions.
36 changes: 36 additions & 0 deletions internal/plugin/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,39 @@ func (m methodUpdate) Generate(f *protogen.GeneratedFile, response string, err s
}
f.P("})")
}

type methodList struct {
resource *annotations.ResourceDescriptor
method *protogen.Method

parent string
pageSize string
pageToken string
}

func (m methodList) Generate(f *protogen.GeneratedFile, response string, err string, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.method.GoName, "(fx.ctx, &", m.method.Input.GoIdent, "{")
if hasParent(m.resource) {
f.P("Parent: ", m.parent, ",")
}
if m.pageSize != "" {
f.P("PageSize: ", m.pageSize, ",")
}
if m.pageToken != "" {
f.P("PageToken: ", m.pageToken, ",")
}
f.P("})")
}

type methodDelete struct {
resource *annotations.ResourceDescriptor
method *protogen.Method

name string
}

func (m methodDelete) Generate(f *protogen.GeneratedFile, response string, err string, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.method.GoName, "(fx.ctx, &", m.method.Input.GoIdent, "{")
f.P("Name: ", m.name, ",")
f.P("})")
}
1 change: 1 addition & 0 deletions internal/plugin/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,6 @@ func (r *resourceGenerator) collectTestCases() []testCase {
r.getTestCase(),
r.batchGetTestCase(),
r.updateTestCase(),
r.listTestCase(),
}
}
257 changes: 257 additions & 0 deletions internal/plugin/testcase_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package plugin

import (
"strconv"

"go.einride.tech/aip/reflect/aipreflect"
"google.golang.org/protobuf/compiler/protogen"
)

func (r *resourceGenerator) listTestCase() testCase {
listMethod, ok := r.standardMethod(aipreflect.MethodTypeList)
if !ok {
return disabledTestCase()
}
createMethod, ok := r.standardMethod(aipreflect.MethodTypeCreate)
if !ok {
return disabledTestCase()
}
// TODO: support LROs for create.
if returnsLRO(createMethod.Desc) {
return disabledTestCase()
}

deleteMethod, hasDelete := r.standardMethod(aipreflect.MethodTypeDelete)

responseResources := aipreflect.GrammaticalName(r.resource.GetPlural()).UpperCamelCase()

return newTestCase("List", func(f *protogen.GeneratedFile) {
testingT := f.QualifiedGoIdent(protogen.GoIdent{GoName: "T", GoImportPath: "testing"})
assertEqual := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "Equal",
GoImportPath: "gotest.tools/v3/assert",
})
assertDeepEqual := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "DeepEqual",
GoImportPath: "gotest.tools/v3/assert",
})
assertNilError := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "NilError",
GoImportPath: "gotest.tools/v3/assert",
})
protocmpTransform := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "Transform",
GoImportPath: "google.golang.org/protobuf/testing/protocmp",
})
cmpoptsSortSlices := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "SortSlices",
GoImportPath: "github.com/google/go-cmp/cmp/cmpopts",
})
statusCode := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "Code",
GoImportPath: "google.golang.org/grpc/status",
})
codesInvalidArgument := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "InvalidArgument",
GoImportPath: "google.golang.org/grpc/codes",
})
codesNotFound := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "NotFound",
GoImportPath: "google.golang.org/grpc/codes",
})

f.P("// Standard methods: List")
f.P("// https://google.aip.dev/132")

if hasParent(r.resource) {
f.P("parent01 := fx.nextParent(t, false)")
f.P("parent02 := fx.nextParent(t, true)")
f.P()
} else {
}

// create 15 under each parent
f.P("const n = 15")
f.P()
if hasParent(r.resource) {
f.P("parent01msgs := make([]*", r.message.GoIdent, ", n)")
f.P("for i := 0; i < n; i++ {")
methodCreate{
resource: r.resource,
method: createMethod,
parent: "parent01",
}.Generate(f, "msg", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P("parent01msgs[i] = msg")
f.P("}")
f.P()
}
f.P("parent02msgs := make([]*", r.message.GoIdent, ", n)")
f.P("for i := 0; i < n; i++ {")
methodCreate{
resource: r.resource,
method: createMethod,
parent: "parent02",
}.Generate(f, "msg", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P("parent02msgs[i] = msg")
f.P("}")

if hasParent(r.resource) {
f.P()
f.P("// Method should fail with InvalidArgument is provided parent is not valid.")
f.P("t.Run(\"invalid parent\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
methodList{
resource: r.resource,
method: listMethod,
parent: strconv.Quote("invalid parent"),
}.Generate(f, "_", "err", ":=")
f.P(assertEqual, "(t, ", codesInvalidArgument, ",", statusCode, "(err), err)")
f.P("})")
}

f.P()
f.P("// Method should fail with InvalidArgument is provided page token is not valid.")
f.P("t.Run(\"invalid page token\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
methodList{
resource: r.resource,
method: listMethod,
parent: "parent01",
pageToken: strconv.Quote("invalid page token"),
}.Generate(f, "_", "err", ":=")
f.P(assertEqual, "(t, ", codesInvalidArgument, ",", statusCode, "(err), err)")
f.P("})")

f.P()
f.P("// Method should fail with InvalidArgument is provided page size is negative.")
f.P("t.Run(\"negative page size\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
methodList{
resource: r.resource,
method: listMethod,
parent: "parent01",
pageSize: "-10",
}.Generate(f, "_", "err", ":=")
f.P(assertEqual, "(t, ", codesInvalidArgument, ",", statusCode, "(err), err)")
f.P("})")

if hasParent(r.resource) {
f.P()
f.P("// If parent is provided the method must only return resources")
f.P("// under that parent.")
f.P("t.Run(\"isolation\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
methodList{
resource: r.resource,
method: listMethod,
parent: "parent02",
pageSize: "999",
}.Generate(f, "response", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P(assertDeepEqual, "(")
f.P("t,")
f.P("parent02msgs,")
f.P("response.", responseResources, ",")
f.P(cmpoptsSortSlices, "(func(a,b *", r.message.GoIdent, ") bool {")
f.P("return a.Name < b.Name")
f.P("}),")
f.P(protocmpTransform, "(),")
f.P(")")
f.P("})")
}

if hasParent(r.resource) {
f.P()
f.P("t.Run(\"pagination\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")

f.P()
f.P("// If there are no more resources, next_page_token should be unset.")
f.P("t.Run(\"next page token\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
methodList{
resource: r.resource,
method: listMethod,
parent: "parent02",
pageSize: "999",
}.Generate(f, "response", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P("assert.Equal(t, \"\", response.NextPageToken)")
f.P("})")
f.P()

f.P("// Listing resource one by one should eventually return all resources created.")
f.P("t.Run(\"one by one\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
f.P("msgs := make([]*", r.message.GoIdent, ", 0, n)")
f.P("var nextPageToken string")
f.P("for {")
methodList{
resource: r.resource,
method: listMethod,
parent: "parent02",
pageSize: "1",
}.Generate(f, "response", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P(assertEqual, "(t, 1, len(response.", responseResources, "))")
f.P("msgs = append(msgs, response.", responseResources, "...)")
f.P("nextPageToken = response.NextPageToken")
f.P("if nextPageToken == \"\" {")
f.P("break")
f.P("}")
f.P("}")
f.P(assertDeepEqual, "(")
f.P("t,")
f.P("parent02msgs,")
f.P("msgs,")
f.P(cmpoptsSortSlices, "(func(a,b *", r.message.GoIdent, ") bool {")
f.P("return a.Name < b.Name")
f.P("}),")
f.P(protocmpTransform, "(),")
f.P(")")
f.P("})")
f.P("})")
f.P()
}

if hasParent(r.resource) && hasDelete {
f.P()
f.P("// Method should not return deleted resources.")
f.P("t.Run(\"deleted\", func(t *", testingT, ") {")
f.P("fx.maybeSkip(t)")
f.P("const nDelete = 5")
f.P("for i := 0; i < nDelete; i++ {")
methodDelete{
method: deleteMethod,
resource: r.resource,
name: "parent02msgs[i].Name",
}.Generate(f, "_", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P("}")
methodList{
resource: r.resource,
method: listMethod,
parent: "parent02",
pageSize: "9999",
}.Generate(f, "response", "err", ":=")
f.P(assertNilError, "(t, err)")
f.P(assertDeepEqual, "(")
f.P("t,")
f.P("parent02msgs[nDelete:],")
f.P("response.", responseResources, ",")
f.P(cmpoptsSortSlices, "(func(a,b *", r.message.GoIdent, ") bool {")
f.P("return a.Name < b.Name")
f.P("}),")
f.P(protocmpTransform, "(),")
f.P(")")
f.P("})")

}

f.P("_ = ", codesNotFound)
f.P("_ = ", protocmpTransform)
f.P("_ = ", cmpoptsSortSlices)
})
}
Loading

0 comments on commit 40bc47a

Please sign in to comment.