github.com/CycloneDX/sbom-utility@v0.16.0/cmd/diff_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  /*
     3   * Licensed to the Apache Software Foundation (ASF) under one or more
     4   * contributor license agreements.  See the NOTICE file distributed with
     5   * this work for additional information regarding copyright ownership.
     6   * The ASF licenses this file to You under the Apache License, Version 2.0
     7   * (the "License"); you may not use this file except in compliance with
     8   * the License.  You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package cmd
    20  
    21  import (
    22  	"fmt"
    23  	"testing"
    24  
    25  	"github.com/CycloneDX/sbom-utility/utils"
    26  )
    27  
    28  const (
    29  	TEST_DIFF_ARRAY_ORDER_CHANGE_BASE  = "test/diff/json-array-order-change-base.json"
    30  	TEST_DIFF_ARRAY_ORDER_CHANGE_DELTA = "test/diff/json-array-order-change-delta.json"
    31  
    32  	TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_DELETE_BASE  = "test/diff/json-array-order-change-with-delete-base.json"
    33  	TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_DELETE_DELTA = "test/diff/json-array-order-change-with-delete-delta.json"
    34  
    35  	TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_BASE  = "test/diff/json-array-order-change-with-add-base.json"
    36  	TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_DELTA = "test/diff/json-array-order-change-with-add-delta.json"
    37  
    38  	TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_AND_DELETE_BASE  = "test/diff/json-array-order-change-with-add-and-delete-base.json"
    39  	TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_AND_DELETE_DELTA = "test/diff/json-array-order-change-with-add-and-delete-delta.json"
    40  
    41  	TEST_DIFF_ARRAY_ORDER_2_CHANGES_BASE  = "test/diff/json-array-order-2-changes-base.json"
    42  	TEST_DIFF_ARRAY_ORDER_2_CHANGES_DELTA = "test/diff/json-array-order-2-changes-delta.json"
    43  )
    44  
    45  // Test CycloneDX BOM deltas
    46  const (
    47  	TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_1_DELTA = "test/diff/cdx-1-4-mature-example-1-delta.json"
    48  	TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_2_DELTA = "test/diff/cdx-1-4-mature-example-2-delta.json"
    49  
    50  	TEST_DIFF_CDX_1_5_VULNERABILITY_BASE     = "test/diff/vulnerability/cdx-1-5-vulnerabilities-base.bom.json"
    51  	TEST_DIFF_CDX_1_5_VULNERABILITY_ADD_1    = "test/diff/vulnerability/cdx-1-5-vulnerabilities-delta-add-1.bom.json"
    52  	TEST_DIFF_CDX_1_5_VULNERABILITY_REMOVE_1 = "test/diff/vulnerability/cdx-1-5-vulnerabilities-delta-remove-1.bom.json"
    53  )
    54  
    55  // Non-standard test files
    56  const (
    57  	TEST_DIFF_PANIC_BASE  = "test/diff/panic/nats1.json"
    58  	TEST_DIFF_PANIC_DELTA = "test/diff/panic/nats2.json"
    59  )
    60  
    61  type DiffTestInfo struct {
    62  	CommonTestInfo
    63  	RevisedFilename string
    64  	Colorize        bool
    65  }
    66  
    67  func (ti *DiffTestInfo) String() string {
    68  	buffer, _ := utils.EncodeAnyToDefaultIndentedJSONStr(ti)
    69  	return buffer.String()
    70  }
    71  
    72  func NewDiffTestInfo(inputFile string, revisedFilename string) *DiffTestInfo {
    73  	var ti = new(DiffTestInfo)
    74  	ti.RevisedFilename = revisedFilename
    75  	var pCommon = &ti.CommonTestInfo
    76  	// Note: Diff is by default "txt" format
    77  	pCommon.InitBasic(inputFile, FORMAT_TEXT, nil)
    78  	return ti
    79  }
    80  
    81  // Tests basic validation and expected errors
    82  func innerDiffTest(t *testing.T, testInfo *DiffTestInfo) (actualError error) {
    83  	getLogger().Enter()
    84  	defer getLogger().Exit()
    85  
    86  	// Copy test parameters to persistent and command-specific flags
    87  	utils.GlobalFlags.PersistentFlags.OutputFile = testInfo.OutputFile
    88  	utils.GlobalFlags.PersistentFlags.OutputFormat = testInfo.OutputFormat
    89  	utils.GlobalFlags.PersistentFlags.InputFile = testInfo.InputFile
    90  	utils.GlobalFlags.DiffFlags.RevisedFile = testInfo.RevisedFilename
    91  	utils.GlobalFlags.DiffFlags.Colorize = testInfo.Colorize
    92  
    93  	getLogger().Tracef("baseFilename: `%s`, revisedFilename=`%s`, actualError=`%T`",
    94  		utils.GlobalFlags.PersistentFlags.InputFile,
    95  		utils.GlobalFlags.DiffFlags.RevisedFile,
    96  		actualError)
    97  
    98  	actualError = Diff(utils.GlobalFlags.PersistentFlags, utils.GlobalFlags.DiffFlags)
    99  
   100  	// Always compare actual against expected error (even if it is `nil`)
   101  	if !ErrorTypesMatch(actualError, testInfo.ResultExpectedError) {
   102  		switch t := actualError.(type) {
   103  		default:
   104  			fmt.Printf("unhandled error type: `%v`\n", t)
   105  			fmt.Printf(">> value: `%v`\n", t)
   106  			getLogger().Error(actualError)
   107  		}
   108  		t.Errorf("expected error type: `%T`, actual type: `%T`", testInfo.ResultExpectedError, actualError)
   109  	}
   110  
   111  	return
   112  }
   113  
   114  // TODO: support testing if "deltas" expressed in JSON diff records
   115  // match expected output records.
   116  // func debugDeltas(deltas []diff.Delta, indent string) (err error) {
   117  // 	for _, delta := range deltas {
   118  // 		//fmt.Printf("delta: %v\n", delta)
   119  // 		//sim := delta.Similarity()
   120  // 		//fmt.Printf("sim: %v\n", sim)
   121  //
   122  // 		indent2 := indent + "...."
   123  //
   124  // 		switch pointer := delta.(type) {
   125  // 		case *diff.Object:
   126  // 			fmt.Printf("%s[Object](%v): PostPosition(): \"%v\", # Deltas: %v\n", indent, pointer.Similarity(), ColorizeBackgroundCyan(pointer.PostPosition().String()), len(pointer.Deltas))
   127  // 			debugDeltas(pointer.Deltas, indent2)
   128  // 			//deltaJson[d.Position.String()], err = f.formatObject(d.Deltas)
   129  // 		case *diff.Array:
   130  // 			fmt.Printf("%s[Array](%v): PostPosition(): \"%v\", # Deltas: %v\n", indent, pointer.Similarity(), ColorizeBackgroundCyan((pointer.PostPosition()).String()), len(pointer.Deltas))
   131  // 			debugDeltas(pointer.Deltas, indent2)
   132  // 			//deltaJson[d.Position.String()], err = f.formatArray(d.Deltas)
   133  // 		case *diff.Added:
   134  // 			sValue := fmt.Sprintf("%v", pointer.Value)
   135  // 			fmt.Printf("%s[Added](%v): Value: \"%v\", PostPosition(): \"%v\"\n", indent, pointer.Similarity(), ColorizeBackgroundGreen(sValue), ColorizeBackgroundCyan((pointer.PostPosition()).String()))
   136  // 			//deltaJson[d.PostPosition().String()] = []interface{}{d.Value}
   137  // 		case *diff.Modified:
   138  // 			fmt.Printf("%s[Modified](%v): PostPosition: \"%v\", OldValue: \"%v\", NewValue: \"%v\"\n", indent, pointer.Similarity(), ColorizeBackgroundCyan((pointer.PostPosition()).String()), ColorizeBackgroundRed((pointer.OldValue).(string)), ColorizeBackgroundGreen((pointer.NewValue).(string)))
   139  // 			//deltaJson[d.PostPosition().String()] = []interface{}{d.OldValue, d.NewValue}
   140  // 		case *diff.TextDiff:
   141  // 			fmt.Printf("%s[TextDiff](%v): PostPosition: \"%v\", OldValue: \"%v\", NewValue: \"%v\"\n", indent, pointer.Similarity(), ColorizeBackgroundCyan((pointer.PostPosition()).String()), ColorizeBackgroundRed((pointer.OldValue).(string)), ColorizeBackgroundGreen((pointer.NewValue).(string)))
   142  // 			//deltaJson[d.PostPosition().String()] = []interface{}{d.DiffString(), 0, DeltaTextDiff}
   143  // 		case *diff.Deleted:
   144  // 			sValue := fmt.Sprintf("%v", pointer.Value)
   145  // 			fmt.Printf("%s[Deleted](%v): Value: \"%v\", PrePosition(): \"%v\"\n", indent, pointer.Similarity(), ColorizeBackgroundRed(sValue), ColorizeBackgroundCyan(pointer.PrePosition().String()))
   146  // 			//deltaJson[d.PrePosition().String()] = []interface{}{d.Value, 0, DeltaDelete}
   147  // 		case *diff.Moved:
   148  // 			sValue := fmt.Sprintf("%v", pointer.Value)
   149  // 			fmt.Printf("%s[Moved](%v): Value: \"%v\", PrePosition(): \"%v\", PostPosition(): \"%v\"\n", indent, pointer.Similarity(), ColorizeBackgroundYellow(sValue), ColorizeBackgroundCyan(pointer.PrePosition().String()), ColorizeBackgroundCyan(pointer.PostPosition().String()))
   150  // 			fmt.Printf("%s[ERROR] 'Move' operation NOT supported for formatting objects\n", indent)
   151  // 		default:
   152  // 			fmt.Printf("%sUnknown Delta type detected: \"%T\"\n", indent, delta)
   153  // 		}
   154  // 	}
   155  //
   156  // 	return
   157  // }
   158  
   159  // See: https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
   160  
   161  // Validate value (range)
   162  // func Colorize(color string, text string) (colorizedText string) {
   163  // 	return color + text + Reset
   164  // }
   165  
   166  func TestDiffJsonArrayOrderMove2ObjectsFormatJson(t *testing.T) {
   167  	ti := NewDiffTestInfo(TEST_DIFF_ARRAY_ORDER_2_CHANGES_BASE, TEST_DIFF_ARRAY_ORDER_2_CHANGES_DELTA)
   168  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_ARRAY_ORDER_2_CHANGES_DELTA)
   169  	err := innerDiffTest(t, ti)
   170  	if err != nil {
   171  		t.Error(err)
   172  	}
   173  }
   174  
   175  func TestDiffJsonArrayOrderMove1ObjectFormatJson(t *testing.T) {
   176  	ti := NewDiffTestInfo(TEST_DIFF_ARRAY_ORDER_CHANGE_BASE, TEST_DIFF_ARRAY_ORDER_CHANGE_DELTA)
   177  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_ARRAY_ORDER_CHANGE_DELTA)
   178  	err := innerDiffTest(t, ti)
   179  	if err != nil {
   180  		t.Error(err)
   181  	}
   182  }
   183  
   184  func TestDiffJsonArrayOrderMove1ObjectFormatText(t *testing.T) {
   185  	ti := NewDiffTestInfo(TEST_DIFF_ARRAY_ORDER_CHANGE_BASE, TEST_DIFF_ARRAY_ORDER_CHANGE_DELTA)
   186  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_ARRAY_ORDER_CHANGE_DELTA)
   187  	err := innerDiffTest(t, ti)
   188  	if err != nil {
   189  		t.Error(err)
   190  	}
   191  }
   192  
   193  func TestDiffJsonArrayOrderMove1ObjectWithDeleteFormatText(t *testing.T) {
   194  	ti := NewDiffTestInfo(TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_DELETE_BASE, TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_DELETE_DELTA)
   195  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_DELETE_DELTA)
   196  	err := innerDiffTest(t, ti)
   197  	if err != nil {
   198  		t.Error(err)
   199  	}
   200  }
   201  
   202  func TestDiffJsonArrayOrderMove1ObjectWithAddFormatText(t *testing.T) {
   203  	ti := NewDiffTestInfo(TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_BASE, TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_DELTA)
   204  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_DELTA)
   205  	err := innerDiffTest(t, ti)
   206  	if err != nil {
   207  		t.Error(err)
   208  	}
   209  }
   210  
   211  func TestDiffJsonArrayOrderMove1ObjectWithAddAndDeleteFormatText(t *testing.T) {
   212  	ti := NewDiffTestInfo(TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_AND_DELETE_BASE, TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_AND_DELETE_DELTA)
   213  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_ARRAY_ORDER_CHANGE_WITH_ADD_AND_DELETE_DELTA)
   214  	err := innerDiffTest(t, ti)
   215  	if err != nil {
   216  		t.Error(err)
   217  	}
   218  }
   219  
   220  // =====================================================
   221  // CycloneDX BOM variant tests
   222  // =====================================================
   223  
   224  func TestDiffCdx14MatureDelta1Text(t *testing.T) {
   225  	ti := NewDiffTestInfo(TEST_CDX_1_4_MATURE_EXAMPLE_1_BASE, TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_1_DELTA)
   226  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_1_DELTA)
   227  	err := innerDiffTest(t, ti)
   228  	if err != nil {
   229  		t.Error(err)
   230  	}
   231  }
   232  
   233  func TestDiffCdx14MatureDelta1Json(t *testing.T) {
   234  	ti := NewDiffTestInfo(TEST_CDX_1_4_MATURE_EXAMPLE_1_BASE, TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_1_DELTA)
   235  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_1_DELTA)
   236  	ti.OutputFormat = FORMAT_JSON
   237  	err := innerDiffTest(t, ti)
   238  	if err != nil {
   239  		t.Error(err)
   240  	}
   241  }
   242  
   243  func TestDiffCdx14MatureDelta2(t *testing.T) {
   244  	ti := NewDiffTestInfo(TEST_CDX_1_4_MATURE_EXAMPLE_1_BASE, TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_2_DELTA)
   245  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_CDX_1_4_MATURITY_EXAMPLE_2_DELTA)
   246  	err := innerDiffTest(t, ti)
   247  	if err != nil {
   248  		t.Error(err)
   249  	}
   250  }
   251  
   252  func TestDiffJsonVulnerabilitiesAdd1(t *testing.T) {
   253  	ti := NewDiffTestInfo(TEST_DIFF_CDX_1_5_VULNERABILITY_BASE, TEST_DIFF_CDX_1_5_VULNERABILITY_ADD_1)
   254  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_CDX_1_5_VULNERABILITY_ADD_1)
   255  	err := innerDiffTest(t, ti)
   256  	if err != nil {
   257  		t.Error(err)
   258  	}
   259  }
   260  
   261  func TestDiffJsonVulnerabilitiesRemove1(t *testing.T) {
   262  	ti := NewDiffTestInfo(TEST_DIFF_CDX_1_5_VULNERABILITY_BASE, TEST_DIFF_CDX_1_5_VULNERABILITY_REMOVE_1)
   263  	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_CDX_1_5_VULNERABILITY_REMOVE_1)
   264  	err := innerDiffTest(t, ti)
   265  	if err != nil {
   266  		t.Error(err)
   267  	}
   268  }
   269  
   270  // NOTE: In order to debug panic handling... here is a test
   271  // Unfortunately, we cannot run it as part of function test as it "times out"
   272  // TODO: Create smaller test files that cause panic in Diff command's underlying libs.
   273  // func TestDiffJsonPanicNATs(t *testing.T) {
   274  // 	ti := NewDiffTestInfo(TEST_DIFF_PANIC_BASE, TEST_DIFF_PANIC_DELTA)
   275  // 	ti.OutputFile = ti.CreateTemporaryTestOutputFilename(TEST_DIFF_PANIC_DELTA)
   276  // 	err := innerDiffTest(t, ti)
   277  // 	if err != nil {
   278  // 		t.Error(err)
   279  // 	}
   280  // }