github.com/google/osv-scalibr@v0.4.1/guidedremediation/internal/manifest/python/poetry_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 python
    16  
    17  import (
    18  	"os"
    19  	"path/filepath"
    20  	"testing"
    21  
    22  	"deps.dev/util/resolve"
    23  	"deps.dev/util/resolve/dep"
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/google/osv-scalibr/fs"
    26  	"github.com/google/osv-scalibr/guidedremediation/internal/manifest"
    27  	"github.com/google/osv-scalibr/guidedremediation/result"
    28  )
    29  
    30  func TestReadPoetry(t *testing.T) {
    31  	fsys := fs.DirFS("./testdata/poetry")
    32  	poetryRW, _ := GetPoetryReadWriter()
    33  	got, err := poetryRW.Read("pyproject.toml", fsys)
    34  	if err != nil {
    35  		t.Fatalf("error reading manifest: %v", err)
    36  	}
    37  
    38  	var optionalType dep.Type
    39  	optionalType.AddAttr(dep.Opt, "")
    40  
    41  	want := testManifest{
    42  		FilePath: "pyproject.toml",
    43  		Root: resolve.Version{
    44  			VersionKey: resolve.VersionKey{
    45  				PackageKey: resolve.PackageKey{
    46  					System: resolve.PyPI,
    47  					Name:   "my-project",
    48  				},
    49  				VersionType: resolve.Concrete,
    50  				Version:     "1.2.3",
    51  			},
    52  		},
    53  		System: resolve.PyPI,
    54  		Requirements: []resolve.RequirementVersion{
    55  			{
    56  				VersionKey: resolve.VersionKey{
    57  					PackageKey: resolve.PackageKey{
    58  						System: resolve.PyPI,
    59  						Name:   "requests",
    60  					},
    61  					Version:     "~=2.25.1",
    62  					VersionType: resolve.Requirement,
    63  				},
    64  			},
    65  			{
    66  				VersionKey: resolve.VersionKey{
    67  					PackageKey: resolve.PackageKey{
    68  						System: resolve.PyPI,
    69  						Name:   "numpy",
    70  					},
    71  					Version:     "==1.22.0",
    72  					VersionType: resolve.Requirement,
    73  				},
    74  			},
    75  			{
    76  				VersionKey: resolve.VersionKey{
    77  					PackageKey: resolve.PackageKey{
    78  						System: resolve.PyPI,
    79  						Name:   "django",
    80  					},
    81  					Version:     ">2.1,<3.0",
    82  					VersionType: resolve.Requirement,
    83  				},
    84  			},
    85  			{
    86  				VersionKey: resolve.VersionKey{
    87  					PackageKey: resolve.PackageKey{
    88  						System: resolve.PyPI,
    89  						Name:   "django",
    90  					},
    91  					Version:     ">2.0,<3.0",
    92  					VersionType: resolve.Requirement,
    93  				},
    94  			},
    95  			{
    96  				VersionKey: resolve.VersionKey{
    97  					PackageKey: resolve.PackageKey{
    98  						System: resolve.PyPI,
    99  						Name:   "pytest",
   100  					},
   101  					Version:     ">=6.2.5",
   102  					VersionType: resolve.Requirement,
   103  				},
   104  				Type: optionalType,
   105  			},
   106  			{
   107  				VersionKey: resolve.VersionKey{
   108  					PackageKey: resolve.PackageKey{
   109  						System: resolve.PyPI,
   110  						Name:   "black",
   111  					},
   112  					Version:     "==22.3.0",
   113  					VersionType: resolve.Requirement,
   114  				},
   115  				Type: optionalType,
   116  			},
   117  		},
   118  		Groups: map[manifest.RequirementKey][]string{
   119  			manifest.RequirementKey(resolve.PackageKey{System: resolve.PyPI, Name: "pytest"}): {"dev"},
   120  			manifest.RequirementKey(resolve.PackageKey{System: resolve.PyPI, Name: "black"}):  {"dev"},
   121  		},
   122  	}
   123  	checkManifest(t, "Manifest", got, want)
   124  }
   125  
   126  func TestWritePoetry(t *testing.T) {
   127  	rw, _ := GetPoetryReadWriter()
   128  	fsys := fs.DirFS("./testdata/poetry")
   129  	manif, err := rw.Read("pyproject.toml", fsys)
   130  	if err != nil {
   131  		t.Fatalf("error reading manifest: %v", err)
   132  	}
   133  
   134  	patches := []result.Patch{
   135  		{
   136  			PackageUpdates: []result.PackageUpdate{
   137  				{
   138  					Name:        "requests",
   139  					VersionFrom: "~=2.25.1",
   140  					VersionTo:   ">=2.26.0,<3.0.0",
   141  				},
   142  			},
   143  		},
   144  		{
   145  			PackageUpdates: []result.PackageUpdate{
   146  				{
   147  					Name:        "black",
   148  					VersionFrom: "==22.3.0",
   149  					VersionTo:   "==23.0.0",
   150  				},
   151  			},
   152  		},
   153  		{
   154  			PackageUpdates: []result.PackageUpdate{
   155  				{
   156  					Name:        "django",
   157  					VersionFrom: ">2.1,<3.0",
   158  					VersionTo:   ">=3.1,<4.0",
   159  				},
   160  			},
   161  		},
   162  		{
   163  			PackageUpdates: []result.PackageUpdate{
   164  				{
   165  					Name:        "django",
   166  					VersionFrom: ">2.0,<3.0",
   167  					VersionTo:   ">=3.0,<4.0",
   168  				},
   169  			},
   170  		},
   171  	}
   172  	outDir := t.TempDir()
   173  	outFile := filepath.Join(outDir, "pyproject.toml")
   174  
   175  	if err := rw.Write(manif, fsys, patches, outFile); err != nil {
   176  		t.Fatalf("failed to write manifest: %v", err)
   177  	}
   178  
   179  	got, err := os.ReadFile(outFile)
   180  	if err != nil {
   181  		t.Fatalf("failed to read got pyproject.toml: %v", err)
   182  	}
   183  	want, err := os.ReadFile(filepath.Join("./testdata/poetry", "want.pyproject.toml"))
   184  	if err != nil {
   185  		t.Fatalf("failed to read want pyproject.toml: %v", err)
   186  	}
   187  	if diff := cmp.Diff(want, got); diff != "" {
   188  		t.Errorf("pyproject.toml (-want +got):\n%s", diff)
   189  	}
   190  }