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  }