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  }