github.com/6543-forks/go-swagger@v0.26.0/generator/client_test.go (about) 1 // Copyright 2015 go-swagger maintainers 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package generator 16 17 import ( 18 "io/ioutil" 19 "log" 20 "os" 21 "path/filepath" 22 "strconv" 23 "strings" 24 "testing" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 ) 29 30 const ( 31 basicFixture = "../fixtures/petstores/petstore.json" 32 ) 33 34 func testClientGenOpts() *GenOpts { 35 g := &GenOpts{} 36 g.Target = "." 37 g.APIPackage = defaultAPIPackage 38 g.ModelPackage = defaultModelPackage 39 g.ServerPackage = defaultServerPackage 40 g.ClientPackage = defaultClientPackage 41 g.Principal = "" 42 g.IncludeModel = true 43 g.IncludeHandler = true 44 g.IncludeParameters = true 45 g.IncludeResponses = true 46 g.IncludeSupport = true 47 g.TemplateDir = "" 48 g.DumpData = false 49 g.IsClient = true 50 if err := g.EnsureDefaults(); err != nil { 51 panic(err) 52 } 53 return g 54 } 55 56 func Test_GenerateClient(t *testing.T) { 57 log.SetOutput(ioutil.Discard) 58 59 // exercise safeguards 60 err := GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, nil) 61 assert.Error(t, err) 62 63 opts := testClientGenOpts() 64 opts.TemplateDir = "dir/nowhere" 65 err = GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, opts) 66 assert.Error(t, err) 67 68 opts = testClientGenOpts() 69 opts.TemplateDir = "http://nowhere.com" 70 err = GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, opts) 71 assert.Error(t, err) 72 73 opts = testClientGenOpts() 74 opts.Spec = "dir/nowhere.yaml" 75 err = GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, opts) 76 assert.Error(t, err) 77 78 opts = testClientGenOpts() 79 opts.Spec = basicFixture 80 err = GenerateClient("test", []string{"model1"}, []string{}, opts) 81 assert.Error(t, err) 82 83 opts = testClientGenOpts() 84 // bad content in spec (HTML...) 85 opts.Spec = "https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v2.0/json/petstore.json" 86 err = GenerateClient("test", []string{}, []string{}, opts) 87 assert.Error(t, err) 88 89 opts = testClientGenOpts() 90 // no operations selected 91 opts.Spec = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml" 92 err = GenerateClient("test", []string{}, []string{"wrongOperationID"}, opts) 93 assert.Error(t, err) 94 95 opts = testClientGenOpts() 96 // generate remote spec 97 opts.Spec = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml" 98 cwd, _ := os.Getwd() 99 tft, _ := ioutil.TempDir(cwd, "generated") 100 defer func() { 101 _ = os.RemoveAll(tft) 102 }() 103 opts.Target = tft 104 opts.IsClient = true 105 DefaultSectionOpts(opts) 106 107 defer func() { 108 _ = os.RemoveAll(opts.Target) 109 }() 110 err = GenerateClient("test", []string{}, []string{}, opts) 111 assert.NoError(t, err) 112 113 // just checks this does not fail 114 origStdout := os.Stdout 115 defer func() { 116 os.Stdout = origStdout 117 }() 118 tgt, _ := ioutil.TempDir(cwd, "dumped") 119 defer func() { 120 _ = os.RemoveAll(tgt) 121 }() 122 os.Stdout, _ = os.Create(filepath.Join(tgt, "stdout")) 123 opts.DumpData = true 124 err = GenerateClient("test", []string{}, []string{}, opts) 125 assert.NoError(t, err) 126 _, err = os.Stat(filepath.Join(tgt, "stdout")) 127 assert.NoError(t, err) 128 } 129 130 func assertImports(t testing.TB, baseImport, code string) { 131 assertRegexpInCode(t, baseImport, code) 132 assertRegexpInCode(t, `"`+baseImport+`/abc_linux"`, code) 133 assertRegexpInCode(t, `"`+baseImport+`/abc_linux"`, code) 134 assertRegexpInCode(t, `"`+baseImport+`/abc_test"`, code) 135 assertRegexpInCode(t, `apiops\s+"`+baseImport+`/api"`, code) 136 assertRegexpInCode(t, `"`+baseImport+`/custom"`, code) 137 assertRegexpInCode(t, `"`+baseImport+`/hash_tag_donuts"`, code) 138 assertRegexpInCode(t, `"`+baseImport+`/nr123abc"`, code) 139 assertRegexpInCode(t, `"`+baseImport+`/nr_at_donuts"`, code) 140 assertRegexpInCode(t, `"`+baseImport+`/plus_donuts`, code) 141 assertRegexpInCode(t, `strfmtops "`+baseImport+`/strfmt`, code) 142 assertRegexpInCode(t, `"`+baseImport+`/forced`, code) 143 assertRegexpInCode(t, `"`+baseImport+`/nr12nasty`, code) 144 assertRegexpInCode(t, `"`+baseImport+`/override`, code) 145 assertRegexpInCode(t, `"`+baseImport+`/gtl`, code) 146 assertRegexpInCode(t, `"`+baseImport+`/operationsops`, code) 147 } 148 149 func TestClient(t *testing.T) { 150 log.SetOutput(ioutil.Discard) 151 152 base := os.Getenv("GOPATH") 153 if base == "" { 154 base = "." 155 } else { 156 base = filepath.Join(base, "src") 157 err := os.MkdirAll(base, 0755) 158 require.NoError(t, err) 159 } 160 targetdir, err := ioutil.TempDir(base, "swagger_nogo") 161 require.NoError(t, err, "Failed to create a test target directory: %v", err) 162 163 defer func() { 164 _ = os.RemoveAll(targetdir) 165 log.SetOutput(os.Stdout) 166 }() 167 168 tests := []struct { 169 name string 170 spec string 171 template string 172 wantError bool 173 prepare func(opts *GenOpts) 174 verify func(testing.TB, string) 175 }{ 176 { 177 name: "InvalidSpec", 178 wantError: true, 179 prepare: func(opts *GenOpts) { 180 opts.Spec = invalidSpecExample 181 opts.ValidateSpec = true 182 }, 183 }, 184 { 185 name: "BaseImportDisabled", 186 prepare: func(opts *GenOpts) { 187 opts.LanguageOpts.BaseImportFunc = nil 188 }, 189 wantError: false, 190 }, 191 { 192 name: "Non_existing_contributor_template", 193 template: "NonExistingContributorTemplate", 194 wantError: true, 195 }, 196 { 197 name: "Existing_contributor", 198 template: "stratoscale", 199 wantError: false, 200 }, 201 { 202 name: "packages mangling", 203 wantError: false, 204 spec: filepath.Join("..", "fixtures", "bugs", "2111", "fixture-2111.yaml"), 205 verify: func(t testing.TB, target string) { 206 require.True(t, fileExists(target, "client")) 207 208 // assert package generation based on mangled tags 209 target = filepath.Join(target, "client") 210 assert.True(t, fileExists(target, "abc_linux")) 211 assert.True(t, fileExists(target, "abc_test")) 212 assert.True(t, fileExists(target, "api")) 213 assert.True(t, fileExists(target, "custom")) 214 assert.True(t, fileExists(target, "hash_tag_donuts")) 215 assert.True(t, fileExists(target, "nr123abc")) 216 assert.True(t, fileExists(target, "nr_at_donuts")) 217 assert.True(t, fileExists(target, "operations")) 218 assert.True(t, fileExists(target, "plus_donuts")) 219 assert.True(t, fileExists(target, "strfmt")) 220 assert.True(t, fileExists(target, "forced")) 221 assert.True(t, fileExists(target, "gtl")) 222 assert.True(t, fileExists(target, "nr12nasty")) 223 assert.True(t, fileExists(target, "override")) 224 assert.True(t, fileExists(target, "operationsops")) 225 226 buf, err := ioutil.ReadFile(filepath.Join(target, "foo_client.go")) 227 require.NoError(t, err) 228 229 // assert client import, with deconfliction 230 code := string(buf) 231 baseImport := `swagger_nogo\d+/packages_mangling/client` 232 assertImports(t, baseImport, code) 233 234 assertInCode(t, `cli.Strfmt = strfmtops.New(transport, formats)`, code) 235 assertInCode(t, `cli.API = apiops.New(transport, formats)`, code) 236 assertInCode(t, `cli.Operations = operations.New(transport, formats)`, code) 237 }, 238 }, 239 { 240 name: "packages flattening", 241 wantError: false, 242 spec: filepath.Join("..", "fixtures", "bugs", "2111", "fixture-2111.yaml"), 243 prepare: func(opts *GenOpts) { 244 opts.SkipTagPackages = true 245 }, 246 verify: func(t testing.TB, target string) { 247 require.True(t, fileExists(target, "client")) 248 249 // packages are not created here 250 target = filepath.Join(target, "client") 251 assert.False(t, fileExists(target, "abc_linux")) 252 assert.False(t, fileExists(target, "abc_test")) 253 assert.False(t, fileExists(target, "api")) 254 assert.False(t, fileExists(target, "custom")) 255 assert.False(t, fileExists(target, "hash_tag_donuts")) 256 assert.False(t, fileExists(target, "nr123abc")) 257 assert.False(t, fileExists(target, "nr_at_donuts")) 258 assert.False(t, fileExists(target, "plus_donuts")) 259 assert.False(t, fileExists(target, "strfmt")) 260 assert.False(t, fileExists(target, "forced")) 261 assert.False(t, fileExists(target, "gtl")) 262 assert.False(t, fileExists(target, "nr12nasty")) 263 assert.False(t, fileExists(target, "override")) 264 assert.False(t, fileExists(target, "operationsops")) 265 266 assert.True(t, fileExists(target, "operations")) 267 }, 268 }, 269 { 270 name: "name with trailing API", 271 spec: filepath.Join("..", "fixtures", "bugs", "2278", "fixture-2278.yaml"), 272 wantError: false, 273 }, 274 } 275 276 for i, tt := range tests { 277 t.Run(tt.name, func(t *testing.T) { 278 opts := testClientGenOpts() 279 opts.Spec = basicFixture 280 opts.Target = filepath.Join(targetdir, opts.LanguageOpts.ManglePackageName(tt.name, "client_test"+strconv.Itoa(i))) 281 err := os.MkdirAll(opts.Target, 0755) 282 require.NoError(t, err) 283 284 if tt.spec == "" { 285 opts.Spec = basicFixture 286 } else { 287 opts.Spec = tt.spec 288 } 289 opts.Template = tt.template 290 291 if tt.prepare != nil { 292 tt.prepare(opts) 293 } 294 295 err = GenerateClient("foo", nil, nil, opts) 296 if tt.wantError { 297 require.Errorf(t, err, "expected an error for client build fixture: %s", opts.Spec) 298 } else { 299 require.NoError(t, err, "unexpected error for client build fixture: %s", opts.Spec) 300 } 301 302 if tt.verify != nil { 303 tt.verify(t, opts.Target) 304 } 305 }) 306 } 307 } 308 309 func TestGenClient_1518(t *testing.T) { 310 // test client response handling when unexpected success response kicks in 311 log.SetOutput(ioutil.Discard) 312 defer func() { 313 log.SetOutput(os.Stdout) 314 }() 315 316 opts := testClientGenOpts() 317 opts.Spec = filepath.Join("..", "fixtures", "bugs", "1518", "fixture-1518.yaml") 318 319 cwd, _ := os.Getwd() 320 tft, _ := ioutil.TempDir(cwd, "generated") 321 opts.Target = tft 322 323 defer func() { 324 _ = os.RemoveAll(opts.Target) 325 }() 326 327 err := GenerateClient("client", []string{}, []string{}, opts) 328 if !assert.NoError(t, err) { 329 t.FailNow() 330 } 331 332 fixtureConfig := map[string][]string{ 333 "client/operations/operations_client.go": { // generated file 334 // expected code lines 335 `success, ok := result.(*GetRecords1OK)`, 336 `if ok {`, 337 `return success, nil`, 338 `msg := fmt.Sprintf(`, 339 `panic(msg)`, 340 // expected code lines 341 `success, ok := result.(*GetRecords2OK)`, 342 `if ok {`, 343 `return success, nil`, 344 `unexpectedSuccess := result.(*GetRecords2Default)`, 345 `return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())`, 346 // expected code lines 347 `switch value := result.(type) {`, 348 `case *GetRecords3OK:`, 349 `return value, nil, nil`, 350 `case *GetRecords3Created:`, 351 `return nil, value, nil`, 352 `msg := fmt.Sprintf(`, 353 `panic(msg)`, 354 // expected code lines 355 `switch value := result.(type) {`, 356 `case *GetRecords4OK:`, 357 `return value, nil, nil`, 358 `case *GetRecords4Created:`, 359 `return nil, value, nil`, 360 `unexpectedSuccess := result.(*GetRecords4Default)`, 361 `return nil, nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())`, 362 }, 363 } 364 365 for fileToInspect, expectedCode := range fixtureConfig { 366 code, err := ioutil.ReadFile(filepath.Join(opts.Target, filepath.FromSlash(fileToInspect))) 367 require.NoError(t, err) 368 for line, codeLine := range expectedCode { 369 if !assertInCode(t, strings.TrimSpace(codeLine), string(code)) { 370 t.Logf("Code expected did not match in codegenfile %s for expected line %d: %q", fileToInspect, line, expectedCode[line]) 371 } 372 } 373 } 374 }