1package main 2 3import ( 4 "io/ioutil" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "strings" 9 "testing" 10) 11 12func testCompiler(t *testing.T, inputFile string, referenceFile string, expectErrors bool) { 13 outputFormat := filepath.Ext(referenceFile)[1:] 14 outputFile := strings.Replace(filepath.Base(inputFile), filepath.Ext(inputFile), "."+outputFormat, 1) 15 errorsFile := strings.Replace(filepath.Base(inputFile), filepath.Ext(inputFile), ".errors", 1) 16 // remove any preexisting output files 17 os.Remove(outputFile) 18 os.Remove(errorsFile) 19 // run the compiler 20 var err error 21 var cmd = exec.Command( 22 "gnostic", 23 inputFile, 24 "--"+outputFormat+"-out=.", 25 "--errors-out=.", 26 "--resolve-refs") 27 //t.Log(cmd.Args) 28 err = cmd.Run() 29 if err != nil && !expectErrors { 30 t.Logf("Compile failed: %+v", err) 31 t.FailNow() 32 } 33 // verify the output against a reference 34 var testFile string 35 if expectErrors { 36 testFile = errorsFile 37 } else { 38 testFile = outputFile 39 } 40 err = exec.Command("diff", testFile, referenceFile).Run() 41 if err != nil { 42 t.Logf("Diff failed: %+v", err) 43 t.FailNow() 44 } else { 45 // if the test succeeded, clean up 46 os.Remove(outputFile) 47 os.Remove(errorsFile) 48 } 49} 50 51func testNormal(t *testing.T, inputFile string, referenceFile string) { 52 testCompiler(t, inputFile, referenceFile, false) 53} 54 55func testErrors(t *testing.T, inputFile string, referenceFile string) { 56 testCompiler(t, inputFile, referenceFile, true) 57} 58 59func TestPetstoreJSON(t *testing.T) { 60 testNormal(t, 61 "examples/v2.0/json/petstore.json", 62 "test/v2.0/petstore.text") 63} 64 65func TestPetstoreYAML(t *testing.T) { 66 testNormal(t, 67 "examples/v2.0/yaml/petstore.yaml", 68 "test/v2.0/petstore.text") 69} 70 71func TestSeparateYAML(t *testing.T) { 72 testNormal(t, 73 "examples/v2.0/yaml/petstore-separate/spec/swagger.yaml", 74 "test/v2.0/yaml/petstore-separate/spec/swagger.text") 75} 76 77func TestSeparateJSON(t *testing.T) { 78 testNormal(t, 79 "examples/v2.0/json/petstore-separate/spec/swagger.json", 80 "test/v2.0/yaml/petstore-separate/spec/swagger.text") // yaml and json results should be identical 81} 82 83func TestRemotePetstoreJSON(t *testing.T) { 84 testNormal(t, 85 "https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/json/petstore.json", 86 "test/v2.0/petstore.text") 87} 88 89func TestRemotePetstoreYAML(t *testing.T) { 90 testNormal(t, 91 "https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/yaml/petstore.yaml", 92 "test/v2.0/petstore.text") 93} 94 95func TestRemoteSeparateYAML(t *testing.T) { 96 testNormal(t, 97 "https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/yaml/petstore-separate/spec/swagger.yaml", 98 "test/v2.0/yaml/petstore-separate/spec/swagger.text") 99} 100 101func TestRemoteSeparateJSON(t *testing.T) { 102 testNormal(t, 103 "https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/json/petstore-separate/spec/swagger.json", 104 "test/v2.0/yaml/petstore-separate/spec/swagger.text") 105} 106 107func TestErrorBadProperties(t *testing.T) { 108 testErrors(t, 109 "examples/errors/petstore-badproperties.yaml", 110 "test/errors/petstore-badproperties.errors") 111} 112 113func TestErrorUnresolvedRefs(t *testing.T) { 114 testErrors(t, 115 "examples/errors/petstore-unresolvedrefs.yaml", 116 "test/errors/petstore-unresolvedrefs.errors") 117} 118 119func TestErrorMissingVersion(t *testing.T) { 120 testErrors(t, 121 "examples/errors/petstore-missingversion.yaml", 122 "test/errors/petstore-missingversion.errors") 123} 124 125func testPlugin(t *testing.T, plugin string, inputFile string, outputFile string, referenceFile string) { 126 // remove any preexisting output files 127 os.Remove(outputFile) 128 // run the compiler 129 var err error 130 output, err := exec.Command( 131 "gnostic", 132 "--"+plugin+"-out=-", 133 inputFile).Output() 134 if err != nil { 135 t.Logf("Compile failed: %+v", err) 136 t.FailNow() 137 } 138 _ = ioutil.WriteFile(outputFile, output, 0644) 139 err = exec.Command("diff", outputFile, referenceFile).Run() 140 if err != nil { 141 t.Logf("Diff failed: %+v", err) 142 t.FailNow() 143 } else { 144 // if the test succeeded, clean up 145 os.Remove(outputFile) 146 } 147} 148 149func TestSamplePluginWithPetstore(t *testing.T) { 150 testPlugin(t, 151 "summary", 152 "examples/v2.0/yaml/petstore.yaml", 153 "sample-petstore.out", 154 "test/v2.0/yaml/sample-petstore.out") 155} 156 157func TestErrorInvalidPluginInvocations(t *testing.T) { 158 var err error 159 output, err := exec.Command( 160 "gnostic", 161 "examples/v2.0/yaml/petstore.yaml", 162 "--errors-out=-", 163 "--plugin-out=foo=bar,:abc", 164 "--plugin-out=,foo=bar:abc", 165 "--plugin-out=foo=:abc", 166 "--plugin-out==bar:abc", 167 "--plugin-out=,,:abc", 168 "--plugin-out=foo=bar=baz:abc", 169 ).Output() 170 if err == nil { 171 t.Logf("Invalid invocations were accepted") 172 t.FailNow() 173 } 174 outputFile := "invalid-plugin-invocation.errors" 175 _ = ioutil.WriteFile(outputFile, output, 0644) 176 err = exec.Command("diff", outputFile, "test/errors/invalid-plugin-invocation.errors").Run() 177 if err != nil { 178 t.Logf("Diff failed: %+v", err) 179 t.FailNow() 180 } else { 181 // if the test succeeded, clean up 182 os.Remove(outputFile) 183 } 184} 185 186func TestValidPluginInvocations(t *testing.T) { 187 var err error 188 output, err := exec.Command( 189 "gnostic", 190 "examples/v2.0/yaml/petstore.yaml", 191 "--errors-out=-", 192 // verify an invocation with no parameters 193 "--summary-out=!", // "!" indicates that no output should be generated 194 // verify single pair of parameters 195 "--summary-out=a=b:!", 196 // verify multiple parameters 197 "--summary-out=a=b,c=123,xyz=alphabetagammadelta:!", 198 // verify that special characters / . - _ can be included in parameter keys and values 199 "--summary-out=a/b/c=x/y/z:!", 200 "--summary-out=a.b.c=x.y.z:!", 201 "--summary-out=a-b-c=x-y-z:!", 202 "--summary-out=a_b_c=x_y_z:!", 203 ).Output() 204 if len(output) != 0 { 205 t.Logf("Valid invocations generated invalid errors\n%s", string(output)) 206 t.FailNow() 207 } 208 if err != nil { 209 t.Logf("Valid invocations were not accepted") 210 t.FailNow() 211 } 212} 213 214func TestExtensionHandlerWithLibraryExample(t *testing.T) { 215 outputFile := "library-example-with-ext.text.out" 216 inputFile := "test/library-example-with-ext.json" 217 referenceFile := "test/library-example-with-ext.text.out" 218 219 os.Remove(outputFile) 220 // run the compiler 221 var err error 222 223 command := exec.Command( 224 "gnostic", 225 "--x-sampleone", 226 "--x-sampletwo", 227 "--text-out="+outputFile, 228 "--resolve-refs", 229 inputFile) 230 231 _, err = command.Output() 232 if err != nil { 233 t.Logf("Compile failed for command %v: %+v", command, err) 234 t.FailNow() 235 } 236 //_ = ioutil.WriteFile(outputFile, output, 0644) 237 err = exec.Command("diff", outputFile, referenceFile).Run() 238 if err != nil { 239 t.Logf("Diff failed: %+v", err) 240 t.FailNow() 241 } else { 242 // if the test succeeded, clean up 243 os.Remove(outputFile) 244 } 245} 246 247func TestJSONOutput(t *testing.T) { 248 inputFile := "test/library-example-with-ext.json" 249 250 textFile := "sample.text" 251 jsonFile := "sample.json" 252 textFile2 := "sample2.text" 253 jsonFile2 := "sample2.json" 254 255 os.Remove(textFile) 256 os.Remove(jsonFile) 257 os.Remove(textFile2) 258 os.Remove(jsonFile2) 259 260 var err error 261 262 // Run the compiler once. 263 command := exec.Command( 264 "gnostic", 265 "--text-out="+textFile, 266 "--json-out="+jsonFile, 267 inputFile) 268 _, err = command.Output() 269 if err != nil { 270 t.Logf("Compile failed for command %v: %+v", command, err) 271 t.FailNow() 272 } 273 274 // Run the compiler again, this time on the generated output. 275 command = exec.Command( 276 "gnostic", 277 "--text-out="+textFile2, 278 "--json-out="+jsonFile2, 279 jsonFile) 280 _, err = command.Output() 281 if err != nil { 282 t.Logf("Compile failed for command %v: %+v", command, err) 283 t.FailNow() 284 } 285 286 // Verify that both models have the same internal representation. 287 err = exec.Command("diff", textFile, textFile2).Run() 288 if err != nil { 289 t.Logf("Diff failed: %+v", err) 290 t.FailNow() 291 } else { 292 // if the test succeeded, clean up 293 os.Remove(textFile) 294 os.Remove(jsonFile) 295 os.Remove(textFile2) 296 os.Remove(jsonFile2) 297 } 298} 299 300func TestYAMLOutput(t *testing.T) { 301 inputFile := "test/library-example-with-ext.json" 302 303 textFile := "sample.text" 304 yamlFile := "sample.yaml" 305 textFile2 := "sample2.text" 306 yamlFile2 := "sample2.yaml" 307 308 os.Remove(textFile) 309 os.Remove(yamlFile) 310 os.Remove(textFile2) 311 os.Remove(yamlFile2) 312 313 var err error 314 315 // Run the compiler once. 316 command := exec.Command( 317 "gnostic", 318 "--text-out="+textFile, 319 "--yaml-out="+yamlFile, 320 inputFile) 321 _, err = command.Output() 322 if err != nil { 323 t.Logf("Compile failed for command %v: %+v", command, err) 324 t.FailNow() 325 } 326 327 // Run the compiler again, this time on the generated output. 328 command = exec.Command( 329 "gnostic", 330 "--text-out="+textFile2, 331 "--yaml-out="+yamlFile2, 332 yamlFile) 333 _, err = command.Output() 334 if err != nil { 335 t.Logf("Compile failed for command %v: %+v", command, err) 336 t.FailNow() 337 } 338 339 // Verify that both models have the same internal representation. 340 err = exec.Command("diff", textFile, textFile2).Run() 341 if err != nil { 342 t.Logf("Diff failed: %+v", err) 343 t.FailNow() 344 } else { 345 // if the test succeeded, clean up 346 os.Remove(textFile) 347 os.Remove(yamlFile) 348 os.Remove(textFile2) 349 os.Remove(yamlFile2) 350 } 351} 352 353func testBuilder(version string, t *testing.T) { 354 var err error 355 356 pbFile := "petstore-" + version + ".pb" 357 yamlFile := "petstore.yaml" 358 jsonFile := "petstore.json" 359 textFile := "petstore.text" 360 textReference := "test/" + version + ".0/petstore.text" 361 362 os.Remove(pbFile) 363 os.Remove(textFile) 364 os.Remove(yamlFile) 365 os.Remove(jsonFile) 366 367 // Generate petstore.pb. 368 command := exec.Command( 369 "petstore-builder", 370 "--"+version) 371 _, err = command.Output() 372 if err != nil { 373 t.Logf("Command %v failed: %+v", command, err) 374 t.FailNow() 375 } 376 377 // Convert petstore.pb to yaml and json. 378 command = exec.Command( 379 "gnostic", 380 pbFile, 381 "--json-out="+jsonFile, 382 "--yaml-out="+yamlFile) 383 _, err = command.Output() 384 if err != nil { 385 t.Logf("Command %v failed: %+v", command, err) 386 t.FailNow() 387 } 388 389 // Read petstore.yaml, resolve references, and export text. 390 command = exec.Command( 391 "gnostic", 392 yamlFile, 393 "--resolve-refs", 394 "--text-out="+textFile) 395 _, err = command.Output() 396 if err != nil { 397 t.Logf("Command %v failed: %+v", command, err) 398 t.FailNow() 399 } 400 401 // Verify that the generated text matches our reference. 402 err = exec.Command("diff", textFile, textReference).Run() 403 if err != nil { 404 t.Logf("Diff failed: %+v", err) 405 t.FailNow() 406 } 407 408 // Read petstore.json, resolve references, and export text. 409 command = exec.Command( 410 "gnostic", 411 jsonFile, 412 "--resolve-refs", 413 "--text-out="+textFile) 414 _, err = command.Output() 415 if err != nil { 416 t.Logf("Command %v failed: %+v", command, err) 417 t.FailNow() 418 } 419 420 // Verify that the generated text matches our reference. 421 err = exec.Command("diff", textFile, textReference).Run() 422 if err != nil { 423 t.Logf("Diff failed: %+v", err) 424 t.FailNow() 425 } 426 427 // if the test succeeded, clean up 428 os.Remove(pbFile) 429 os.Remove(textFile) 430 os.Remove(yamlFile) 431 os.Remove(jsonFile) 432} 433 434func TestBuilderV2(t *testing.T) { 435 testBuilder("v2", t) 436} 437 438func TestBuilderV3(t *testing.T) { 439 testBuilder("v3", t) 440} 441 442// OpenAPI 3.0 tests 443 444func TestPetstoreYAML_30(t *testing.T) { 445 testNormal(t, 446 "examples/v3.0/yaml/petstore.yaml", 447 "test/v3.0/petstore.text") 448} 449 450func TestPetstoreJSON_30(t *testing.T) { 451 testNormal(t, 452 "examples/v3.0/json/petstore.json", 453 "test/v3.0/petstore.text") 454} 455 456// Test that empty required fields are exported. 457 458func TestEmptyRequiredFields_v2(t *testing.T) { 459 testNormal(t, 460 "examples/v2.0/yaml/empty-v2.yaml", 461 "test/v2.0/json/empty-v2.json") 462} 463 464func TestEmptyRequiredFields_v3(t *testing.T) { 465 testNormal(t, 466 "examples/v3.0/yaml/empty-v3.yaml", 467 "test/v3.0/json/empty-v3.json") 468} 469