github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/cmd/syft/cli/options/writer_test.go (about) 1 package options 2 3 import ( 4 "io" 5 "path/filepath" 6 "strings" 7 "testing" 8 9 "github.com/docker/docker/pkg/homedir" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/anchore/syft/syft/sbom" 14 ) 15 16 func Test_MakeSBOMWriter(t *testing.T) { 17 tests := []struct { 18 name string 19 outputs []string 20 wantErr assert.ErrorAssertionFunc 21 }{ 22 { 23 name: "go case", 24 outputs: []string{"json"}, 25 wantErr: assert.NoError, 26 }, 27 { 28 name: "multiple", 29 outputs: []string{"table", "json"}, 30 wantErr: assert.NoError, 31 }, 32 { 33 name: "unknown format", 34 outputs: []string{"unknown"}, 35 wantErr: func(t assert.TestingT, err error, bla ...interface{}) bool { 36 return assert.ErrorContains(t, err, `unsupported output format "unknown", supported formats are:`) 37 }, 38 }, 39 } 40 41 for _, tt := range tests { 42 t.Run(tt.name, func(t *testing.T) { 43 opt := DefaultOutput() 44 require.NoError(t, opt.Format.PostLoad()) 45 encoders, err := opt.Encoders() 46 require.NoError(t, err) 47 _, err = makeSBOMWriter(tt.outputs, "", encoders) 48 tt.wantErr(t, err) 49 }) 50 } 51 } 52 53 func dummyFormat(name string) sbom.FormatEncoder { 54 return dummyEncoder{name: name} 55 } 56 57 var _ sbom.FormatEncoder = (*dummyEncoder)(nil) 58 59 type dummyEncoder struct { 60 name string 61 } 62 63 func (d dummyEncoder) ID() sbom.FormatID { 64 return sbom.FormatID(d.name) 65 } 66 67 func (d dummyEncoder) Aliases() []string { 68 return nil 69 } 70 71 func (d dummyEncoder) Version() string { 72 return sbom.AnyVersion 73 } 74 75 func (d dummyEncoder) Encode(writer io.Writer, s sbom.SBOM) error { 76 return nil 77 } 78 79 func Test_newSBOMMultiWriter(t *testing.T) { 80 type writerConfig struct { 81 format string 82 file string 83 } 84 85 tmp := t.TempDir() 86 87 testName := func(options []sbomWriterDescription, err bool) string { 88 var out []string 89 for _, opt := range options { 90 out = append(out, string(opt.Format.ID())+"="+opt.Path) 91 } 92 errs := "" 93 if err { 94 errs = "(err)" 95 } 96 return strings.Join(out, ", ") + errs 97 } 98 99 tests := []struct { 100 outputs []sbomWriterDescription 101 err bool 102 expected []writerConfig 103 }{ 104 { 105 outputs: []sbomWriterDescription{}, 106 err: true, 107 }, 108 { 109 outputs: []sbomWriterDescription{ 110 { 111 Format: dummyFormat("table"), 112 Path: "", 113 }, 114 }, 115 expected: []writerConfig{ 116 { 117 format: "table", 118 }, 119 }, 120 }, 121 { 122 outputs: []sbomWriterDescription{ 123 { 124 Format: dummyFormat("json"), 125 }, 126 }, 127 expected: []writerConfig{ 128 { 129 format: "json", 130 }, 131 }, 132 }, 133 { 134 outputs: []sbomWriterDescription{ 135 { 136 Format: dummyFormat("json"), 137 Path: "test-2.json", 138 }, 139 }, 140 expected: []writerConfig{ 141 { 142 format: "json", 143 file: "test-2.json", 144 }, 145 }, 146 }, 147 { 148 outputs: []sbomWriterDescription{ 149 { 150 Format: dummyFormat("json"), 151 Path: "test-3/1.json", 152 }, 153 { 154 Format: dummyFormat("spdx-json"), 155 Path: "test-3/2.json", 156 }, 157 }, 158 expected: []writerConfig{ 159 { 160 format: "json", 161 file: "test-3/1.json", 162 }, 163 { 164 format: "spdx-json", 165 file: "test-3/2.json", 166 }, 167 }, 168 }, 169 { 170 outputs: []sbomWriterDescription{ 171 { 172 Format: dummyFormat("text"), 173 }, 174 { 175 Format: dummyFormat("spdx-json"), 176 Path: "test-4.json", 177 }, 178 }, 179 expected: []writerConfig{ 180 { 181 format: "text", 182 }, 183 { 184 format: "spdx-json", 185 file: "test-4.json", 186 }, 187 }, 188 }, 189 } 190 191 for _, test := range tests { 192 t.Run(testName(test.outputs, test.err), func(t *testing.T) { 193 outputs := test.outputs 194 for i := range outputs { 195 if outputs[i].Path != "" { 196 outputs[i].Path = tmp + outputs[i].Path 197 } 198 } 199 200 mw, err := newSBOMMultiWriter(outputs...) 201 202 if test.err { 203 assert.Error(t, err) 204 return 205 } else { 206 assert.NoError(t, err) 207 } 208 209 assert.Len(t, mw.writers, len(test.expected)) 210 211 for i, e := range test.expected { 212 switch w := mw.writers[i].(type) { 213 case *sbomStreamWriter: 214 assert.Equal(t, string(w.format.ID()), e.format) 215 assert.NotNil(t, w.out) 216 if e.file != "" { 217 assert.FileExists(t, tmp+e.file) 218 } 219 case *sbomPublisher: 220 assert.Equal(t, string(w.format.ID()), e.format) 221 default: 222 t.Fatalf("unknown writer type: %T", w) 223 } 224 225 } 226 }) 227 } 228 } 229 230 func Test_newSBOMWriterDescription(t *testing.T) { 231 tests := []struct { 232 name string 233 path string 234 expected string 235 }{ 236 { 237 name: "expand home dir", 238 path: "~/place.txt", 239 expected: filepath.Join(homedir.Get(), "place.txt"), 240 }, 241 { 242 name: "passthrough other paths", 243 path: "/other/place.txt", 244 expected: "/other/place.txt", 245 }, 246 { 247 name: "no path", 248 path: "", 249 expected: "", 250 }, 251 } 252 for _, tt := range tests { 253 t.Run(tt.name, func(t *testing.T) { 254 o := newSBOMWriterDescription(dummyFormat("table"), tt.path) 255 assert.Equal(t, tt.expected, o.Path) 256 }) 257 } 258 } 259 260 func Test_formatVersionOptions(t *testing.T) { 261 262 tests := []struct { 263 name string 264 nameVersionPairs []string 265 want string 266 }{ 267 { 268 name: "gocase", 269 nameVersionPairs: []string{ 270 "cyclonedx-json@1.2", "cyclonedx-json@1.3", "cyclonedx-json@1.4", "cyclonedx-json@1.5", 271 "cyclonedx-xml@1.0", "cyclonedx-xml@1.1", "cyclonedx-xml@1.2", "cyclonedx-xml@1.3", 272 "cyclonedx-xml@1.4", "cyclonedx-xml@1.5", "github-json", "spdx-json@2.2", "spdx-json@2.3", 273 "spdx-tag-value@2.1", "spdx-tag-value@2.2", "spdx-tag-value@2.3", "syft-json@11.0.0", 274 "syft-table", "syft-text", "template", 275 }, 276 want: ` 277 Available formats: 278 - cyclonedx-json @ 1.2, 1.3, 1.4, 1.5 279 - cyclonedx-xml @ 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 280 - github-json 281 - spdx-json @ 2.2, 2.3 282 - spdx-tag-value @ 2.1, 2.2, 2.3 283 - syft-json 284 - syft-table 285 - syft-text 286 - template`, 287 }, 288 } 289 for _, tt := range tests { 290 t.Run(tt.name, func(t *testing.T) { 291 assert.Equal(t, tt.want, formatVersionOptions(tt.nameVersionPairs)) 292 }) 293 } 294 }