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