github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/cache/s3_test.go (about) 1 package cache 2 3 import ( 4 "context" 5 "errors" 6 "reflect" 7 "testing" 8 "time" 9 10 "github.com/aws/aws-sdk-go-v2/feature/s3/manager" 11 "github.com/aws/aws-sdk-go-v2/service/s3" 12 "golang.org/x/xerrors" 13 14 "github.com/devseccon/trivy/pkg/fanal/types" 15 ) 16 17 type mockS3Client struct { 18 s3API 19 } 20 21 const ( 22 correctHash = "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7" 23 ) 24 25 func (m *mockS3Client) PutObject(ctx context.Context, in *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) { 26 return &s3.PutObjectOutput{}, nil 27 } 28 29 func (m *mockS3Client) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) { 30 return &s3.HeadObjectOutput{}, nil 31 } 32 33 func (m *mockS3Client) DeleteBucket(ctx context.Context, in *s3.DeleteBucketInput, optFns ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) { 34 if in != nil && *in.Bucket == blobBucket+"/prefix/"+correctHash { 35 return &s3.DeleteBucketOutput{}, nil 36 } 37 return nil, errors.New("unknown bucket") 38 } 39 40 func TestS3Cache_PutBlob(t *testing.T) { 41 mockSvc := &mockS3Client{} 42 43 type fields struct { 44 S3 s3API 45 Downloader *manager.Downloader 46 BucketName string 47 Prefix string 48 } 49 type args struct { 50 blobID string 51 blobInfo types.BlobInfo 52 } 53 tests := []struct { 54 name string 55 fields fields 56 args args 57 wantErr bool 58 }{ 59 { 60 name: "happy path", 61 fields: fields{ 62 S3: mockSvc, 63 BucketName: "test", 64 Prefix: "prefix", 65 }, 66 args: args{ 67 blobID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", 68 blobInfo: types.BlobInfo{ 69 SchemaVersion: 1, 70 OS: types.OS{ 71 Family: "alpine", 72 Name: "3.10", 73 }, 74 }}, 75 }, 76 } 77 for _, tt := range tests { 78 t.Run(tt.name, func(t *testing.T) { 79 c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader) 80 if err := c.PutBlob(tt.args.blobID, tt.args.blobInfo); (err != nil) != tt.wantErr { 81 t.Errorf("S3Cache.PutBlob() error = %v, wantErr %v", err, tt.wantErr) 82 } 83 }) 84 } 85 } 86 87 func TestS3Cache_PutArtifact(t *testing.T) { 88 mockSvc := &mockS3Client{} 89 90 type fields struct { 91 S3 s3API 92 Downloader *manager.Downloader 93 BucketName string 94 Prefix string 95 } 96 type args struct { 97 artifactID string 98 artifactConfig types.ArtifactInfo 99 } 100 tests := []struct { 101 name string 102 fields fields 103 args args 104 wantErr bool 105 }{ 106 { 107 name: "happy path", 108 fields: fields{ 109 S3: mockSvc, 110 BucketName: "test", 111 Prefix: "prefix", 112 }, 113 args: args{ 114 artifactID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4", 115 artifactConfig: types.ArtifactInfo{ 116 SchemaVersion: 1, 117 Architecture: "amd64", 118 Created: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC), 119 DockerVersion: "18.06.1-ce", 120 OS: "linux", 121 HistoryPackages: []types.Package{ 122 { 123 Name: "musl", 124 Version: "1.2.3", 125 }, 126 }, 127 }}, 128 }, 129 } 130 for _, tt := range tests { 131 t.Run(tt.name, func(t *testing.T) { 132 c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader) 133 if err := c.PutArtifact(tt.args.artifactID, tt.args.artifactConfig); (err != nil) != tt.wantErr { 134 t.Errorf("S3Cache.PutArtifact() error = %v, wantErr %v", err, tt.wantErr) 135 } 136 }) 137 } 138 } 139 140 func TestS3Cache_getIndex(t *testing.T) { 141 mockSvc := &mockS3Client{} 142 143 type fields struct { 144 S3 s3API 145 Downloader *manager.Downloader 146 BucketName string 147 Prefix string 148 } 149 type args struct { 150 key string 151 keyType string 152 } 153 tests := []struct { 154 name string 155 fields fields 156 args args 157 wantErr bool 158 }{ 159 { 160 name: "happy path", 161 fields: fields{ 162 S3: mockSvc, 163 BucketName: "test", 164 Prefix: "prefix", 165 }, 166 args: args{ 167 key: "key", 168 keyType: "artifactBucket", 169 }, 170 wantErr: false, 171 }, 172 } 173 for _, tt := range tests { 174 t.Run(tt.name, func(t *testing.T) { 175 c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader) 176 if err := c.getIndex(tt.args.key, tt.args.keyType); (err != nil) != tt.wantErr { 177 t.Errorf("S3Cache.getIndex() error = %v, wantErr %v", err, tt.wantErr) 178 } 179 }) 180 } 181 } 182 183 type mockS3ClientMissingBlobs struct { 184 s3API 185 } 186 187 func (m *mockS3ClientMissingBlobs) PutObject(ctx context.Context, in *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) { 188 return &s3.PutObjectOutput{}, nil 189 } 190 191 func (m *mockS3ClientMissingBlobs) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) { 192 return &s3.HeadObjectOutput{}, xerrors.Errorf("the object doesn't exist in S3") 193 } 194 195 func TestS3Cache_MissingBlobs(t *testing.T) { 196 mockSvc := &mockS3ClientMissingBlobs{} 197 198 type fields struct { 199 S3 s3API 200 Downloader *manager.Downloader 201 BucketName string 202 Prefix string 203 } 204 type args struct { 205 artifactID string 206 blobIDs []string 207 analyzerVersions map[string]int 208 configAnalyzerVersions map[string]int 209 } 210 tests := []struct { 211 name string 212 fields fields 213 args args 214 want bool 215 wantStringSlice []string 216 wantErr bool 217 }{{ 218 name: "happy path", 219 fields: fields{ 220 S3: mockSvc, 221 BucketName: "test", 222 Prefix: "prefix", 223 }, 224 args: args{ 225 artifactID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4/1", 226 blobIDs: []string{"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7/10011"}, 227 }, 228 want: true, 229 wantStringSlice: []string{"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7/10011"}, 230 wantErr: false, 231 }, 232 } 233 for _, tt := range tests { 234 t.Run(tt.name, func(t *testing.T) { 235 c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader) 236 got, got1, err := c.MissingBlobs(tt.args.artifactID, tt.args.blobIDs) 237 if (err != nil) != tt.wantErr { 238 t.Errorf("S3Cache.MissingBlobs() error = %v, wantErr %v", err, tt.wantErr) 239 return 240 } 241 if got != tt.want { 242 t.Errorf("S3Cache.MissingBlobs() got = %v, want %v", got, tt.want) 243 } 244 if !reflect.DeepEqual(got1, tt.wantStringSlice) { 245 t.Errorf("S3Cache.MissingBlobs() got1 = %v, want %v", got1, tt.wantStringSlice) 246 } 247 }) 248 } 249 } 250 251 func TestS3Cache_DeleteBlobs(t *testing.T) { 252 mockSvc := &mockS3Client{} 253 254 type fields struct { 255 S3 s3API 256 Downloader *manager.Downloader 257 BucketName string 258 Prefix string 259 } 260 type args struct { 261 blobIDs []string 262 } 263 tests := []struct { 264 name string 265 fields fields 266 args args 267 wantErr bool 268 }{ 269 { 270 name: "happy path", 271 fields: fields{ 272 S3: mockSvc, 273 BucketName: "test", 274 Prefix: "prefix", 275 }, 276 args: args{ 277 blobIDs: []string{correctHash}, 278 }, 279 }, 280 { 281 name: "delete blob with bad ID", 282 fields: fields{ 283 S3: mockSvc, 284 BucketName: "test", 285 Prefix: "prefix", 286 }, 287 args: args{ 288 blobIDs: []string{"unde"}, 289 }, 290 wantErr: true, 291 }, 292 { 293 name: "delete blobs with bad ID", 294 fields: fields{ 295 S3: mockSvc, 296 BucketName: "test", 297 Prefix: "prefix", 298 }, 299 args: args{ 300 blobIDs: []string{correctHash}, 301 }, 302 }, 303 } 304 for _, tt := range tests { 305 t.Run(tt.name, func(t *testing.T) { 306 c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader) 307 if err := c.DeleteBlobs(tt.args.blobIDs); (err != nil) != tt.wantErr { 308 t.Errorf("S3Cache.PutBlob() error = %v, wantErr %v", err, tt.wantErr) 309 } 310 }) 311 } 312 }