github.com/CycloneDX/sbom-utility@v0.16.0/cmd/component_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  	"bufio"
    23  	"bytes"
    24  	"io/fs"
    25  	"log"
    26  	"os"
    27  	"testing"
    28  
    29  	"github.com/CycloneDX/sbom-utility/common"
    30  	"github.com/CycloneDX/sbom-utility/utils"
    31  )
    32  
    33  // Test "resource list" command
    34  const (
    35  	// test/cyclonedx/cdx-1-3-resource-list.json
    36  	TEST_COMPONENT_LIST_CDX_1_3 = TEST_RESOURCE_LIST_CDX_1_3
    37  	// test/cyclonedx/cdx-1-5-mature-example-1.json
    38  	TEST_COMPONENT_LIST_CDX_1_5_MATURE = TEST_CDX_1_5_MATURE_EXAMPLE_1_BASE
    39  	// test/cyclonedx/1.6/cdx-1-6-valid-cbom-full-1.6.json
    40  	TEST_COMPONENT_LIST_CDX_1_6_CBOM = TEST_CDX_1_6_CRYPTO_BOM
    41  	// test/cyclonedx/1.6/cdx-1-6-valid-mlbom-environmental-considerations.json
    42  	TEST_COMPONENT_LIST_CDX_1_6_MLBOM = TEST_CDX_1_6_MACHINE_LEARNING_BOM
    43  )
    44  
    45  var COMPONENT_TEST_DEFAULT_FLAGS utils.ComponentCommandFlags
    46  
    47  type ComponentTestInfo struct {
    48  	CommonTestInfo
    49  }
    50  
    51  func (ti *ComponentTestInfo) String() string {
    52  	buffer, _ := utils.EncodeAnyToDefaultIndentedJSONStr(ti)
    53  	return buffer.String()
    54  }
    55  
    56  func NewComponentTestInfo(inputFile string, outputFormat string, listSummary bool, whereClause string,
    57  	resultExpectedLineCount int) *ComponentTestInfo {
    58  
    59  	var ti = new(ComponentTestInfo)
    60  	var pCommon = &ti.CommonTestInfo
    61  	// initialize common fields
    62  	pCommon.Init(inputFile, outputFormat, listSummary, whereClause,
    63  		nil, resultExpectedLineCount, nil)
    64  	return ti
    65  }
    66  
    67  func NewComponentTestInfoBasic(inputFile string, listFormat string, resultExpectedError error) *ComponentTestInfo {
    68  	var ti = new(ComponentTestInfo)
    69  	var pCommon = &ti.CommonTestInfo
    70  	pCommon.InitBasic(inputFile, listFormat, resultExpectedError)
    71  	return ti
    72  }
    73  
    74  // -------------------------------------------
    75  // resource list test helper functions
    76  // -------------------------------------------
    77  func innerBufferedTestComponentList(testInfo *ComponentTestInfo, whereFilters []common.WhereFilter, flags utils.ComponentCommandFlags) (outputBuffer bytes.Buffer, err error) {
    78  	// Declare an output outputBuffer/outputWriter to use used during tests
    79  	var outputWriter = bufio.NewWriter(&outputBuffer)
    80  	// ensure all data is written to buffer before further validation
    81  	defer outputWriter.Flush()
    82  
    83  	var persistentFlags utils.PersistentCommandFlags
    84  	persistentFlags.OutputFormat = testInfo.OutputFormat
    85  
    86  	err = ListComponents(outputWriter, persistentFlags, flags, whereFilters)
    87  	return
    88  }
    89  
    90  func innerTestComponentList(t *testing.T, testInfo *ComponentTestInfo, flags utils.ComponentCommandFlags) (outputBuffer bytes.Buffer, basicTestInfo string, err error) {
    91  	getLogger().Tracef("TestInfo: %s", testInfo)
    92  
    93  	// Parse out --where filters and exit out if error detected
    94  	whereFilters, err := prepareWhereFilters(t, &testInfo.CommonTestInfo)
    95  	if err != nil {
    96  		return
    97  	}
    98  
    99  	// The command looks for the input filename in global flags struct
   100  	utils.GlobalFlags.PersistentFlags.InputFile = testInfo.InputFile
   101  
   102  	// Mock stdin if requested
   103  	if testInfo.MockStdin == true {
   104  		utils.GlobalFlags.PersistentFlags.InputFile = INPUT_TYPE_STDIN
   105  		file, err := os.Open(testInfo.InputFile) // For read access.
   106  		if err != nil {
   107  			log.Fatal(err)
   108  		}
   109  
   110  		// convert byte slice to io.Reader
   111  		savedStdIn := os.Stdin
   112  		// !!!Important restore stdin
   113  		defer func() { os.Stdin = savedStdIn }()
   114  		os.Stdin = file
   115  	}
   116  
   117  	// invoke resource list command with a byte buffer
   118  	outputBuffer, err = innerBufferedTestComponentList(testInfo, whereFilters, flags)
   119  
   120  	// Run all common tests against "result" values in the CommonTestInfo struct
   121  	err = innerRunReportResultTests(t, &testInfo.CommonTestInfo, outputBuffer, err)
   122  
   123  	return
   124  }
   125  
   126  // -------------------------------------------
   127  // Test format unsupported (SPDX)
   128  // -------------------------------------------
   129  func TestComponentListFormatUnsupportedSPDXMinReq(t *testing.T) {
   130  	ti := NewComponentTestInfoBasic(
   131  		TEST_INPUT_FILE_NON_EXISTENT,
   132  		FORMAT_DEFAULT,
   133  		&fs.PathError{},
   134  	)
   135  	// verify correct error is returned
   136  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   137  }
   138  
   139  // -------------------------------------------
   140  // Format variants
   141  // -------------------------------------------
   142  
   143  func TestComponentListCdx13Text(t *testing.T) {
   144  	ti := NewComponentTestInfoBasic(TEST_RESOURCE_LIST_CDX_1_3, FORMAT_TEXT, nil)
   145  	ti.ResultExpectedLineCount = 14 // title + separator + 11 data + EOF LF
   146  	ti.ResultLineContainsValuesAtLineNum = 6
   147  	ti.ResultLineContainsValues = []string{"Library G"}
   148  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   149  }
   150  
   151  func TestComponentListCdx13Csv(t *testing.T) {
   152  	ti := NewComponentTestInfoBasic(TEST_RESOURCE_LIST_CDX_1_3, FORMAT_CSV, nil)
   153  	ti.ResultExpectedLineCount = 13 // title + 11 data + EOF LF
   154  	ti.ResultLineContainsValuesAtLineNum = 5
   155  	ti.ResultLineContainsValues = []string{"Library G"}
   156  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   157  }
   158  
   159  func TestComponentListCdx13Markdown(t *testing.T) {
   160  	ti := NewComponentTestInfoBasic(TEST_RESOURCE_LIST_CDX_1_3, FORMAT_MARKDOWN, nil)
   161  	ti.ResultExpectedLineCount = 14 // title + separator + 11 data + EOF LF
   162  	ti.ResultLineContainsValuesAtLineNum = 6
   163  	ti.ResultLineContainsValues = []string{"Library G"}
   164  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   165  }
   166  
   167  // -------------------------------------------
   168  // CDX variants
   169  // -------------------------------------------
   170  
   171  func TestComponentListCdx15MatureCsv(t *testing.T) {
   172  	ti := NewComponentTestInfoBasic(TEST_COMPONENT_LIST_CDX_1_5_MATURE, FORMAT_CSV, nil)
   173  	//ti.ListSummary = false
   174  	//ti.WhereClause = "version=2.0"
   175  	ti.ResultExpectedLineCount = 5 // title + 3 data + EOF LF
   176  	ti.ResultLineContainsValuesAtLineNum = 3
   177  	ti.ResultLineContainsValues = []string{"sample"}
   178  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   179  }
   180  
   181  func TestComponentListCdx16CryptoBOMCsv(t *testing.T) {
   182  	ti := NewComponentTestInfoBasic(TEST_COMPONENT_LIST_CDX_1_6_CBOM, FORMAT_CSV, nil)
   183  	ti.ResultExpectedLineCount = 6 // title + 4 data + EOF LF
   184  	ti.ResultLineContainsValuesAtLineNum = 2
   185  	ti.ResultLineContainsValues = []string{"asset-2"}
   186  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   187  }
   188  
   189  func TestComponentListCdx16MachineLearningBOMCsv(t *testing.T) {
   190  	ti := NewComponentTestInfoBasic(TEST_COMPONENT_LIST_CDX_1_6_MLBOM, FORMAT_CSV, nil)
   191  	ti.ResultExpectedLineCount = 3 // title + 1 data + EOF LF
   192  	ti.ResultLineContainsValuesAtLineNum = 1
   193  	ti.ResultLineContainsValues = []string{"Llama-2-7b"}
   194  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   195  }
   196  
   197  // ./sbom-utility component list -i test/cyclonedx/cdx-1-3-resource-list.json --where "number-licenses=0"  --quiet --format=txt
   198  //
   199  //	library  Library NoLicense  1.0.0  Library "NoLicense" description.  pkg:lib/libraryNoLicense@1.0.0  0   0
   200  func TestComponentListCdx13WhereNumLicensesCsv(t *testing.T) {
   201  	ti := NewComponentTestInfoBasic(TEST_RESOURCE_LIST_CDX_1_3, FORMAT_CSV, nil)
   202  	ti.WhereClause = "number-licenses=0"
   203  	ti.ResultExpectedLineCount = 3 // title + 1 data + EOF LF
   204  	ti.ResultLineContainsValuesAtLineNum = 1
   205  	ti.ResultLineContainsValues = []string{"NoLicense"}
   206  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   207  }
   208  
   209  func TestComponentListCdx16ValidBom(t *testing.T) {
   210  	ti := NewComponentTestInfoBasic(TEST_CDX_SPEC_1_6_VALID_BOM, FORMAT_CSV, nil)
   211  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   212  }
   213  
   214  func TestComponentListCdx16ValidComponentIds(t *testing.T) {
   215  	ti := NewComponentTestInfoBasic(TEST_CDX_SPEC_1_6_VALID_COMPONENT_IDS, FORMAT_CSV, nil)
   216  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   217  }
   218  
   219  func TestComponentListCdx16ValidComponentSwid(t *testing.T) {
   220  	ti := NewComponentTestInfoBasic(TEST_CDX_SPEC_1_6_VALID_SWID, FORMAT_CSV, nil)
   221  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   222  }
   223  
   224  func TestComponentListCdx16ValidComponentTypes(t *testing.T) {
   225  	ti := NewComponentTestInfoBasic(TEST_CDX_SPEC_1_6_VALID_COMPONENT_TYPES, FORMAT_CSV, nil)
   226  	innerTestComponentList(t, ti, COMPONENT_TEST_DEFAULT_FLAGS)
   227  }