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 }