Repository: Khan/genqlient (go)

Issue:
Add subcommands to init config file, and otherwise require the config file exists
Follow-up to #73 : Require the file to exist but make it easy to create an initial config? We could do this by introducing subcommands, e.g.:
```
genqlient init
genqlient generate [CONFIG]
```
That would make it easy for users to get up and running (and the default config could provide pointers into the documentation) while also avoiding any potential confusion.


Pull request description:
Add a flag --init to write a default config
## Summary:
Steve pointed out (#73) that having genqlient with no arguments silently
use a default config file was a bit confusing, and changed it to use
`genqlient.yaml` by default (#74).  Mark pointed out (#76) that this
makes it a bit less convenient when you're starting from scratch; you
have to go create a config file.  In this commit I add a new init flag
that creates you a config file before using it.

Originally the suggestion was to use subcommands, e.g. we'd have
`genqlient init` and `genqlient generate` and so on.  But I couldn't
think of anything else we might want subcommands for in the future, and
it felt a little silly to make you type `generate` each time.  So
instead, I made it a flag, which has the nice property that you can do
`genqlient --init` and it will generate and then use a config file.  (I
mean, maybe it will immediately crash because you don't have a schema,
but hopefully that's still a useful clue as to what to do next!)  The
implmentation was fairly trivial.

Since we now have a nice way to generate a default config, I removed the
default values for most of the options; I've always felt they were
probably more confusing than helpful.  (And indeed, all the users I know
of (Khan/webapp, and the much smaller project Steve was working on, are
setting those options explicitly.)  This required a slight change to
the syntax to say "don't use context", which is probably also net clearer.

I decided this is also a good time to pull in a proper CLI parser (#31);
see ADR-504 for more on that choice.  This also adds some nice help
messages!

Fixes #76, #31.

Issue: https://github.com/Khan/genqlient/issues/76

## Test plan:
```
go run .
go run . --init
go run . --init example/genqlient.yaml     # refuses to clobber
go run . --init example/newgenqlient.yaml
```


Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 4d16002..4a7835d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -50,6 +50,7 @@ linters-settings:
       - github.com/vektah/gqlparser/v2
       - golang.org/x/tools
       - gopkg.in/yaml.v2
+      - github.com/alexflint/go-arg
 
   gocritic:
     # Which checks should be enabled:
diff --git a/generate/config.go b/generate/config.go
index ab4f70a..e96774d 100644
--- a/generate/config.go
+++ b/generate/config.go
@@ -2,19 +2,14 @@ package generate
 
 import (
 	"go/token"
+	"io"
 	"io/ioutil"
+	"os"
 	"path/filepath"
 
 	"gopkg.in/yaml.v2"
 )
 
-var defaultConfig = &Config{
-	Schema:      "schema.graphql",
-	Operations:  []string{"genqlient.graphql"},
-	Generated:   "generated.go",
-	ContextType: "context.Context",
-}
-
 type Config struct {
 	// The filename with the GraphQL schema (in SDL format); defaults to
 	// schema.graphql
@@ -56,9 +51,9 @@ type Config struct {
 	// Set to the fully-qualified name of a Go type which generated helpers
 	// should accept and use as the context.Context for HTTP requests.
 	//
-	// Defaults to context.Context; set to the empty string to omit context
-	// entirely (i.e. use context.Background()).  Must be a type which
-	// implements context.Context.
+	// Defaults to context.Context; set to "-" to omit context entirely (i.e.
+	// use context.Background()).  Must be a type which implements
+	// context.Context.
 	ContextType string `yaml:"context_type"`
 
 	// If set, a function to get a graphql.Client, perhaps from the context.
@@ -157,6 +152,10 @@ func (c *Config) ValidateAndFillDefaults(configFilename string) error {
 		c.ExportOperations = filepath.Join(c.baseDir(), c.ExportOperations)
 	}
 
+	if c.ContextType == "" {
+		c.ContextType = "context.Context"
+	}
+
 	if c.Package == "" {
 		abs, err := filepath.Abs(c.Generated)
 		if err != nil {
@@ -175,23 +174,36 @@ func (c *Config) ValidateAndFillDefaults(configFilename string) error {
 }
 
 func ReadAndValidateConfig(filename string) (*Config, error) {
-	config := *defaultConfig
-	if filename != "" {
-		text, err := ioutil.ReadFile(filename)
-		if err != nil {
-			return nil, errorf(nil, "unreadable config file %v: %v", filename, err)
-		}
+	text, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, errorf(nil, "unreadable config file %v: %v", filename, err)
+	}
 
-		err = yaml.UnmarshalStrict(text, &config)
-		if err != nil {
-			return nil, errorf(nil, "invalid config file %v: %v", filename, err)
-		}
+	var config Config
+	err = yaml.UnmarshalStrict(text, &config)
+	if err != nil {
+		return nil, errorf(nil, "invalid config file %v: %v", filename, err)
 	}
 
-	err := config.ValidateAndFillDefaults(filename)
+	err = config.ValidateAndFillDefaults(filename)
 	if err != nil {
 		return nil, errorf(nil, "invalid config file %v: %v", filename, err)
 	}
 
 	return &config, nil
 }
+
+func initConfig(filename string) error {
+	// TODO(benkraft): Embed this config file into the binary, see
+	// https://github.com/Khan/genqlient/issues/9.
+	r, err := os.Open(filepath.Join(thisDir, "default_genqlient.yaml"))
+	if err != nil {
+		return err
+	}
+	w, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
+	if err != nil {
+		return err
+	}
+	_, err = io.Copy(w, r)
+	return err
+}
diff --git a/generate/default_genqlient.yaml b/generate/default_genqlient.yaml
new file mode 100644
index 0000000..66eda35
--- /dev/null
+++ b/generate/default_genqlient.yaml
@@ -0,0 +1,7 @@
+# Default genqlient config, see 
+#	go doc github.com/Khan/genqlient/generate.Config
+# for more options.
+schema: schema.graphql
+operations:
+- genqlient.graphql
+generated: generated.go
diff --git a/generate/generate.go b/generate/generate.go
index fce9baf..8c9bc7c 100644
--- a/generate/generate.go
+++ b/generate/generate.go
@@ -103,7 +103,7 @@ func newGenerator(
 		}
 	}
 
-	if g.Config.ContextType != "" {
+	if g.Config.ContextType != "-" {
 		_, err := g.addRef(g.Config.ContextType)
 		if err != nil {
 			return nil, fmt.Errorf("invalid context_type: %w", err)
diff --git a/generate/main.go b/generate/main.go
index db83a7f..8841879 100644
--- a/generate/main.go
+++ b/generate/main.go
@@ -6,6 +6,8 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
+
+	"github.com/alexflint/go-arg"
 )
 
 func readConfigGenerateAndWrite(configFilename string) error {
@@ -36,25 +38,32 @@ func readConfigGenerateAndWrite(configFilename string) error {
 	return nil
 }
 
+type cliArgs struct {
+	ConfigFilename string `arg:"positional" placeholder:"CONFIG" default:"genqlient.yaml" help:"path to genqlient configuration (default genqlient.yaml)"`
+	Init           bool   `arg:"--init" help:"write out and use a default config file"`
+}
+
+func (cliArgs) Description() string {
+	return strings.TrimSpace(`
+Generates GraphQL client code for a given schema and queries.
+See https://github.com/Khan/genqlient for full documentation.
+`)
+}
+
 func Main() {
-	var err error
-	defer func() {
+	exitIfError := func(err error) {
 		if err != nil {
 			fmt.Println(err)
 			os.Exit(1)
 		}
-	}()
-
-	switch len(os.Args) {
-	case 2:
-		err = readConfigGenerateAndWrite(os.Args[1])
-	case 1:
-		err = readConfigGenerateAndWrite("genqlient.yaml")
-	default:
-		argv0 := os.Args[0]
-		if strings.Contains(argv0, string(filepath.Separator)+"go-build") {
-			argv0 = "go run github.com/Khan/genqlient"
-		}
-		err = errorf(nil, "usage: %s [config]", argv0)
 	}
+
+	var args cliArgs
+	arg.MustParse(&args)
+	if args.Init {
+		err := initConfig(args.ConfigFilename)
+		exitIfError(err)
+	}
+	err := readConfigGenerateAndWrite(args.ConfigFilename)
+	exitIfError(err)
 }
diff --git a/generate/operation.go.tmpl b/generate/operation.go.tmpl
index 74f309b..c5614b5 100644
--- a/generate/operation.go.tmpl
+++ b/generate/operation.go.tmpl
@@ -11,7 +11,7 @@ package {{.Config.Package}}
 {{range .Operations}}
 {{.Doc}}
 func {{.Name}}(
-    {{if $.Config.ContextType -}}
+    {{if ne $.Config.ContextType "-" -}}
     ctx {{ref $.Config.ContextType}},
     {{end}}
     {{- if not $.Config.ClientGetter -}}
@@ -47,7 +47,7 @@ func {{.Name}}(
 
     var err error
     {{if $.Config.ClientGetter -}}
-    client, err := {{ref $.Config.ClientGetter}}({{if $.Config.ContextType}}ctx{{else}}{{end}})
+    client, err := {{ref $.Config.ClientGetter}}({{if ne $.Config.ContextType "-"}}ctx{{else}}{{end}})
     if err != nil {
         return nil, err
     }
@@ -55,7 +55,7 @@ func {{.Name}}(
 
 	var retval {{.ResponseName}}
     err = client.MakeRequest(
-        {{if $.Config.ContextType}}ctx{{else}}nil{{end}},
+        {{if ne $.Config.ContextType "-"}}ctx{{else}}nil{{end}},
         "{{.Name}}",
         `{{.Body}}`,
         &retval,
diff --git a/generate/template.go b/generate/template.go
index e8887e4..984fdf6 100644
--- a/generate/template.go
+++ b/generate/template.go
@@ -9,6 +9,8 @@ import (
 )
 
 var (
+	// TODO(benkraft): Embed templates into the binary, see
+	// https://github.com/Khan/genqlient/issues/9.
 	_, thisFilename, _, _ = runtime.Caller(0)
 	thisDir               = filepath.Dir(thisFilename)
 )
diff --git a/go.mod b/go.mod
index cd426cb..5498c49 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.14
 
 require (
 	github.com/99designs/gqlgen v0.13.0
+	github.com/alexflint/go-arg v1.4.2
 	github.com/bradleyjkemp/cupaloy/v2 v2.6.0
 	github.com/stretchr/testify v1.7.0
 	github.com/vektah/gqlparser/v2 v2.1.0
diff --git a/go.sum b/go.sum
index f7c511b..53ba6d6 100644
--- a/go.sum
+++ b/go.sum
@@ -4,13 +4,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
 github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
+github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0=
+github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM=
+github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70=
+github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
 github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
 github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
 github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -34,34 +37,29 @@ github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQ
 github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
 github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
 github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
 github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
 github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
 github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns=


Test patch:
diff --git a/generate/generate_test.go b/generate/generate_test.go
index 22aa1d7..6a271b2 100644
--- a/generate/generate_test.go
+++ b/generate/generate_test.go
@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"github.com/Khan/genqlient/internal/testutil"
+	"gopkg.in/yaml.v2"
 )
 
 const (
@@ -80,6 +81,7 @@ func TestGenerate(t *testing.T) {
 				Package:          "test",
 				Generated:        goFilename,
 				ExportOperations: queriesFilename,
+				ContextType:      "-",
 				Bindings: map[string]*TypeBinding{
 					"ID":          {Type: "github.com/Khan/genqlient/internal/testutil.ID"},
 					"DateTime":    {Type: "time.Time"},
@@ -120,6 +122,22 @@ func TestGenerate(t *testing.T) {
 	}
 }
 
+func defaultConfig(t *testing.T) *Config {
+	// Parse the config that `genqlient --init` generates, to make sure that
+	// works.
+	var config Config
+	b, err := ioutil.ReadFile("default_genqlient.yaml")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = yaml.UnmarshalStrict(b, &config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return &config
+}
+
 // TestGenerateWithConfig tests several configuration options that affect
 // generated code but don't require particular query structures to test.
 //
@@ -131,24 +149,20 @@ func TestGenerateWithConfig(t *testing.T) {
 		fakeConfigFilename string
 		config             *Config // omits Schema and Operations, set below.
 	}{
-		{"DefaultConfig", "genqlient.yaml", defaultConfig},
+		{"DefaultConfig", "genqlient.yaml", defaultConfig(t)},
 		{"Subpackage", "genqlient.yaml", &Config{
-			Generated:   "mypkg/myfile.go",
-			ContextType: "context.Context", // (from defaultConfig)
+			Generated: "mypkg/myfile.go",
 		}},
 		{"SubpackageConfig", "mypkg/genqlient.yaml", &Config{
-			Generated:   "myfile.go", // (relative to genqlient.yaml)
-			ContextType: "context.Context",
+			Generated: "myfile.go", // (relative to genqlient.yaml)
 		}},
 		{"PackageName", "genqlient.yaml", &Config{
-			Generated:   "myfile.go",
-			Package:     "mypkg",
-			ContextType: "context.Context",
+			Generated: "myfile.go",
+			Package:   "mypkg",
 		}},
 		{"ExportOperations", "genqlient.yaml", &Config{
 			Generated:        "generated.go",
 			ExportOperations: "operations.json",
-			ContextType:      "context.Context",
 		}},
 		{"CustomContext", "genqlient.yaml", &Config{
 			Generated:   "generated.go",
@@ -156,12 +170,11 @@ func TestGenerateWithConfig(t *testing.T) {
 		}},
 		{"NoContext", "genqlient.yaml", &Config{
 			Generated:   "generated.go",
-			ContextType: "",
+			ContextType: "-",
 		}},
 		{"ClientGetter", "genqlient.yaml", &Config{
 			Generated:    "generated.go",
 			ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromContext",
-			ContextType:  "context.Context",
 		}},
 		{"ClientGetterCustomContext", "genqlient.yaml", &Config{
 			Generated:    "generated.go",
@@ -171,7 +184,7 @@ func TestGenerateWithConfig(t *testing.T) {
 		{"ClientGetterNoContext", "genqlient.yaml", &Config{
 			Generated:    "generated.go",
 			ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromNowhere",
-			ContextType:  "",
+			ContextType:  "-",
 		}},
 	}
 
@@ -240,10 +253,11 @@ func TestGenerateErrors(t *testing.T) {
 
 		t.Run(sourceFilename, func(t *testing.T) {
 			_, err := Generate(&Config{
-				Schema:     filepath.Join(errorsDir, schemaFilename),
-				Operations: []string{filepath.Join(errorsDir, sourceFilename)},
-				Package:    "test",
-				Generated:  os.DevNull,
+				Schema:      filepath.Join(errorsDir, schemaFilename),
+				Operations:  []string{filepath.Join(errorsDir, sourceFilename)},
+				Package:     "test",
+				Generated:   os.DevNull,
+				ContextType: "context.Context",
 				Bindings: map[string]*TypeBinding{
 					"ValidScalar":   {Type: "string"},
 					"InvalidScalar": {Type: "bogus"},