Repository: googleapis/gapic-generator-go
Pull request #1579 (2024-11-04 22:08:20)

feat: split doc.go generation into comments and code portions
This PR splits the current content of generated doc.go into their respective code and documentation generation parts.

* **doc.go** - package docs, canonical import name
* **helpers.go** - DefaultAuthScopes helper, client construction hook definitions, getVersionClient helper

Related: b/377347282



shollyman: > @shollyman Bikeshed: Was there anything wrong with the simpler `helpers.go` name?

Collision avoidance.  Looking internally there's already a number of protos named `helpers.proto` so I wanted to avoid having to add special collision logic.
shollyman: > > @shollyman Bikeshed: Was there anything wrong with the simpler `helpers.go` name?
> 
> Collision avoidance. Looking internally there's already a number of protos named `helpers.proto` so I wanted to avoid having to add special collision logic.

Though in retrospect, the protoc generator produces files with the `.pb.go` suffix so the risk of collision is smaller.  I'm happy to switch back to `helpers.go` in that case.

Patch:
diff --git a/internal/gengapic/doc_file.go b/internal/gengapic/doc_file.go
index 0801e10..5f8e53b 100644
--- a/internal/gengapic/doc_file.go
+++ b/internal/gengapic/doc_file.go
@@ -15,14 +15,11 @@
 package gengapic
 
 import (
-	"sort"
 	"strings"
 
 	"github.com/googleapis/gapic-generator-go/internal/license"
 	"github.com/googleapis/gapic-generator-go/internal/pbinfo"
 	"github.com/googleapis/gapic-generator-go/internal/printer"
-	"google.golang.org/genproto/googleapis/api/annotations"
-	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/types/descriptorpb"
 )
 
@@ -30,7 +27,7 @@ import (
 //
 // Since it's the only file that needs to write package documentation and canonical import,
 // it does not use g.commit().
-func (g *generator) genDocFile(year int, scopes []string, serv *descriptorpb.ServiceDescriptorProto) {
+func (g *generator) genDocFile(year int, serv *descriptorpb.ServiceDescriptorProto) {
 	p := g.printf
 
 	p(license.Apache, year)
@@ -125,56 +122,6 @@ func (g *generator) genDocFile(year int, scopes []string, serv *descriptorpb.Ser
 	p("// [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors")
 	p("package %s // import %q", g.opts.pkgName, g.opts.pkgPath)
 	p("")
-
-	p("import (")
-	p("%s%q", "\t", "context")
-	p("")
-	p("%s%q", "\t", "google.golang.org/api/option")
-	p(")")
-	p("")
-
-	p("// For more information on implementing a client constructor hook, see")
-	p("// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.")
-	p("type clientHookParams struct{}")
-	p("type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)")
-	p("")
-
-	p("var versionClient string")
-	p("")
-	p("func getVersionClient() string {")
-	p(`  if versionClient == "" {`)
-	p(`    return "UNKNOWN"`)
-	p("  }")
-	p("  return versionClient")
-	p("}")
-	p("")
-
-	p("// DefaultAuthScopes reports the default set of authentication scopes to use with this package.")
-	p("func DefaultAuthScopes() []string {")
-	p("  return []string{")
-	for _, sc := range scopes {
-		p("%q,", sc)
-	}
-	p("  }")
-	p("}")
-}
-
-func collectScopes(servs []*descriptorpb.ServiceDescriptorProto) []string {
-	scopeSet := map[string]bool{}
-	for _, s := range servs {
-		eOauthScopes := proto.GetExtension(s.Options, annotations.E_OauthScopes)
-		scopes := strings.Split(eOauthScopes.(string), ",")
-		for _, sc := range scopes {
-			scopeSet[sc] = true
-		}
-	}
-
-	var scopes []string
-	for sc := range scopeSet {
-		scopes = append(scopes, sc)
-	}
-	sort.Strings(scopes)
-	return scopes
 }
 
 func wrapString(str string, max int) []string {
diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go
index cde61b3..388194c 100644
--- a/internal/gengapic/gengapic.go
+++ b/internal/gengapic/gengapic.go
@@ -19,6 +19,7 @@ import (
 	"net/url"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 	"time"
 
@@ -69,7 +70,7 @@ func gen(genReq *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse
 		return nil, err
 	}
 
-	genServs := g.collectServices(genReq)
+	genServs, scopes := g.collectServicesAndScopes(genReq)
 	if len(genServs) == 0 {
 		return &g.resp, nil
 	}
@@ -100,6 +101,11 @@ func gen(genReq *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse
 	// Initialize the model that will collect snippet metadata.
 	g.snippetMetadata = g.newSnippetsMetadata(protoPkg)
 
+	// generate shared code such as client hooks and scopes.
+	if err := g.genAndCommitHelpers(scopes); err != nil {
+		return &g.resp, fmt.Errorf("error generating helper file: %v", err)
+	}
+
 	for _, s := range genServs {
 		// TODO(pongad): gapic-generator does not remove the package name here,
 		// so even though the client for LoggingServiceV2 is just "Client"
@@ -150,10 +156,8 @@ func gen(genReq *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse
 		return nil, err
 	}
 	g.reset()
-	scopes := collectScopes(genServs)
 	serv := genServs[0]
-
-	g.genDocFile(time.Now().Year(), scopes, serv)
+	g.genDocFile(time.Now().Year(), serv)
 	g.resp.File = append(g.resp.File, &pluginpb.CodeGeneratorResponse_File{
 		Name:    proto.String(filepath.Join(g.opts.outDir, "doc.go")),
 		Content: proto.String(g.pt.String()),
@@ -184,8 +188,9 @@ func gen(genReq *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse
 	return &g.resp, nil
 }
 
-// Collects the proto services to generate GAPICs for from the CodeGeneratorRequest.
-func (g *generator) collectServices(genReq *pluginpb.CodeGeneratorRequest) (genServs []*descriptorpb.ServiceDescriptorProto) {
+// Collects the proto services and scopes to generate GAPICs for from the CodeGeneratorRequest.
+func (g *generator) collectServicesAndScopes(genReq *pluginpb.CodeGeneratorRequest) (genServs []*descriptorpb.ServiceDescriptorProto, scopes []string) {
+	scopeSet := map[string]bool{}
 	for _, f := range genReq.GetProtoFile() {
 		if !strContains(genReq.GetFileToGenerate(), f.GetName()) {
 			continue
@@ -193,11 +198,75 @@ func (g *generator) collectServices(genReq *pluginpb.CodeGeneratorRequest) (genS
 		if !g.includeMixinInputFile(f.GetName()) {
 			continue
 		}
+		// record service(s) present in each file.
 		genServs = append(genServs, f.GetService()...)
+
+		// record encountered scopes in each service.
+		servs := f.GetService()
+		for _, s := range servs {
+			eOauthScopes := proto.GetExtension(s.Options, annotations.E_OauthScopes)
+			if scopeStr, ok := eOauthScopes.(string); ok {
+				if len(scopeStr) > 0 {
+					scopes := strings.Split(scopeStr, ",")
+					for _, sc := range scopes {
+						scopeSet[sc] = true
+					}
+				}
+			}
+		}
 	}
+
+	// transform map to ordered list.
+	for sc := range scopeSet {
+		scopes = append(scopes, sc)
+	}
+	sort.Strings(scopes)
 	return
 }
 
+// getAndCommitHelpers commits shared generated code that should be defined only once.
+// Currently, this includes functionality for reporting default scopes, version information,
+// and client constructors hooks.
+func (g *generator) genAndCommitHelpers(scopes []string) error {
+	p := g.printf
+	g.reset()
+	p("import (")
+	p("%s%q", "\t", "context")
+	p("")
+	p("%s%q", "\t", "google.golang.org/api/option")
+	p(")")
+	p("")
+
+	p("// For more information on implementing a client constructor hook, see")
+	p("// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.")
+	p("type clientHookParams struct{}")
+	p("type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)")
+	p("")
+
+	p("var versionClient string")
+	p("")
+	p("func getVersionClient() string {")
+	p(`  if versionClient == "" {`)
+	p(`    return "UNKNOWN"`)
+	p("  }")
+	p("  return versionClient")
+	p("}")
+	p("")
+
+	p("// DefaultAuthScopes reports the default set of authentication scopes to use with this package.")
+	p("func DefaultAuthScopes() []string {")
+	p("  return []string{")
+	for _, sc := range scopes {
+		p("%q,", sc)
+	}
+	p("  }")
+	p("}")
+
+	outFile := filepath.Join(g.opts.outDir, "helpers.go")
+	g.commit(outFile, g.opts.pkgName)
+	return nil
+}
+
 // gen generates client for the given service.
 func (g *generator) gen(serv *descriptorpb.ServiceDescriptorProto) error {
 	servName := pbinfo.ReduceServName(serv.GetName(), g.opts.pkgName)


Test patch:
diff --git a/internal/gengapic/doc_file_test.go b/internal/gengapic/doc_file_test.go
index 18502d6..677529c 100644
--- a/internal/gengapic/doc_file_test.go
+++ b/internal/gengapic/doc_file_test.go
@@ -67,7 +67,7 @@ func TestDocFile(t *testing.T) {
 	} {
 		t.Run(tst.want, func(t *testing.T) {
 			g.opts.relLvl = tst.relLvl
-			g.genDocFile(sample.Year, []string{sample.ServiceOAuthScope}, serv)
+			g.genDocFile(sample.Year, serv)
 			txtdiff.Diff(t, g.pt.String(), tst.want)
 			g.reset()
 		})
@@ -121,7 +121,7 @@ func TestDocFileEmptyService(t *testing.T) {
 	} {
 		t.Run(tst.want, func(t *testing.T) {
 			g.opts.relLvl = tst.relLvl
-			g.genDocFile(sample.Year, []string{sample.ServiceOAuthScope}, serv)
+			g.genDocFile(sample.Year, serv)
 			txtdiff.Diff(t, g.pt.String(), tst.want)
 			g.reset()
 		})
diff --git a/internal/gengapic/gengapic_test.go b/internal/gengapic/gengapic_test.go
index 421a4ec..78c3577 100644
--- a/internal/gengapic/gengapic_test.go
+++ b/internal/gengapic/gengapic_test.go
@@ -26,6 +26,7 @@ import (
 	conf "github.com/googleapis/gapic-generator-go/internal/grpc_service_config"
 	"github.com/googleapis/gapic-generator-go/internal/pbinfo"
 	"github.com/googleapis/gapic-generator-go/internal/snippets"
+	"github.com/googleapis/gapic-generator-go/internal/testing/sample"
 	"github.com/googleapis/gapic-generator-go/internal/txtdiff"
 	"google.golang.org/genproto/googleapis/api/annotations"
 	"google.golang.org/genproto/googleapis/api/serviceconfig"
@@ -1252,10 +1253,11 @@ func TestReturnType(t *testing.T) {
 	}
 }
 
-func TestCollectServices(t *testing.T) {
+func TestCollectServicesAndScopes(t *testing.T) {
 	libraryServ := &descriptorpb.ServiceDescriptorProto{
 		Name: proto.String("Library"),
 	}
+
 	library := &descriptorpb.FileDescriptorProto{
 		Name: proto.String("google/cloud/library/v1/library.proto"),
 		Options: &descriptorpb.FileOptions{
@@ -1269,13 +1271,15 @@ func TestCollectServices(t *testing.T) {
 		toGen           []string
 		fileSet         []*descriptorpb.FileDescriptorProto
 		want            []*descriptorpb.ServiceDescriptorProto
+		wantScopes      []string
 	}{
 		{
-			name:      "simple",
-			goPkgPath: "cloud.google.com/go/library/apiv1",
-			toGen:     []string{library.GetName()},
-			fileSet:   []*descriptorpb.FileDescriptorProto{library},
-			want:      []*descriptorpb.ServiceDescriptorProto{libraryServ},
+			name:       "simple",
+			goPkgPath:  "cloud.google.com/go/library/apiv1",
+			toGen:      []string{library.GetName()},
+			fileSet:    []*descriptorpb.FileDescriptorProto{library},
+			want:       []*descriptorpb.ServiceDescriptorProto{libraryServ},
+			wantScopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
 		},
 		{
 			name:      "ignore-mixins",
@@ -1287,7 +1291,8 @@ func TestCollectServices(t *testing.T) {
 				mixinFiles["google.iam.v1.IAMPolicy"][0],
 				mixinFiles["google.cloud.location.Locations"][0],
 			},
-			want: []*descriptorpb.ServiceDescriptorProto{libraryServ},
+			want:       []*descriptorpb.ServiceDescriptorProto{libraryServ},
+			wantScopes: []string{"scope-a", "scope-b"},
 		},
 		{
 			name:      "include-iam-mixin",
@@ -1302,13 +1307,80 @@ func TestCollectServices(t *testing.T) {
 		},
 	} {
 		g := &generator{opts: &options{pkgPath: tst.goPkgPath}}
-		got := g.collectServices(&pluginpb.CodeGeneratorRequest{
+		setOAuthScopes(libraryServ, tst.wantScopes)
+		gotServices, gotScopes := g.collectServicesAndScopes(&pluginpb.CodeGeneratorRequest{
 			FileToGenerate: tst.toGen,
 			ProtoFile:      tst.fileSet,
 		})
-		if diff := cmp.Diff(got, tst.want, cmp.Comparer(proto.Equal)); diff != "" {
+		if diff := cmp.Diff(gotServices, tst.want, cmp.Comparer(proto.Equal)); diff != "" {
 			t.Errorf("%s: got(-),want(+):\n%s", tst.name, diff)
 		}
+		if diff := cmp.Diff(gotScopes, tst.wantScopes); diff != "" {
+			t.Errorf("%s scopes got(-),want(+):\n%s", tst.name, diff)
+		}
+	}
+}
+
+func TestGenAndCommentHelpers(t *testing.T) {
+	g := generator{
+		apiName:       sample.ServiceTitle,
+		imports:       map[pbinfo.ImportSpec]bool{},
+		serviceConfig: sample.ServiceConfig(),
+		opts: &options{
+			pkgPath:    sample.GoPackagePath,
+			pkgName:    sample.GoPackageName,
+			transports: []transport{grpc, rest},
+		},
+	}
+	inputType := sample.InputType(sample.CreateRequest)
+	outputType := sample.OutputType(sample.Resource)
+	file := sample.File()
+
+	commonTypes(&g)
+	for _, typ := range []*descriptorpb.DescriptorProto{
+		inputType, outputType,
+	} {
+		typName := sample.DescriptorInfoTypeName(typ.GetName())
+		g.descInfo.Type[typName] = typ
+		g.descInfo.ParentFile[typ] = file
+	}
+
+	serv := sample.Service()
+	serv.Method = nil
+	for _, tst := range []struct {
+		description string
+		scopes      []string
+		want        string
+	}{
+		{
+			description: "nil",
+			scopes:      nil,
+			want:        filepath.Join("testdata", "helpers_no_scopes.want"),
+		},
+		{
+			description: "empty",
+			scopes:      []string{},
+			want:        filepath.Join("testdata", "helpers_no_scopes.want"),
+		},
+		{
+			description: "default",
+			scopes:      []string{"https://www.googleapis.com/auth/cloud-platform"},
+			want:        filepath.Join("testdata", "helpers_default_scope.want"),
+		},
+		{
+			description: "multiple",
+			scopes:      []string{"scope-a", "scope-b", "scope-c"},
+			want:        filepath.Join("testdata", "helpers_multiple_scopes.want"),
+		},
+	} {
+		t.Run(tst.description, func(t *testing.T) {
+			if err := g.genAndCommitHelpers(tst.scopes); err != nil {
+				t.Errorf("genAndCommitHelpers: %v", err)
+				return
+			}
+			txtdiff.Diff(t, g.pt.String(), tst.want)
+			g.reset()
+		})
 	}
 }
 
@@ -1319,3 +1391,15 @@ func setHTTPOption(o *descriptorpb.MethodOptions, pattern string) {
 		},
 	})
 }
+
+func setOAuthScopes(o *descriptorpb.ServiceDescriptorProto, scopes []string) {
+	sopts := o.Options
+	if sopts == nil {
+		sopts = &descriptorpb.ServiceOptions{}
+	}
+	proto.ClearExtension(sopts, annotations.E_OauthScopes)
+	if len(scopes) > 0 {
+		proto.SetExtension(sopts, annotations.E_OauthScopes, strings.Join(scopes, ","))
+	}
+	o.Options = sopts
+}
diff --git a/internal/gengapic/testdata/doc_file.want b/internal/gengapic/testdata/doc_file.want
index 97fb473..6f20fab 100644
--- a/internal/gengapic/testdata/doc_file.want
+++ b/internal/gengapic/testdata/doc_file.want
@@ -83,29 +83,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/doc_file_alpha.want b/internal/gengapic/testdata/doc_file_alpha.want
index 299de1d..61161d9 100644
--- a/internal/gengapic/testdata/doc_file_alpha.want
+++ b/internal/gengapic/testdata/doc_file_alpha.want
@@ -85,29 +85,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/doc_file_alpha_emptyservice.want b/internal/gengapic/testdata/doc_file_alpha_emptyservice.want
index b8dcc67..a638173 100644
--- a/internal/gengapic/testdata/doc_file_alpha_emptyservice.want
+++ b/internal/gengapic/testdata/doc_file_alpha_emptyservice.want
@@ -69,29 +69,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/doc_file_beta.want b/internal/gengapic/testdata/doc_file_beta.want
index 018e88b..9f7e701 100644
--- a/internal/gengapic/testdata/doc_file_beta.want
+++ b/internal/gengapic/testdata/doc_file_beta.want
@@ -85,29 +85,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/doc_file_beta_emptyservice.want b/internal/gengapic/testdata/doc_file_beta_emptyservice.want
index 6ea0ad6..da28214 100644
--- a/internal/gengapic/testdata/doc_file_beta_emptyservice.want
+++ b/internal/gengapic/testdata/doc_file_beta_emptyservice.want
@@ -69,29 +69,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want b/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want
index 4ab0138..f8e9b17 100644
--- a/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want
+++ b/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want
@@ -69,29 +69,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/doc_file_emptyservice.want b/internal/gengapic/testdata/doc_file_emptyservice.want
index 9a68f0a..480c886 100644
--- a/internal/gengapic/testdata/doc_file_emptyservice.want
+++ b/internal/gengapic/testdata/doc_file_emptyservice.want
@@ -67,29 +67,3 @@
 // [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
 package secretmanager // import "cloud.google.com/go/secretmanager/apiv1"
 
-import (
-	"context"
-
-	"google.golang.org/api/option"
-)
-
-// For more information on implementing a client constructor hook, see
-// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
-type clientHookParams struct{}
-type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
-
-var versionClient string
-
-func getVersionClient() string {
-	if versionClient == "" {
-		return "UNKNOWN"
-	}
-	return versionClient
-}
-
-// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
-func DefaultAuthScopes() []string {
-	return []string{
-		"https://www.googleapis.com/auth/cloud-platform",
-	}
-}
diff --git a/internal/gengapic/testdata/helpers_default_scope.want b/internal/gengapic/testdata/helpers_default_scope.want
new file mode 100644
index 0000000..475b61b
--- /dev/null
+++ b/internal/gengapic/testdata/helpers_default_scope.want
@@ -0,0 +1,26 @@
+import (
+	"context"
+
+	"google.golang.org/api/option"
+)
+
+// For more information on implementing a client constructor hook, see
+// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
+type clientHookParams struct{}
+type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
+
+var versionClient string
+
+func getVersionClient() string {
+	if versionClient == "" {
+		return "UNKNOWN"
+	}
+	return versionClient
+}
+
+// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
+func DefaultAuthScopes() []string {
+	return []string{
+		"https://www.googleapis.com/auth/cloud-platform",
+	}
+}
diff --git a/internal/gengapic/testdata/helpers_multiple_scopes.want b/internal/gengapic/testdata/helpers_multiple_scopes.want
new file mode 100644
index 0000000..4ee4735
--- /dev/null
+++ b/internal/gengapic/testdata/helpers_multiple_scopes.want
@@ -0,0 +1,28 @@
+import (
+	"context"
+
+	"google.golang.org/api/option"
+)
+
+// For more information on implementing a client constructor hook, see
+// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
+type clientHookParams struct{}
+type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
+
+var versionClient string
+
+func getVersionClient() string {
+	if versionClient == "" {
+		return "UNKNOWN"
+	}
+	return versionClient
+}
+
+// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
+func DefaultAuthScopes() []string {
+	return []string{
+		"scope-a",
+		"scope-b",
+		"scope-c",
+	}
+}
diff --git a/internal/gengapic/testdata/helpers_no_scopes.want b/internal/gengapic/testdata/helpers_no_scopes.want
new file mode 100644
index 0000000..311e126
--- /dev/null
+++ b/internal/gengapic/testdata/helpers_no_scopes.want
@@ -0,0 +1,25 @@
+import (
+	"context"
+
+	"google.golang.org/api/option"
+)
+
+// For more information on implementing a client constructor hook, see
+// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
+type clientHookParams struct{}
+type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
+
+var versionClient string
+
+func getVersionClient() string {
+	if versionClient == "" {
+		return "UNKNOWN"
+	}
+	return versionClient
+}
+
+// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
+func DefaultAuthScopes() []string {
+	return []string{
+	}
+}