github.com/anchore/syft@v1.38.2/syft/format/syftjson/model/file_test.go (about) 1 package model 2 3 import ( 4 "testing" 5 6 "github.com/google/go-cmp/cmp" 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 ) 10 11 func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) { 12 tests := []struct { 13 name string 14 jsonData []byte 15 expected FileMetadataEntry 16 }{ 17 { 18 name: "unmarshal current format", 19 jsonData: []byte(`{ 20 "mode": 644, 21 "type": "RegularFile", 22 "linkDestination": "/usr/bin/python3", 23 "userID": 1000, 24 "groupID": 1000, 25 "mimeType": "text/plain", 26 "size": 10174 27 }`), 28 expected: FileMetadataEntry{ 29 Mode: 644, 30 Type: "RegularFile", 31 LinkDestination: "/usr/bin/python3", 32 UserID: 1000, 33 GroupID: 1000, 34 MIMEType: "text/plain", 35 Size: 10174, 36 }, 37 }, 38 { 39 name: "unmarshal legacy image add internal document format", 40 jsonData: []byte(`{ 41 "FileInfo": {}, 42 "Mode": 644, 43 "Type": "RegularFile", 44 "LinkDestination": "/usr/bin/python3", 45 "UserID": 1000, 46 "GroupID": 1000, 47 "MIMEType": "text/plain", 48 "Size": 10174 49 }`), 50 expected: FileMetadataEntry{ 51 Mode: 420, // important! we convert this to base 10 so that all documents are consistent 52 Type: "RegularFile", 53 LinkDestination: "/usr/bin/python3", 54 UserID: 1000, 55 GroupID: 1000, 56 MIMEType: "text/plain", 57 Size: 10174, 58 }, 59 }, 60 { 61 name: "unmarshal legacy sbom import format", 62 jsonData: []byte(`{ 63 "FileInfo": {}, 64 "Mode": 644, 65 "Type": 0, 66 "LinkDestination": "/usr/bin/python3", 67 "UserID": 1000, 68 "GroupID": 1000, 69 "MIMEType": "text/plain", 70 "Size": 10174 71 }`), 72 expected: FileMetadataEntry{ 73 Mode: 644, 74 Type: "RegularFile", 75 LinkDestination: "/usr/bin/python3", 76 UserID: 1000, 77 GroupID: 1000, 78 MIMEType: "text/plain", 79 Size: 10174, 80 }, 81 }, 82 { 83 name: "unmarshal minimal current format", 84 jsonData: []byte(`{ 85 "mode": 0, 86 "type": "RegularFile", 87 "userID": 0, 88 "groupID": 0, 89 "size": 0 90 }`), 91 expected: FileMetadataEntry{ 92 Type: "RegularFile", 93 }, 94 }, 95 { 96 name: "unmarshal minimal legacy format", 97 jsonData: []byte(`{ 98 "FileInfo": {}, 99 "Mode": 0, 100 "Type": "RegularFile", 101 "UserID": 0, 102 "GroupID": 0, 103 "Size": 0 104 }`), 105 expected: FileMetadataEntry{ 106 Type: "RegularFile", 107 }, 108 }, 109 } 110 111 for _, test := range tests { 112 t.Run(test.name, func(t *testing.T) { 113 var actual FileMetadataEntry 114 err := actual.UnmarshalJSON(test.jsonData) 115 require.NoError(t, err) 116 117 if diff := cmp.Diff(test.expected, actual); diff != "" { 118 t.Errorf("FileMetadataEntry mismatch (-expected +actual):\n%s", diff) 119 } 120 }) 121 } 122 } 123 124 func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) { 125 tests := []struct { 126 name string 127 jsonData []byte 128 expected string 129 expectedWasInt bool 130 wantErr require.ErrorAssertionFunc 131 }{ 132 // string inputs - should pass through unchanged 133 { 134 name: "string RegularFile", 135 jsonData: []byte(`"RegularFile"`), 136 expected: "RegularFile", 137 }, 138 { 139 name: "string HardLink", 140 jsonData: []byte(`"HardLink"`), 141 expected: "HardLink", 142 }, 143 { 144 name: "string Directory", 145 jsonData: []byte(`"Directory"`), 146 expected: "Directory", 147 }, 148 { 149 name: "string custom value", 150 jsonData: []byte(`"CustomFileType"`), 151 expected: "CustomFileType", 152 }, 153 // integer inputs - should convert to string representation 154 { 155 name: "int 0 (TypeRegular)", 156 jsonData: []byte(`0`), 157 expected: "RegularFile", 158 expectedWasInt: true, 159 }, 160 { 161 name: "int 1 (TypeHardLink)", 162 jsonData: []byte(`1`), 163 expected: "HardLink", 164 expectedWasInt: true, 165 }, 166 { 167 name: "int 2 (TypeSymLink)", 168 jsonData: []byte(`2`), 169 expected: "SymbolicLink", 170 expectedWasInt: true, 171 }, 172 { 173 name: "int 3 (TypeCharacterDevice)", 174 jsonData: []byte(`3`), 175 expected: "CharacterDevice", 176 expectedWasInt: true, 177 }, 178 { 179 name: "int 4 (TypeBlockDevice)", 180 jsonData: []byte(`4`), 181 expected: "BlockDevice", 182 expectedWasInt: true, 183 }, 184 { 185 name: "int 5 (TypeDirectory)", 186 jsonData: []byte(`5`), 187 expected: "Directory", 188 expectedWasInt: true, 189 }, 190 { 191 name: "int 6 (TypeFIFO)", 192 jsonData: []byte(`6`), 193 expected: "FIFONode", 194 expectedWasInt: true, 195 }, 196 { 197 name: "int 7 (TypeSocket)", 198 jsonData: []byte(`7`), 199 expected: "Socket", 200 expectedWasInt: true, 201 }, 202 { 203 name: "int 8 (TypeIrregular)", 204 jsonData: []byte(`8`), 205 expected: "IrregularFile", 206 expectedWasInt: true, 207 }, 208 { 209 name: "unknown int", 210 jsonData: []byte(`99`), 211 expected: "Unknown", 212 expectedWasInt: true, 213 }, 214 { 215 name: "negative int", 216 jsonData: []byte(`-1`), 217 expected: "Unknown", 218 expectedWasInt: true, 219 }, 220 { 221 name: "null value", 222 jsonData: []byte(`null`), 223 }, 224 { 225 name: "invalid JSON", 226 jsonData: []byte(`{`), 227 wantErr: require.Error, 228 }, 229 { 230 name: "boolean value", 231 jsonData: []byte(`true`), 232 wantErr: require.Error, 233 }, 234 { 235 name: "array value", 236 jsonData: []byte(`[]`), 237 wantErr: require.Error, 238 }, 239 { 240 name: "object value", 241 jsonData: []byte(`{}`), 242 wantErr: require.Error, 243 }, 244 { 245 name: "float value", 246 jsonData: []byte(`1.5`), 247 wantErr: require.Error, 248 }, 249 } 250 251 for _, test := range tests { 252 t.Run(test.name, func(t *testing.T) { 253 if test.wantErr == nil { 254 test.wantErr = require.NoError 255 } 256 var ft intOrStringFileType 257 err := ft.UnmarshalJSON(test.jsonData) 258 test.wantErr(t, err) 259 if err != nil { 260 return 261 } 262 assert.Equal(t, test.expected, ft.Value) 263 assert.Equal(t, test.expectedWasInt, ft.WasInt) 264 }) 265 } 266 } 267 268 func Test_convertBase10ToBase8(t *testing.T) { 269 tests := []struct { 270 name string 271 input int 272 expected int 273 }{ 274 { 275 name: "no permissions", 276 input: 0, 277 expected: 0, 278 }, 279 { 280 name: "symlink + rwxrwxrwx", 281 input: 134218239, 282 expected: 1000000777, 283 }, 284 } 285 286 for _, tt := range tests { 287 t.Run(tt.name, func(t *testing.T) { 288 actual := convertBase10ToBase8(tt.input) 289 290 require.Equal(t, tt.expected, actual) 291 }) 292 } 293 } 294 295 func Test_convertBase8ToBase10(t *testing.T) { 296 tests := []struct { 297 name string 298 input int 299 expected int 300 }{ 301 { 302 name: "no permissions", 303 input: 0, 304 expected: 0, 305 }, 306 { 307 name: "symlink + rwxrwxrwx", 308 input: 1000000777, 309 expected: 134218239, 310 }, 311 } 312 313 for _, tt := range tests { 314 t.Run(tt.name, func(t *testing.T) { 315 actual := convertBase8ToBase10(tt.input) 316 317 require.Equal(t, tt.expected, actual) 318 }) 319 } 320 }