Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: alternative way to bootstrap the tests #263

Merged
merged 3 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions example/freight_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,22 @@ func Test_FreightService(t *testing.T) {
},
})
}

func Test_FreightService_AlternativeSetup(t *testing.T) {
// Even though no implementation exists, the tests will pass but be skipped.
examplefreightv1.TestFreightService(t, &aipTests{})
}

type aipTests struct{}

var _ examplefreightv1.FreightServiceTestSuiteConfigProvider = &aipTests{}

func (a aipTests) ShipperTestSuiteConfig(_ *testing.T) *examplefreightv1.FreightServiceShipperTestSuiteConfig {
// Returns nil to indicate that it's not ready to be tested.
return nil
}

func (a aipTests) SiteTestSuiteConfig(_ *testing.T) *examplefreightv1.FreightServiceSiteTestSuiteConfig {
// Returns nil to indicate that it's not ready to be tested.
return nil
}
13 changes: 12 additions & 1 deletion internal/plugin/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,20 @@ func serviceTestSuiteName(service protoreflect.ServiceDescriptor) string {
return string(service.Name()) + "TestSuite"
}

func serviceResourceName(
service protoreflect.ServiceDescriptor,
resource *annotations.ResourceDescriptor,
) string {
return string(service.Name()) + resourceType(resource)
}

func resourceTestSuiteConfigName(
service protoreflect.ServiceDescriptor,
resource *annotations.ResourceDescriptor,
) string {
return string(service.Name()) + resourceType(resource) + "TestSuiteConfig"
return serviceResourceName(service, resource) + "TestSuiteConfig"
}

func serviceTestConfigSupplierName(service protoreflect.ServiceDescriptor) string {
return string(service.Name()) + "TestSuiteConfigProvider"
}
10 changes: 7 additions & 3 deletions internal/plugin/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ func (r *resourceGenerator) generateFixture(f *protogen.GeneratedFile) {
})

f.P("type ", resourceTestSuiteConfigName(r.service.Desc, r.resource), " struct {")
f.P("ctx ", context)
f.P("service ", service)
f.P("currParent int")
f.P()

f.P("// Service should return the service that should be tested.")
f.P("// The service will be used for several tests.")
f.P("Service", " func() ", service)
f.P("// Context should return a new context.")
f.P("// The context will be used for several tests.")
f.P("Context", " func() ", context)
if util.HasParent(r.resource) {
f.P("// The parents to use when creating resources.")
f.P("// At least one parent needs to be set. Depending on methods available on the resource,")
Expand Down Expand Up @@ -239,7 +243,7 @@ func (r *resourceGenerator) generateCreate(f *protogen.GeneratedFile) {
f.P("if fx.CreateResource == nil {")
f.P("t.Skip(\"Test skipped because CreateResource not specified on ", fixtureName, "\")")
f.P("}")
f.P("created, err := fx.CreateResource(fx.ctx", parentCallArg, ")")
f.P("created, err := fx.CreateResource(fx.Context()", parentCallArg, ")")
f.P(ident.AssertNilError, "(t, err)")
f.P("return created")
}
Expand Down
79 changes: 77 additions & 2 deletions internal/plugin/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ type serviceGenerator struct {
}

func (s *serviceGenerator) Generate(f *protogen.GeneratedFile) error {
s.generateConfigProvider(f)
s.generateMainTestFunction(f)
s.generateTestFunctions(f)
s.generateFixture(f)
s.generateTestMethods(f)
for i, resource := range s.resources {
Expand All @@ -30,6 +33,74 @@ func (s *serviceGenerator) Generate(f *protogen.GeneratedFile) error {
return nil
}

func (s *serviceGenerator) generateConfigProvider(f *protogen.GeneratedFile) {
t := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "T",
GoImportPath: "testing",
})
name := serviceTestConfigSupplierName(s.service.Desc)
f.P("// ", name, " is the interface to implement to decide which resources")
f.P("// that should be tested and how it's configured.")
f.P("type ", name, " interface {")
for _, resource := range s.resources {
resourceFx := resourceTestSuiteConfigName(s.service.Desc, resource)
f.P("// ", resourceFx, " should return a config, or nil, which means that the tests will be skipped.")
f.P(resourceType(resource), "TestSuiteConfig(t *", t, ") *", resourceFx, "")
}
f.P("}")
f.P()
}

func (s *serviceGenerator) generateMainTestFunction(f *protogen.GeneratedFile) {
t := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "T",
GoImportPath: "testing",
})
funcName := "Test" + string(s.service.Desc.Name())
f.P("// ", funcName, " is the main entrypoint for starting the AIP tests.")
f.P("func ", funcName, "(t *", t, ",s ", serviceTestConfigSupplierName(s.service.Desc), ") {")
for _, resource := range s.resources {
name := resourceTestSuiteConfigName(s.service.Desc, resource)
f.P("test", name, "(t, s)")
}
f.P("}")
f.P()
}

func (s *serviceGenerator) generateTestFunctions(f *protogen.GeneratedFile) {
t := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "T",
GoImportPath: "testing",
})
context := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "Context",
GoImportPath: "context",
})
background := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "Background",
GoImportPath: "context",
})
for _, resource := range s.resources {
name := resourceTestSuiteConfigName(s.service.Desc, resource)
f.P("func test", name, "(t *", t, ",s ", serviceTestConfigSupplierName(s.service.Desc), ") {")
f.P("t.Run(", strconv.Quote(resourceType(resource)), ", func(t *", t, ") {")
f.P("config := s.", resourceType(resource), "TestSuiteConfig(t)")
f.P("if (config == nil) {")
f.P("t.Skip(\"Method ", resourceType(resource), "TestSuiteConfig not implemented\")")
f.P("}")
f.P("if (config.Service == nil) {")
f.P("t.Skip(\"Method ", name, ".Service() not implemented\")")
f.P("}")
f.P("if (config.Context == nil) {")
f.P("config.Context = func() ", context, " { return ", background, "() }")
f.P("}")
f.P("config.test(t)")
f.P("})")
f.P("}")
f.P()
}
}

func (s *serviceGenerator) generateFixture(f *protogen.GeneratedFile) {
testingT := f.QualifiedGoIdent(protogen.GoIdent{
GoName: "T",
Expand Down Expand Up @@ -60,13 +131,17 @@ func (s *serviceGenerator) generateTestMethods(f *protogen.GeneratedFile) {
GoName: "T",
GoImportPath: "testing",
})
service := f.QualifiedGoIdent(protogen.GoIdent{
GoName: s.service.GoName + "Server",
GoImportPath: s.service.Methods[0].Input.GoIdent.GoImportPath,
})
serviceFx := serviceTestSuiteName(s.service.Desc)
for _, resource := range s.resources {
resourceFx := resourceTestSuiteConfigName(s.service.Desc, resource)
f.P("func (fx ", serviceFx, ") Test", resourceType(resource), "(ctx ", context, ", options ", resourceFx, ") {")
f.P("fx.T.Run(", strconv.Quote(resourceType(resource)), ", func(t *", testingT, ") {")
f.P("options.ctx = ctx")
f.P("options.service = fx.Server")
f.P("options.Context = func() ", context, " { return ctx }")
f.P("options.Service = func() ", service, " { return fx.Server", "}")
f.P("options.test(t)")
f.P("})")
f.P("}")
Expand Down
14 changes: 7 additions & 7 deletions internal/util/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (m MethodCreate) Generate(f *protogen.GeneratedFile, response, err, assign
f.P("}")
}

f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
if HasParent(m.Resource) {
f.P("Parent: ", m.Parent, ",")
}
Expand Down Expand Up @@ -59,7 +59,7 @@ type MethodGet struct {
}

func (m MethodGet) Generate(f *protogen.GeneratedFile, response, err, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
f.P("Name: ", m.Name, ",")
f.P("})")
}
Expand All @@ -73,7 +73,7 @@ type MethodBatchGet struct {
}

func (m MethodBatchGet) Generate(f *protogen.GeneratedFile, response, err, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
if HasParent(m.Resource) {
f.P("Parent: ", m.Parent, ",")
}
Expand Down Expand Up @@ -111,7 +111,7 @@ func (m MethodUpdate) Generate(f *protogen.GeneratedFile, response, err, assign
}
f.P("msg.Name = ", m.Name)
}
f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
if m.Msg != "" {
f.P(upper, ":", m.Msg, ",")
} else {
Expand Down Expand Up @@ -154,7 +154,7 @@ type MethodList struct {
}

func (m MethodList) Generate(f *protogen.GeneratedFile, response, err, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
if HasParent(m.Resource) {
f.P("Parent: ", m.Parent, ",")
}
Expand All @@ -177,7 +177,7 @@ type MethodSearch struct {
}

func (m MethodSearch) Generate(f *protogen.GeneratedFile, response, err, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
if HasParent(m.Resource) {
f.P("Parent: ", m.Parent, ",")
}
Expand All @@ -200,7 +200,7 @@ type MethodDelete struct {
}

func (m MethodDelete) Generate(f *protogen.GeneratedFile, response, err, assign string) {
f.P(response, ", ", err, " ", assign, " fx.service.", m.Method.GoName, "(fx.ctx, &", m.Method.Input.GoIdent, "{")
f.P(response, ", ", err, " ", assign, " fx.Service().", m.Method.GoName, "(fx.Context(), &", m.Method.Input.GoIdent, "{") //nolint:lll
if m.Name != "" {
f.P("Name: ", m.Name, ",")
} else {
Expand Down
Loading