github.com/rzurga/go-swagger@v0.28.1-0.20211109195225-5d1f453ffa3a/cmd/swagger/commands/diff_test.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "log" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "testing" 13 14 "github.com/go-swagger/go-swagger/cmd/swagger/commands/diff" 15 "github.com/go-swagger/go-swagger/cmd/swagger/commands/internal/cmdtest" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func fixturePath(file string, parts ...string) string { 21 return filepath.Join("..", "..", "..", "fixtures", "diff", strings.Join(append([]string{file}, parts...), "")) 22 } 23 24 type testCaseData struct { 25 name string 26 oldSpec string 27 newSpec string 28 expectedError bool 29 expectedWarning bool 30 expectedLines io.ReadCloser 31 expectedFile string 32 } 33 34 // TestDiffForVariousCombinations - computes the diffs for a number 35 // of scenarios and compares the computed diff with expected diffs 36 func TestDiffForVariousCombinations(t *testing.T) { 37 38 pattern := fixturePath("*.diff.txt") 39 40 // To filter cases for debugging poke an individual case here eg "path", "enum" etc 41 // see the test cases in fixtures/diff 42 // Don't forget to remove it once you're done. 43 // (There's a test at the end to check all cases were run) 44 allTests, err := filepath.Glob(pattern) 45 require.NoErrorf(t, err, "could not find test files") 46 require.False(t, len(allTests) == 0, "could not find test files") 47 48 testCases := makeTestCases(t, allTests) 49 50 for i, tc := range testCases { 51 tc := tc 52 t.Run(tc.name, func(t *testing.T) { 53 cmd := DiffCommand{} 54 cmd.Args.OldSpec = tc.oldSpec 55 cmd.Args.NewSpec = tc.newSpec 56 diffs, err := cmd.getDiffs() 57 58 if tc.expectedError { 59 // edge cases with error 60 require.Error(t, err) 61 return 62 } 63 require.NoError(t, err) 64 65 out, err, warn := diffs.ReportAllDiffs(false) 66 require.NoError(t, err) 67 68 // breaking changes reported with a warning 69 if tc.expectedWarning { 70 assert.Error(t, warn) 71 } else { 72 assert.NoError(t, warn) 73 } 74 75 if !cmdtest.AssertReadersContent(t, true, tc.expectedLines, out) { 76 t.Logf("unexpected content for fixture %q[%d] (file: %s)", tc.name, i, tc.expectedFile) 77 } 78 }) 79 } 80 } 81 82 func TestDiffReadIgnores(t *testing.T) { 83 log.SetOutput(ioutil.Discard) 84 defer func() { 85 log.SetOutput(os.Stdout) 86 }() 87 88 cmd := DiffCommand{ 89 IgnoreFile: fixturePath("ignoreFile.json"), 90 } 91 92 ignores, err := cmd.readIgnores() 93 require.NoError(t, err) 94 require.True(t, len(ignores) > 0) 95 96 isIn := diff.SpecDifference{DifferenceLocation: diff.DifferenceLocation{ 97 Method: "get", 98 Response: 200, 99 URL: "/b/", 100 Node: &diff.Node{Field: "Body", TypeName: "A1", IsArray: true, ChildNode: &diff.Node{Field: "personality", TypeName: "string"}}, 101 }, 102 Code: diff.DeletedEnumValue, 103 Compatibility: diff.NonBreaking, 104 DiffInfo: "crazy", 105 } 106 assert.Contains(t, ignores, isIn) 107 108 // edge case 109 cmd = DiffCommand{ 110 IgnoreFile: "/someplace/wrong", 111 } 112 _, err = cmd.readIgnores() 113 require.Error(t, err) 114 assert.Contains(t, err.Error(), "/someplace/wrong") 115 } 116 117 func TestDiffProcessIgnores(t *testing.T) { 118 log.SetOutput(ioutil.Discard) 119 defer func() { 120 log.SetOutput(os.Stdout) 121 }() 122 123 const namePart = "enum" 124 tc := testCaseData{ 125 name: namePart, 126 oldSpec: fixturePath(namePart, ".v1.json"), 127 newSpec: fixturePath(namePart, ".v2.json"), 128 expectedLines: linesInFile(t, fixturePath("ignoreDiffs.json")), 129 } 130 131 reportFile, err := ioutil.TempFile("", "report.txt") 132 require.NoError(t, err) 133 defer func() { 134 _ = os.Remove(reportFile.Name()) 135 }() 136 137 cmd := DiffCommand{ 138 Format: "json", 139 IgnoreFile: fixturePath("ignoreFile.json"), 140 Destination: reportFile.Name(), 141 } 142 cmd.Args.OldSpec = tc.oldSpec 143 cmd.Args.NewSpec = tc.newSpec 144 145 err = cmd.Execute([]string{tc.oldSpec, tc.newSpec}) 146 require.NoError(t, err) 147 148 output, err := os.Open(cmd.Destination) 149 require.NoError(t, err) 150 defer func() { 151 _ = output.Close() 152 }() 153 154 cmdtest.AssertReadersContent(t, true, tc.expectedLines, output) 155 } 156 157 func TestDiffNoArgs(t *testing.T) { 158 159 cmd := DiffCommand{ 160 Format: "json", 161 IgnoreFile: "", 162 } 163 require.Error(t, cmd.Execute(nil)) 164 165 cmd.Args.NewSpec = "x" 166 require.Error(t, cmd.Execute(nil)) 167 } 168 169 func TestDiffCannotReport(t *testing.T) { 170 log.SetOutput(ioutil.Discard) 171 defer func() { 172 log.SetOutput(os.Stdout) 173 }() 174 175 cmd := DiffCommand{ 176 OnlyBreakingChanges: true, 177 Format: "txt", 178 IgnoreFile: "", 179 Destination: "/someplace/wrong", 180 } 181 const namePart = "enum" 182 cmd.Args.OldSpec = fixturePath(namePart, ".v1.json") 183 cmd.Args.NewSpec = fixturePath(namePart, ".v2.json") 184 err := cmd.Execute(nil) 185 require.Error(t, err) 186 assert.Contains(t, err.Error(), "/someplace/wrong") 187 } 188 189 func TestDiffOnlyBreaking(t *testing.T) { 190 log.SetOutput(ioutil.Discard) 191 defer func() { 192 log.SetOutput(os.Stdout) 193 }() 194 195 reportDir, err := ioutil.TempDir("", "diff-reports") 196 require.NoError(t, err) 197 defer func() { 198 _ = os.RemoveAll(reportDir) 199 }() 200 txtReport := filepath.Join(reportDir, "report.txt") 201 202 cmd := DiffCommand{ 203 OnlyBreakingChanges: true, 204 Format: "txt", 205 IgnoreFile: "", 206 Destination: txtReport, 207 } 208 209 const namePart = "enum" 210 cmd.Args.OldSpec = fixturePath(namePart, ".v1.json") 211 cmd.Args.NewSpec = fixturePath(namePart, ".v2.json") 212 err = cmd.Execute(nil) 213 require.Error(t, err) 214 assert.Contains(t, err.Error(), "compatibility test FAILED") 215 216 actual, err := os.Open(txtReport) 217 require.NoError(t, err) 218 defer func() { 219 _ = actual.Close() 220 }() 221 222 expected, err := os.Open(fixturePath("enum", ".diff.breaking.txt")) 223 require.NoError(t, err) 224 defer func() { 225 _ = expected.Close() 226 }() 227 228 cmdtest.AssertReadersContent(t, true, expected, actual) 229 230 // assert stdout just the same (we do it just once, so there is no race condition on os.Stdout) 231 cmd.Destination = "stdout" 232 output, err := cmdtest.CatchStdOut(t, func() error { return cmd.Execute(nil) }) 233 require.Error(t, err) 234 assert.Contains(t, err.Error(), "compatibility test FAILED") 235 236 _, _ = expected.Seek(0, io.SeekStart) 237 result := bytes.NewBuffer(output) 238 cmdtest.AssertReadersContent(t, true, expected, result) 239 } 240 241 func fixturePart(file string) string { 242 base := filepath.Base(file) 243 parts := strings.Split(base, ".diff.txt") 244 return parts[0] 245 } 246 247 func hasFixtureBreaking(part string) bool { 248 // these fixtures expect some breaking changes 249 switch part { 250 case "enum", "kitchensink", "param", "path", "response", "refprop": 251 return true 252 default: 253 return false 254 } 255 } 256 257 func makeTestCases(t testing.TB, matches []string) []testCaseData { 258 testCases := make([]testCaseData, 0, len(matches)+2) 259 for _, eachFile := range matches { 260 namePart := fixturePart(eachFile) 261 262 testCases = append( 263 testCases, testCaseData{ 264 name: namePart, 265 oldSpec: fixturePath(namePart, ".v1.json"), 266 newSpec: fixturePath(namePart, ".v2.json"), 267 expectedLines: linesInFile(t, fixturePath(namePart, ".diff.txt")), 268 expectedFile: fixturePath(namePart, ".diff.txt"), // only for debugging failed tests 269 expectedWarning: hasFixtureBreaking(namePart), 270 }) 271 } 272 273 // edge cases with errors 274 testCases = append(testCases, testCaseData{ 275 name: "failure to load old spec", 276 oldSpec: "nowhere.json", 277 newSpec: fixturePath("enum", ".v2.json"), 278 expectedError: true, 279 }, 280 testCaseData{ 281 name: "failure to load new spec", 282 oldSpec: fixturePath("enum", ".v1.json"), 283 newSpec: "nowhere.json", 284 expectedError: true, 285 }, 286 ) 287 return testCases 288 } 289 290 func linesInFile(t testing.TB, fileName string) io.ReadCloser { 291 file, err := os.Open(fileName) 292 require.NoError(t, err) 293 return file 294 }