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