github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/test/integration/encode_decode_cycle_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"bytes"
     5  	"regexp"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/nextlinux/gosbom/gosbom/formats"
    10  	"github.com/nextlinux/gosbom/gosbom/formats/cyclonedxjson"
    11  	"github.com/nextlinux/gosbom/gosbom/formats/cyclonedxxml"
    12  	"github.com/nextlinux/gosbom/gosbom/formats/gosbomjson"
    13  	"github.com/nextlinux/gosbom/gosbom/sbom"
    14  	"github.com/nextlinux/gosbom/gosbom/source"
    15  	"github.com/sergi/go-diff/diffmatchpatch"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  // TestEncodeDecodeEncodeCycleComparison is testing for differences in how SBOM documents get encoded on multiple cycles.
    21  // By encoding and decoding the sbom we can compare the differences between the set of resulting objects. However,
    22  // this requires specific comparisons being done, and select redactions/omissions being made. Additionally, there are
    23  // already unit tests on each format encoder-decoder for properly functioning comparisons in depth, so there is no need
    24  // to do an object-to-object comparison. For this reason this test focuses on a bytes-to-bytes comparison after an
    25  // encode-decode-encode loop which will detect lossy behavior in both directions.
    26  func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
    27  	// use second image for relationships
    28  	images := []string{"image-pkg-coverage", "image-owning-package"}
    29  	tests := []struct {
    30  		formatOption sbom.FormatID
    31  		redactor     func(in []byte) []byte
    32  		json         bool
    33  	}{
    34  		{
    35  			formatOption: gosbomjson.ID,
    36  			redactor: func(in []byte) []byte {
    37  				// no redactions necessary
    38  				return in
    39  			},
    40  			json: true,
    41  		},
    42  		{
    43  			formatOption: cyclonedxjson.ID,
    44  			redactor: func(in []byte) []byte {
    45  				// unstable values
    46  				in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref)": "[^"]+",`).ReplaceAll(in, []byte{})
    47  
    48  				return in
    49  			},
    50  			json: true,
    51  		},
    52  		{
    53  			formatOption: cyclonedxxml.ID,
    54  			redactor: func(in []byte) []byte {
    55  				// unstable values
    56  				in = regexp.MustCompile(`(serialNumber|bom-ref)="[^"]+"`).ReplaceAll(in, []byte{})
    57  				in = regexp.MustCompile(`<timestamp>[^<]+</timestamp>`).ReplaceAll(in, []byte{})
    58  
    59  				return in
    60  			},
    61  		},
    62  	}
    63  
    64  	for _, test := range tests {
    65  		t.Run(string(test.formatOption), func(t *testing.T) {
    66  			for _, image := range images {
    67  				originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope, nil)
    68  
    69  				format := formats.ByName(string(test.formatOption))
    70  				require.NotNil(t, format)
    71  
    72  				by1, err := formats.Encode(originalSBOM, format)
    73  				require.NoError(t, err)
    74  
    75  				newSBOM, newFormat, err := formats.Decode(bytes.NewReader(by1))
    76  				require.NoError(t, err)
    77  				require.Equal(t, format.ID(), newFormat.ID())
    78  
    79  				by2, err := formats.Encode(*newSBOM, format)
    80  				require.NoError(t, err)
    81  
    82  				if test.redactor != nil {
    83  					by1 = test.redactor(by1)
    84  					by2 = test.redactor(by2)
    85  				}
    86  
    87  				if test.json {
    88  					s1 := string(by1)
    89  					s2 := string(by2)
    90  					if diff := cmp.Diff(s1, s2); diff != "" {
    91  						t.Errorf("Encode/Decode mismatch (-want +got) [image %q]:\n%s", image, diff)
    92  					}
    93  				} else if !assert.True(t, bytes.Equal(by1, by2)) {
    94  					dmp := diffmatchpatch.New()
    95  					diffs := dmp.DiffMain(string(by1), string(by2), true)
    96  					t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
    97  				}
    98  			}
    99  		})
   100  	}
   101  }