1package codegen
2
3import (
4	"bytes"
5	"go/format"
6	"io/ioutil"
7	"net/http"
8	"testing"
9
10	examplePetstoreClient "github.com/deepmap/oapi-codegen/examples/petstore-expanded"
11	examplePetstore "github.com/deepmap/oapi-codegen/examples/petstore-expanded/echo/api"
12	"github.com/getkin/kin-openapi/openapi3"
13	"github.com/golangci/lint-1"
14	"github.com/stretchr/testify/assert"
15)
16
17func TestExamplePetStoreCodeGeneration(t *testing.T) {
18
19	// Input vars for code generation:
20	packageName := "api"
21	opts := Options{
22		GenerateClient:     true,
23		GenerateEchoServer: true,
24		GenerateTypes:      true,
25		EmbedSpec:          true,
26	}
27
28	// Get a spec from the example PetStore definition:
29	swagger, err := examplePetstore.GetSwagger()
30	assert.NoError(t, err)
31
32	// Run our code generation:
33	code, err := Generate(swagger, packageName, opts)
34	assert.NoError(t, err)
35	assert.NotEmpty(t, code)
36
37	// Check that we have valid (formattable) code:
38	_, err = format.Source([]byte(code))
39	assert.NoError(t, err)
40
41	// Check that we have a package:
42	assert.Contains(t, code, "package api")
43
44	// Check that the client method signatures return response structs:
45	assert.Contains(t, code, "func (c *Client) FindPetByID(ctx context.Context, id int64, reqEditors ...RequestEditorFn) (*http.Response, error) {")
46
47	// Check that the property comments were generated
48	assert.Contains(t, code, "// Unique id of the pet")
49
50	// Check that the summary comment contains newlines
51	assert.Contains(t, code, `// Deletes a pet by ID
52	// (DELETE /pets/{id})
53`)
54
55	// Make sure the generated code is valid:
56	linter := new(lint.Linter)
57	problems, err := linter.Lint("test.gen.go", []byte(code))
58	assert.NoError(t, err)
59	assert.Len(t, problems, 0)
60}
61
62func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) {
63
64	userTemplates := map[string]string{"typedef.tmpl": "//blah"}
65
66	// Input vars for code generation:
67	packageName := "api"
68	opts := Options{
69		GenerateTypes: true,
70		UserTemplates: userTemplates,
71	}
72
73	// Get a spec from the example PetStore definition:
74	swagger, err := examplePetstore.GetSwagger()
75	assert.NoError(t, err)
76
77	// Run our code generation:
78	code, err := Generate(swagger, packageName, opts)
79	assert.NoError(t, err)
80	assert.NotEmpty(t, code)
81
82	// Check that we have valid (formattable) code:
83	_, err = format.Source([]byte(code))
84	assert.NoError(t, err)
85
86	// Check that we have a package:
87	assert.Contains(t, code, "package api")
88
89	// Check that the built-in template has been overriden
90	assert.Contains(t, code, "//blah")
91}
92
93func TestExamplePetStoreParseFunction(t *testing.T) {
94
95	bodyBytes := []byte(`{"id": 5, "name": "testpet", "tag": "cat"}`)
96
97	cannedResponse := &http.Response{
98		StatusCode: 200,
99		Body:       ioutil.NopCloser(bytes.NewReader(bodyBytes)),
100		Header:     http.Header{},
101	}
102	cannedResponse.Header.Add("Content-type", "application/json")
103
104	findPetByIDResponse, err := examplePetstoreClient.ParseFindPetByIDResponse(cannedResponse)
105	assert.NoError(t, err)
106	assert.NotNil(t, findPetByIDResponse.JSON200)
107	assert.Equal(t, int64(5), findPetByIDResponse.JSON200.Id)
108	assert.Equal(t, "testpet", findPetByIDResponse.JSON200.Name)
109	assert.NotNil(t, findPetByIDResponse.JSON200.Tag)
110	assert.Equal(t, "cat", *findPetByIDResponse.JSON200.Tag)
111}
112
113func TestExampleOpenAPICodeGeneration(t *testing.T) {
114
115	// Input vars for code generation:
116	packageName := "testswagger"
117	opts := Options{
118		GenerateClient:     true,
119		GenerateEchoServer: true,
120		GenerateTypes:      true,
121		EmbedSpec:          true,
122	}
123
124	// Get a spec from the test definition in this file:
125	swagger, err := openapi3.NewLoader().LoadFromData([]byte(testOpenAPIDefinition))
126	assert.NoError(t, err)
127
128	// Run our code generation:
129	code, err := Generate(swagger, packageName, opts)
130	assert.NoError(t, err)
131	assert.NotEmpty(t, code)
132
133	// Check that we have valid (formattable) code:
134	_, err = format.Source([]byte(code))
135	assert.NoError(t, err)
136
137	// Check that we have a package:
138	assert.Contains(t, code, "package testswagger")
139
140	// Check that response structs are generated correctly:
141	assert.Contains(t, code, "type GetTestByNameResponse struct {")
142
143	// Check that response structs contains fallbacks to interface for invalid types:
144	// Here an invalid array with no items.
145	assert.Contains(t, code, `
146type GetTestByNameResponse struct {
147	Body         []byte
148	HTTPResponse *http.Response
149	JSON200      *[]Test
150	XML200       *[]Test
151	JSON422      *[]interface{}
152	XML422       *[]interface{}
153	JSONDefault  *Error
154}`)
155
156	// Check that the helper methods are generated correctly:
157	assert.Contains(t, code, "func (r GetTestByNameResponse) Status() string {")
158	assert.Contains(t, code, "func (r GetTestByNameResponse) StatusCode() int {")
159	assert.Contains(t, code, "func ParseGetTestByNameResponse(rsp *http.Response) (*GetTestByNameResponse, error) {")
160
161	// Check the client method signatures:
162	assert.Contains(t, code, "type GetTestByNameParams struct {")
163	assert.Contains(t, code, "Top *int `json:\"$top,omitempty\"`")
164	assert.Contains(t, code, "func (c *Client) GetTestByName(ctx context.Context, name string, params *GetTestByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) {")
165	assert.Contains(t, code, "func (c *ClientWithResponses) GetTestByNameWithResponse(ctx context.Context, name string, params *GetTestByNameParams, reqEditors ...RequestEditorFn) (*GetTestByNameResponse, error) {")
166	assert.Contains(t, code, "DeadSince *time.Time    `json:\"dead_since,omitempty\" tag1:\"value1\" tag2:\"value2\"`")
167
168	// Make sure the generated code is valid:
169	linter := new(lint.Linter)
170	problems, err := linter.Lint("test.gen.go", []byte(code))
171	assert.NoError(t, err)
172	assert.Len(t, problems, 0)
173}
174
175const testOpenAPIDefinition = `
176openapi: 3.0.1
177
178info:
179  title: OpenAPI-CodeGen Test
180  description: 'This is a test OpenAPI Spec'
181  version: 1.0.0
182
183servers:
184- url: https://test.oapi-codegen.com/v2
185- url: http://test.oapi-codegen.com/v2
186
187paths:
188  /test/{name}:
189    get:
190      tags:
191      - test
192      summary: Get test
193      operationId: getTestByName
194      parameters:
195      - name: name
196        in: path
197        required: true
198        schema:
199          type: string
200      - name: $top
201        in: query
202        required: false
203        schema:
204          type: integer
205      responses:
206        200:
207          description: Success
208          content:
209            application/xml:
210              schema:
211                type: array
212                items:
213                  $ref: '#/components/schemas/Test'
214            application/json:
215              schema:
216                type: array
217                items:
218                  $ref: '#/components/schemas/Test'
219        422:
220          description: InvalidArray
221          content:
222            application/xml:
223              schema:
224                type: array
225            application/json:
226              schema:
227                type: array
228        default:
229          description: Error
230          content:
231            application/json:
232              schema:
233                $ref: '#/components/schemas/Error'
234  /cat:
235    get:
236      tags:
237      - cat
238      summary: Get cat status
239      operationId: getCatStatus
240      responses:
241        200:
242          description: Success
243          content:
244            application/json:
245              schema:
246                oneOf:
247                - $ref: '#/components/schemas/CatAlive'
248                - $ref: '#/components/schemas/CatDead'
249            application/xml:
250              schema:
251                anyOf:
252                - $ref: '#/components/schemas/CatAlive'
253                - $ref: '#/components/schemas/CatDead'
254            application/yaml:
255              schema:
256                allOf:
257                - $ref: '#/components/schemas/CatAlive'
258                - $ref: '#/components/schemas/CatDead'
259        default:
260          description: Error
261          content:
262            application/json:
263              schema:
264                $ref: '#/components/schemas/Error'
265
266components:
267  schemas:
268
269    Test:
270      properties:
271        name:
272          type: string
273        cases:
274          type: array
275          items:
276            $ref: '#/components/schemas/TestCase'
277
278    TestCase:
279      properties:
280        name:
281          type: string
282        command:
283          type: string
284
285    Error:
286      properties:
287        code:
288          type: integer
289          format: int32
290        message:
291          type: string
292
293    CatAlive:
294      properties:
295        name:
296          type: string
297        alive_since:
298          type: string
299          format: date-time
300
301    CatDead:
302      properties:
303        name:
304          type: string
305        dead_since:
306          type: string
307          format: date-time
308          x-oapi-codegen-extra-tags:
309            tag1: value1
310            tag2: value2
311        cause:
312          type: string
313          enum: [car, dog, oldage]
314`
315