github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/containers/dockercomposeimage/dockercomposeimage_test.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package dockercomposeimage_test 16 17 import ( 18 "testing" 19 20 "github.com/google/go-cmp/cmp" 21 "github.com/google/go-cmp/cmp/cmpopts" 22 "github.com/google/osv-scalibr/extractor" 23 "github.com/google/osv-scalibr/extractor/filesystem/containers/dockercomposeimage" 24 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 25 "github.com/google/osv-scalibr/inventory" 26 "github.com/google/osv-scalibr/purl" 27 "github.com/google/osv-scalibr/testing/extracttest" 28 ) 29 30 func TestFileRequired(t *testing.T) { 31 extr := dockercomposeimage.New(dockercomposeimage.DefaultConfig()) 32 33 tests := []struct { 34 name string 35 path string 36 want bool 37 }{ 38 { 39 name: "compose.yaml", 40 path: "testdata/compose.yaml", 41 want: true, 42 }, 43 { 44 name: "docker_compose_file_with_yml_extension", 45 path: "testdata/docker-compose-1.yml", 46 want: true, 47 }, 48 { 49 name: "docker_compose_file_with_yaml_extension", 50 path: "testdata/docker-compose-extending.yaml", 51 want: true, 52 }, 53 { 54 name: "not_a_docker_compose_file", 55 path: "testdata/docker.conf", 56 want: false, 57 }, 58 } 59 60 for _, tc := range tests { 61 t.Run(tc.name, func(t *testing.T) { 62 isRequired := extr.FileRequired(simplefileapi.New(tc.path, nil)) 63 if isRequired != tc.want { 64 t.Fatalf("FileRequired(%s): got %v, want %v", tc.path, isRequired, tc.want) 65 } 66 }) 67 } 68 } 69 70 func TestExtract(t *testing.T) { 71 tests := []struct { 72 name string 73 path string 74 cfg dockercomposeimage.Config 75 wantPackages []*extractor.Package 76 }{ 77 { 78 name: "single_stage_docker_compose_file", 79 path: "testdata/docker-compose-extending.yaml", 80 cfg: dockercomposeimage.DefaultConfig(), 81 wantPackages: []*extractor.Package{ 82 { 83 Name: "ghcr.io/acme/api", 84 Version: "1.2.0", 85 Locations: []string{"testdata/docker-compose-extending.yaml"}, 86 PURLType: "docker", 87 }, 88 }, 89 }, 90 { 91 name: "multi_stage_docker_compose_file", 92 path: "testdata/docker-compose-1.yml", 93 cfg: dockercomposeimage.DefaultConfig(), 94 wantPackages: []*extractor.Package{ 95 // "image3" and "image6" are not expected because 96 // the version values are set(partially) via environment variables, 97 // the responsible environment variable values are not known at extraction time, 98 // and the version values are imperfectly extracted. 99 { 100 Name: "image1", 101 Version: "1.1.1", 102 Locations: []string{"testdata/docker-compose-1.yml"}, 103 PURLType: purl.TypeDocker, 104 }, 105 { 106 Name: "image2", 107 Version: "2.2.2", 108 Locations: []string{"testdata/docker-compose-1.yml"}, 109 PURLType: purl.TypeDocker, 110 }, 111 { 112 Name: "image4", 113 Version: "4.4.4", 114 Locations: []string{"testdata/docker-compose-1.yml"}, 115 PURLType: purl.TypeDocker, 116 }, 117 { 118 Name: "image5", 119 Version: "5.5.5", 120 Locations: []string{"testdata/docker-compose-1.yml"}, 121 PURLType: purl.TypeDocker, 122 }, 123 }, 124 }, 125 } 126 127 for _, tc := range tests { 128 t.Run(tc.name, func(t *testing.T) { 129 extr := dockercomposeimage.New(tc.cfg) 130 131 input := extracttest.GenerateScanInputMock(t, extracttest.ScanInputMockConfig{ 132 Path: tc.path, 133 }) 134 defer extracttest.CloseTestScanInput(t, input) 135 136 got, err := extr.Extract(t.Context(), &input) 137 if err != nil { 138 t.Fatalf("%s.Extract(%q) failed: %v", extr.Name(), tc.path, err) 139 } 140 141 wantInv := inventory.Inventory{Packages: tc.wantPackages} 142 if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" { 143 t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tc.path, diff) 144 } 145 }) 146 } 147 } 148 149 func TestExtract_failures(t *testing.T) { 150 tests := []struct { 151 name string 152 path string 153 }{ 154 { 155 name: "invalid_docker_compose_file", 156 path: "testdata/docker-compose-yaml-parse-error.yaml", 157 }, 158 } 159 160 for _, tc := range tests { 161 t.Run(tc.name, func(t *testing.T) { 162 extr := dockercomposeimage.New(dockercomposeimage.DefaultConfig()) 163 164 input := extracttest.GenerateScanInputMock(t, extracttest.ScanInputMockConfig{ 165 Path: tc.path, 166 }) 167 defer extracttest.CloseTestScanInput(t, input) 168 169 got, _ := extr.Extract(t.Context(), &input) 170 if diff := cmp.Diff(inventory.Inventory{}, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" { 171 t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tc.path, diff) 172 } 173 }) 174 } 175 }