github.com/ManabuSeki/goa-v1@v1.4.3/goagen/gen_client/generator_test.go (about) 1 package genclient_test 2 3 import ( 4 "bytes" 5 "html/template" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/goadesign/goa/design" 12 "github.com/goadesign/goa/dslengine" 13 "github.com/goadesign/goa/goagen/codegen" 14 genclient "github.com/goadesign/goa/goagen/gen_client" 15 "github.com/goadesign/goa/version" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 var _ = Describe("Generate", func() { 21 const testgenPackagePath = "github.com/goadesign/goa/goagen/gen_client/test_" 22 23 var workspace *codegen.Workspace 24 var outDir string 25 var files []string 26 var genErr error 27 28 BeforeEach(func() { 29 var err error 30 workspace, err = codegen.NewWorkspace("test") 31 Ω(err).ShouldNot(HaveOccurred()) 32 outDir, err = ioutil.TempDir(filepath.Join(workspace.Path, "src"), "") 33 Ω(err).ShouldNot(HaveOccurred()) 34 os.Args = []string{"goagen", "--out=" + outDir, "--design=foo", "--version=" + version.String()} 35 }) 36 37 JustBeforeEach(func() { 38 files, genErr = genclient.Generate() 39 }) 40 41 AfterEach(func() { 42 workspace.Delete() 43 delete(codegen.Reserved, "client") 44 }) 45 46 Context("with a basic design", func() { 47 var ( 48 resourceHeader string 49 clientHeader string 50 userTypesHeader string 51 mediaTypesHeader string 52 ) 53 54 funcs := template.FuncMap{ 55 "sep": func() string { return string(os.PathSeparator) }, 56 } 57 58 genHeader := func(data map[string]string) string { 59 clientHeaderT, err := template.New("context").Funcs(funcs).Parse(clientHeaderTmpl) 60 Ω(err).ShouldNot(HaveOccurred()) 61 var b bytes.Buffer 62 err = clientHeaderT.Execute(&b, data) 63 Ω(err).ShouldNot(HaveOccurred()) 64 return b.String() 65 } 66 67 BeforeEach(func() { 68 codegen.TempCount = 0 69 design.Design = &design.APIDefinition{ 70 Name: "testapi", 71 Consumes: design.DefaultEncoders, 72 Resources: map[string]*design.ResourceDefinition{ 73 "foo": { 74 Name: "foo", 75 Actions: map[string]*design.ActionDefinition{ 76 "show": { 77 Name: "show", 78 Routes: []*design.RouteDefinition{ 79 { 80 Verb: "GET", 81 Path: "", 82 }, 83 }, 84 }, 85 }, 86 }, 87 }, 88 } 89 fooRes := design.Design.Resources["foo"] 90 showAct := fooRes.Actions["show"] 91 showAct.Parent = fooRes 92 showAct.Routes[0].Parent = showAct 93 94 data := map[string]string{ 95 "outDir": outDir, 96 "design": "foo", 97 "tmpDir": filepath.Base(outDir), 98 "version": version.String(), 99 } 100 101 // Generate Headers 102 data["title"] = "Client" 103 clientHeader = genHeader(data) 104 105 data["title"] = "foo Resource Client" 106 resourceHeader = genHeader(data) 107 108 data["title"] = "Application Media Types" 109 mediaTypesHeader = genHeader(data) 110 111 data["title"] = "Application User Types" 112 userTypesHeader = genHeader(data) 113 }) 114 115 It("generates code generated header", func() { 116 Ω(genErr).Should(BeNil()) 117 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 118 Ω(err).ShouldNot(HaveOccurred()) 119 Ω(string(content)).Should(HavePrefix(resourceHeader)) 120 121 content, err = ioutil.ReadFile(filepath.Join(outDir, "client", "client.go")) 122 Ω(err).ShouldNot(HaveOccurred()) 123 Ω(string(content)).Should(HavePrefix(clientHeader)) 124 125 content, err = ioutil.ReadFile(filepath.Join(outDir, "client", "media_types.go")) 126 Ω(err).ShouldNot(HaveOccurred()) 127 Ω(string(content)).Should(HavePrefix(mediaTypesHeader)) 128 129 content, err = ioutil.ReadFile(filepath.Join(outDir, "client", "user_types.go")) 130 Ω(err).ShouldNot(HaveOccurred()) 131 Ω(string(content)).Should(HavePrefix(userTypesHeader)) 132 }) 133 }) 134 135 Context("with a required UUID header", func() { 136 BeforeEach(func() { 137 codegen.TempCount = 0 138 o := design.Object{ 139 "header_name": &design.AttributeDefinition{Type: design.UUID}, 140 } 141 design.Design = &design.APIDefinition{ 142 Name: "testapi", 143 Consumes: design.DefaultEncoders, 144 Resources: map[string]*design.ResourceDefinition{ 145 "foo": { 146 Name: "foo", 147 Actions: map[string]*design.ActionDefinition{ 148 "show": { 149 Name: "show", 150 Routes: []*design.RouteDefinition{ 151 {Verb: "GET", Path: ""}}, 152 Headers: &design.AttributeDefinition{ 153 Type: o, 154 Validation: &dslengine.ValidationDefinition{ 155 Required: []string{"header_name"}, 156 }, 157 }}}, 158 }, 159 }, 160 } 161 fooRes := design.Design.Resources["foo"] 162 showAct := fooRes.Actions["show"] 163 showAct.Parent = fooRes 164 showAct.Routes[0].Parent = showAct 165 }) 166 167 It("generates header initialization code that compiles", func() { 168 Ω(genErr).Should(BeNil()) 169 Ω(files).Should(HaveLen(9)) 170 c, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 171 Ω(err).ShouldNot(HaveOccurred()) 172 content := string(c) 173 Ω(content).Should(ContainSubstring("header.Set(\"header_name\", tmp3)\n")) 174 }) 175 }) 176 177 Context("with querystring params in path", func() { 178 BeforeEach(func() { 179 codegen.TempCount = 0 180 o := design.Object{ 181 "foo": &design.AttributeDefinition{Type: design.String}, 182 "bar": &design.AttributeDefinition{Type: &design.Array{ElemType: &design.AttributeDefinition{Type: design.Integer}}}, 183 "baz": &design.AttributeDefinition{Type: design.DateTime}, 184 "bat": &design.AttributeDefinition{Type: design.UUID}, 185 } 186 design.Design = &design.APIDefinition{ 187 Name: "testapi", 188 Consumes: design.DefaultEncoders, 189 Resources: map[string]*design.ResourceDefinition{ 190 "foo": { 191 Name: "foo", 192 Actions: map[string]*design.ActionDefinition{ 193 "show": { 194 Name: "show", 195 Params: &design.AttributeDefinition{Type: o}, 196 Routes: []*design.RouteDefinition{ 197 { 198 Verb: "GET", 199 Path: "/foo/:foo/bar/:bar/baz/:baz/bat/:bat", 200 }, 201 }, 202 }, 203 }, 204 }, 205 }, 206 } 207 fooRes := design.Design.Resources["foo"] 208 showAct := fooRes.Actions["show"] 209 showAct.Parent = fooRes 210 showAct.Routes[0].Parent = showAct 211 }) 212 213 It("generates path initialization code that uses all defined URL params in proper format", func() { 214 Ω(genErr).Should(BeNil()) 215 Ω(files).Should(HaveLen(9)) 216 c, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 217 Ω(err).ShouldNot(HaveOccurred()) 218 content := string(c) 219 Ω(content).Should(ContainSubstring("func ShowFooPath(")) 220 Ω(content).Should(ContainSubstring(`param0 := foo`)) 221 Ω(content).Should(ContainSubstring(`tmp2 := make([]string, len(bar)) 222 for i, e := range bar { 223 tmp3 := strconv.Itoa(e) 224 tmp2[i] = tmp3 225 } 226 param1 := strings.Join(tmp2, ",")`)) 227 Ω(content).Should(ContainSubstring(`param2 := baz.Format(time.RFC3339)`)) 228 Ω(content).Should(ContainSubstring(`param3 := bat.String()`)) 229 Ω(content).Should(ContainSubstring(`fmt.Sprintf("/foo/%s/bar/%s/baz/%s/bat/%s", param0, param1, param2, param3)`)) 230 }) 231 }) 232 233 Context("with jsonapi like querystring params", func() { 234 BeforeEach(func() { 235 codegen.TempCount = 0 236 o := design.Object{ 237 "fields[foo]": &design.AttributeDefinition{Type: design.String}, 238 "fields[bar]": &design.AttributeDefinition{Type: &design.Array{ElemType: &design.AttributeDefinition{Type: design.String}}}, 239 "fields[baz]": &design.AttributeDefinition{Type: &design.Array{ElemType: &design.AttributeDefinition{Type: design.Integer}}}, 240 "fields[bat]": &design.AttributeDefinition{Type: design.DateTime}, 241 } 242 design.Design = &design.APIDefinition{ 243 Name: "testapi", 244 Consumes: design.DefaultEncoders, 245 Resources: map[string]*design.ResourceDefinition{ 246 "foo": { 247 Name: "foo", 248 Actions: map[string]*design.ActionDefinition{ 249 "show": { 250 Name: "show", 251 Routes: []*design.RouteDefinition{ 252 { 253 Verb: "GET", 254 Path: "", 255 }, 256 }, 257 QueryParams: &design.AttributeDefinition{Type: o}, 258 }, 259 }, 260 }, 261 }, 262 } 263 fooRes := design.Design.Resources["foo"] 264 showAct := fooRes.Actions["show"] 265 showAct.Parent = fooRes 266 showAct.Routes[0].Parent = showAct 267 }) 268 269 It("generates param initialization code that uses the param name given in the design", func() { 270 Ω(genErr).Should(BeNil()) 271 Ω(files).Should(HaveLen(9)) 272 c, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 273 Ω(err).ShouldNot(HaveOccurred()) 274 content := string(c) 275 Ω(content).Should(ContainSubstring("func ShowFooPath(")) 276 Ω(content).Should(ContainSubstring(`values.Set("fields[foo]", *fieldsFoo)`)) 277 Ω(content).Should(ContainSubstring(` for _, p := range fieldsBar { 278 tmp3 := p 279 values.Add("fields[bar]", tmp3) 280 } 281 `)) 282 Ω(content).Should(ContainSubstring(` for _, p := range fieldsBaz { 283 tmp5 := strconv.Itoa(p) 284 values.Add("fields[baz]", tmp5) 285 } 286 `)) 287 Ω(content).Should(ContainSubstring(` tmp4 := fieldsBat.Format(time.RFC3339) 288 values.Set("fields[bat]", tmp4)`)) 289 }) 290 291 Context("with --notool", func() { 292 BeforeEach(func() { 293 os.Args = append(os.Args, "--notool") 294 }) 295 296 It("should not return an error", func() { 297 Ω(genErr).Should(BeNil()) 298 Ω(files).Should(HaveLen(5)) // 9, minus 4 entries for tool paths 299 }) 300 }) 301 }) 302 303 Context("with an action using websocket", func() { 304 BeforeEach(func() { 305 codegen.TempCount = 0 306 o := design.Object{ 307 "fields[foo]": &design.AttributeDefinition{Type: design.String}, 308 "fields[bar]": &design.AttributeDefinition{Type: &design.Array{ElemType: &design.AttributeDefinition{Type: design.String}}}, 309 "fields[baz]": &design.AttributeDefinition{Type: &design.Array{ElemType: &design.AttributeDefinition{Type: design.Integer}}}, 310 "fields[bat]": &design.AttributeDefinition{Type: design.DateTime}, 311 } 312 design.Design = &design.APIDefinition{ 313 Name: "testapi", 314 Consumes: design.DefaultEncoders, 315 Resources: map[string]*design.ResourceDefinition{ 316 "foo": { 317 Name: "foo", 318 Actions: map[string]*design.ActionDefinition{ 319 "show": { 320 Name: "show", 321 Schemes: []string{"ws"}, 322 Routes: []*design.RouteDefinition{ 323 { 324 Verb: "GET", 325 Path: "", 326 }, 327 }, 328 QueryParams: &design.AttributeDefinition{Type: o}, 329 }, 330 }, 331 }, 332 }, 333 } 334 fooRes := design.Design.Resources["foo"] 335 showAct := fooRes.Actions["show"] 336 showAct.Parent = fooRes 337 showAct.Routes[0].Parent = showAct 338 }) 339 340 It("generates param initialization code that uses the param name given in the design", func() { 341 Ω(genErr).Should(BeNil()) 342 Ω(files).Should(HaveLen(9)) 343 c, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 344 Ω(err).ShouldNot(HaveOccurred()) 345 content := string(c) 346 Ω(content).Should(ContainSubstring("func ShowFooPath(")) 347 Ω(content).Should(ContainSubstring(`values.Set("fields[foo]", *fieldsFoo) 348 `)) 349 Ω(content).Should(ContainSubstring(` if fieldsBar != nil { 350 for _, p := range fieldsBar { 351 tmp3 := p 352 values.Add("fields[bar]", tmp3) 353 } 354 } 355 `)) 356 Ω(content).Should(ContainSubstring(` if fieldsBaz != nil { 357 for _, p := range fieldsBaz { 358 tmp5 := strconv.Itoa(p) 359 values.Add("fields[baz]", tmp5) 360 } 361 } 362 `)) 363 Ω(content).Should(ContainSubstring(` tmp4 := fieldsBat.Format(time.RFC3339) 364 values.Set("fields[bat]", tmp4) 365 `)) 366 }) 367 368 Context("with --notool", func() { 369 BeforeEach(func() { 370 os.Args = append(os.Args, "--notool") 371 }) 372 373 It("should not return an error", func() { 374 Ω(genErr).Should(BeNil()) 375 Ω(files).Should(HaveLen(5)) // 9, minus 4 entries for tool paths 376 }) 377 }) 378 }) 379 380 Context("with an action with multiple routes", func() { 381 BeforeEach(func() { 382 design.Design = &design.APIDefinition{ 383 Name: "testapi", 384 Consumes: design.DefaultEncoders, 385 Resources: map[string]*design.ResourceDefinition{ 386 "foo": { 387 Name: "foo", 388 Actions: map[string]*design.ActionDefinition{ 389 "show": { 390 Name: "show", 391 Routes: []*design.RouteDefinition{ 392 { 393 Verb: "GET", 394 Path: "", 395 }, 396 { 397 Verb: "GET", 398 Path: "/foo", 399 }, 400 }, 401 }, 402 }, 403 }, 404 }, 405 } 406 fooRes := design.Design.Resources["foo"] 407 showAct := fooRes.Actions["show"] 408 showAct.Parent = fooRes 409 showAct.Routes[0].Parent = showAct 410 showAct.Routes[1].Parent = showAct 411 }) 412 413 It("generates Path function with unique names", func() { 414 Ω(genErr).Should(BeNil()) 415 Ω(files).Should(HaveLen(9)) 416 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 417 Ω(err).ShouldNot(HaveOccurred()) 418 Ω(content).Should(ContainSubstring("func ShowFooPath(")) 419 Ω(strings.Count(string(content), "func ShowFooPath(")).Should(Equal(1)) 420 Ω(content).Should(ContainSubstring("func ShowFooPath2(")) 421 Ω(strings.Count(string(content), "func ShowFooPath2(")).Should(Equal(1)) 422 }) 423 424 Context("with a file server", func() { 425 BeforeEach(func() { 426 res := design.Design.Resources["foo"] 427 res.FileServers = []*design.FileServerDefinition{ 428 { 429 Parent: res, 430 FilePath: "/swagger/swagger.json", 431 RequestPath: "/swagger.json", 432 }, 433 } 434 }) 435 436 It("generates a Download function", func() { 437 Ω(genErr).Should(BeNil()) 438 Ω(files).Should(HaveLen(9)) 439 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 440 Ω(err).ShouldNot(HaveOccurred()) 441 Ω(content).Should(ContainSubstring("func (c *Client) DownloadSwaggerJSON(")) 442 }) 443 444 }) 445 }) 446 447 Context("with an action with security configured", func() { 448 BeforeEach(func() { 449 codegen.TempCount = 0 450 securitySchemeDef := &design.SecuritySchemeDefinition{ 451 SchemeName: "jwt-1", 452 Kind: design.JWTSecurityKind, 453 } 454 design.Design = &design.APIDefinition{ 455 Name: "testapi", 456 Title: "dummy API with no resource", 457 Description: "I told you it's dummy", 458 Consumes: design.DefaultEncoders, 459 SecuritySchemes: []*design.SecuritySchemeDefinition{ 460 securitySchemeDef, 461 }, 462 Resources: map[string]*design.ResourceDefinition{ 463 "foo": { 464 Name: "foo", 465 Actions: map[string]*design.ActionDefinition{ 466 "show": { 467 Name: "show", 468 QueryParams: &design.AttributeDefinition{ 469 Type: design.Object{ 470 "param": &design.AttributeDefinition{Type: design.Integer}, 471 "time": &design.AttributeDefinition{Type: design.DateTime}, 472 "uuid": &design.AttributeDefinition{Type: design.UUID}, 473 }, 474 }, 475 Routes: []*design.RouteDefinition{ 476 { 477 Verb: "GET", 478 Path: "", 479 }, 480 }, 481 Security: &design.SecurityDefinition{ 482 Scheme: securitySchemeDef, 483 }, 484 }, 485 }, 486 }, 487 }, 488 } 489 fooRes := design.Design.Resources["foo"] 490 showAct := fooRes.Actions["show"] 491 showAct.Parent = fooRes 492 showAct.Routes[0].Parent = showAct 493 }) 494 495 It("generates the correct client Fields", func() { 496 Ω(genErr).Should(BeNil()) 497 Ω(files).Should(HaveLen(9)) 498 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "client.go")) 499 Ω(err).ShouldNot(HaveOccurred()) 500 Ω(content).Should(ContainSubstring("JWT1Signer goaclient.Signer")) 501 Ω(content).Should(ContainSubstring("func (c *Client) SetJWT1Signer(signer goaclient.Signer) {\n c.JWT1Signer = signer\n}")) 502 }) 503 504 It("generates the Signer.Sign call from Action", func() { 505 Ω(genErr).Should(BeNil()) 506 Ω(files).Should(HaveLen(9)) 507 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 508 Ω(err).ShouldNot(HaveOccurred()) 509 Ω(content).Should(ContainSubstring(` if err := c.JWT1Signer.Sign(req); err != nil { 510 return nil, err 511 }`)) 512 }) 513 }) 514 515 Context("with an action with a user type payload", func() { 516 BeforeEach(func() { 517 codegen.TempCount = 0 518 testType := &design.UserTypeDefinition{ 519 AttributeDefinition: &design.AttributeDefinition{ 520 Type: design.Object{ 521 "param": &design.AttributeDefinition{Type: design.Integer}, 522 "time": &design.AttributeDefinition{Type: design.DateTime}, 523 "uuid": &design.AttributeDefinition{Type: design.UUID}, 524 }, 525 }, 526 TypeName: "TestType", 527 } 528 design.Design = &design.APIDefinition{ 529 Types: map[string]*design.UserTypeDefinition{ 530 "TestType": testType, 531 }, 532 Name: "testapi", 533 Title: "dummy API with no resource", 534 Description: "I told you it's dummy", 535 Consumes: design.DefaultEncoders, 536 Resources: map[string]*design.ResourceDefinition{ 537 "foo": { 538 Name: "foo", 539 Actions: map[string]*design.ActionDefinition{ 540 "show": { 541 Name: "show", 542 Routes: []*design.RouteDefinition{ 543 { 544 Verb: "GET", 545 Path: "", 546 }, 547 }, 548 Payload: testType, 549 }, 550 }, 551 }, 552 }, 553 } 554 fooRes := design.Design.Resources["foo"] 555 showAct := fooRes.Actions["show"] 556 showAct.Parent = fooRes 557 showAct.Routes[0].Parent = showAct 558 }) 559 560 It("generates the user type imports", func() { 561 Ω(genErr).Should(BeNil()) 562 Ω(files).Should(HaveLen(9)) 563 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "user_types.go")) 564 Ω(err).ShouldNot(HaveOccurred()) 565 Ω(content).Should(ContainSubstring("uuid \"github.com/goadesign/goa/uuid\"")) 566 }) 567 }) 568 569 Context("with a multipartform action with a user type payload", func() { 570 BeforeEach(func() { 571 codegen.TempCount = 0 572 testType := &design.UserTypeDefinition{ 573 AttributeDefinition: &design.AttributeDefinition{ 574 Type: design.Object{ 575 "param": &design.AttributeDefinition{Type: design.Integer}, 576 "time": &design.AttributeDefinition{Type: design.DateTime}, 577 "uuid": &design.AttributeDefinition{Type: design.UUID}, 578 }, 579 Validation: &dslengine.ValidationDefinition{ 580 Required: []string{"uuid"}, 581 }, 582 }, 583 TypeName: "TestType", 584 } 585 design.Design = &design.APIDefinition{ 586 Types: map[string]*design.UserTypeDefinition{ 587 "TestType": testType, 588 }, 589 Name: "testapi", 590 Title: "dummy API with no resource", 591 Description: "I told you it's dummy", 592 Consumes: design.DefaultEncoders, 593 Resources: map[string]*design.ResourceDefinition{ 594 "foo": { 595 Name: "foo", 596 Actions: map[string]*design.ActionDefinition{ 597 "show": { 598 Name: "show", 599 Routes: []*design.RouteDefinition{ 600 { 601 Verb: "GET", 602 Path: "", 603 }, 604 }, 605 Payload: testType, 606 PayloadMultipart: true, 607 }, 608 }, 609 }, 610 }, 611 } 612 fooRes := design.Design.Resources["foo"] 613 showAct := fooRes.Actions["show"] 614 showAct.Parent = fooRes 615 showAct.Routes[0].Parent = showAct 616 }) 617 618 It("treat non-required param as pointer type", func() { 619 Ω(genErr).Should(BeNil()) 620 Ω(files).Should(HaveLen(9)) 621 content, err := ioutil.ReadFile(filepath.Join(outDir, "client", "foo.go")) 622 Ω(err).ShouldNot(HaveOccurred()) 623 Ω(string(content)).Should(ContainSubstring("tmp_Param := *payload.Param")) 624 Ω(string(content)).Should(ContainSubstring("tmp_UUID := payload.UUID")) 625 }) 626 }) 627 }) 628 629 var _ = Describe("NewGenerator", func() { 630 var generator *genclient.Generator 631 632 var args = struct { 633 api *design.APIDefinition 634 outDir string 635 target string 636 toolDirName string 637 tool string 638 noTool bool 639 }{ 640 api: &design.APIDefinition{ 641 Name: "test api", 642 }, 643 target: "app", 644 toolDirName: "test_dir", 645 tool: "mycli", 646 noTool: true, 647 } 648 649 Context("with options all options set", func() { 650 BeforeEach(func() { 651 652 generator = genclient.NewGenerator( 653 genclient.API(args.api), 654 genclient.OutDir(args.outDir), 655 genclient.Target(args.target), 656 genclient.ToolDirName(args.toolDirName), 657 genclient.Tool(args.tool), 658 genclient.NoTool(args.noTool), 659 ) 660 }) 661 662 It("has all public properties set with expected value", func() { 663 Ω(generator).ShouldNot(BeNil()) 664 Ω(generator.API.Name).Should(Equal(args.api.Name)) 665 Ω(generator.OutDir).Should(Equal(args.outDir)) 666 Ω(generator.Target).Should(Equal(args.target)) 667 Ω(generator.ToolDirName).Should(Equal(args.toolDirName)) 668 Ω(generator.Tool).Should(Equal(args.tool)) 669 Ω(generator.NoTool).Should(Equal(args.noTool)) 670 }) 671 672 }) 673 }) 674 675 const clientHeaderTmpl = `// Code generated by goagen {{ .version }}, DO NOT EDIT. 676 // 677 // API "testapi": {{.title}} 678 // 679 // Command: 680 // $ goagen 681 // --out=$(GOPATH){{sep}}src{{sep}}{{.tmpDir}} 682 // --design={{.design}} 683 // --version={{.version}} 684 `