github.com/google/osv-scalibr@v0.4.1/guidedremediation/internal/strategy/relax/relax_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 relax_test 16 17 import ( 18 "encoding/json" 19 "os" 20 "testing" 21 22 "deps.dev/util/resolve/dep" 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/go-cmp/cmp/cmpopts" 25 "github.com/google/osv-scalibr/clients/clienttest" 26 scalibrfs "github.com/google/osv-scalibr/fs" 27 "github.com/google/osv-scalibr/guidedremediation/internal/manifest" 28 "github.com/google/osv-scalibr/guidedremediation/internal/manifest/npm" 29 "github.com/google/osv-scalibr/guidedremediation/internal/manifest/python" 30 "github.com/google/osv-scalibr/guidedremediation/internal/remediation" 31 "github.com/google/osv-scalibr/guidedremediation/internal/strategy/relax" 32 "github.com/google/osv-scalibr/guidedremediation/internal/vulnenrichertest" 33 "github.com/google/osv-scalibr/guidedremediation/options" 34 "github.com/google/osv-scalibr/guidedremediation/result" 35 "github.com/google/osv-scalibr/guidedremediation/upgrade" 36 ) 37 38 func TestComputePatches(t *testing.T) { 39 npmRW, err := npm.GetReadWriter() 40 if err != nil { 41 t.Fatalf("failed getting npm ReadWriter: %v", err) 42 } 43 pythonRW, _ := python.GetRequirementsReadWriter() 44 45 tests := []struct { 46 name string 47 universeFile string 48 vulnsFile string 49 manifestPath string 50 readWriter manifest.ReadWriter 51 opts options.RemediationOptions 52 wantFile string 53 }{ 54 { 55 name: "npm-simple", 56 universeFile: "testdata/npm/universe.yaml", 57 vulnsFile: "testdata/npm/vulnerabilities.json", 58 manifestPath: "npm/simple/package.json", 59 readWriter: npmRW, 60 opts: options.DefaultRemediationOptions(), 61 wantFile: "testdata/npm/simple/patches.json", 62 }, 63 { 64 name: "npm-vuln-without-fix", 65 universeFile: "testdata/npm/universe.yaml", 66 vulnsFile: "testdata/npm/vulnerabilities.json", 67 manifestPath: "npm/vuln-without-fix/package.json", 68 readWriter: npmRW, 69 opts: options.DefaultRemediationOptions(), 70 wantFile: "testdata/npm/vuln-without-fix/patches.json", 71 }, 72 { 73 name: "npm-diamond", 74 universeFile: "testdata/npm/universe.yaml", 75 vulnsFile: "testdata/npm/vulnerabilities.json", 76 manifestPath: "npm/diamond/package.json", 77 readWriter: npmRW, 78 opts: options.DefaultRemediationOptions(), 79 wantFile: "testdata/npm/diamond/patches.json", 80 }, 81 { 82 name: "npm-removed-vuln-dep", 83 universeFile: "testdata/npm/universe.yaml", 84 vulnsFile: "testdata/npm/vulnerabilities.json", 85 manifestPath: "npm/removed-vuln/package.json", 86 readWriter: npmRW, 87 opts: options.DefaultRemediationOptions(), 88 wantFile: "testdata/npm/removed-vuln/patches.json", 89 }, 90 { 91 name: "npm-introduced-vuln", 92 universeFile: "testdata/npm/universe.yaml", 93 vulnsFile: "testdata/npm/vulnerabilities.json", 94 manifestPath: "npm/introduce-vuln/package.json", 95 readWriter: npmRW, 96 opts: options.DefaultRemediationOptions(), 97 wantFile: "testdata/npm/introduce-vuln/patches.json", 98 }, 99 { 100 name: "npm-non-constraining-dep", 101 universeFile: "testdata/npm/universe.yaml", 102 vulnsFile: "testdata/npm/vulnerabilities.json", 103 manifestPath: "npm/non-constraining/package.json", 104 readWriter: npmRW, 105 opts: options.DefaultRemediationOptions(), 106 wantFile: "testdata/npm/non-constraining/patches.json", 107 }, 108 { 109 name: "npm-deepen-to-remediate", 110 universeFile: "testdata/npm/universe.yaml", 111 vulnsFile: "testdata/npm/vulnerabilities.json", 112 manifestPath: "npm/deepen/package.json", 113 readWriter: npmRW, 114 opts: options.RemediationOptions{ 115 MaxDepth: 3, 116 UpgradeConfig: upgrade.NewConfig(), 117 }, 118 wantFile: "testdata/npm/deepen/patches.json", 119 }, 120 { 121 name: "python-simple", 122 universeFile: "testdata/python/universe.yaml", 123 vulnsFile: "testdata/python/vulnerabilities.json", 124 manifestPath: "python/simple/requirements.txt", 125 readWriter: pythonRW, 126 opts: options.DefaultRemediationOptions(), 127 wantFile: "testdata/python/simple/patches.json", 128 }, 129 { 130 name: "python-no-fix", 131 universeFile: "testdata/python/universe.yaml", 132 vulnsFile: "testdata/python/vulnerabilities.json", 133 manifestPath: "python/no-fix/requirements.txt", 134 readWriter: pythonRW, 135 opts: options.DefaultRemediationOptions(), 136 wantFile: "testdata/python/no-fix/patches.json", 137 }, 138 { 139 name: "python-diamond", 140 universeFile: "testdata/python/universe.yaml", 141 vulnsFile: "testdata/python/vulnerabilities.json", 142 manifestPath: "python/diamond/requirements.txt", 143 readWriter: pythonRW, 144 opts: options.DefaultRemediationOptions(), 145 wantFile: "testdata/python/diamond/patches.json", 146 }, 147 { 148 name: "python-removed-dependency", 149 universeFile: "testdata/python/universe.yaml", 150 vulnsFile: "testdata/python/vulnerabilities.json", 151 manifestPath: "python/removed/requirements.txt", 152 readWriter: pythonRW, 153 opts: options.DefaultRemediationOptions(), 154 wantFile: "testdata/python/removed/patches.json", 155 }, 156 { 157 name: "python-introduce-new-vuln", 158 universeFile: "testdata/python/universe.yaml", 159 vulnsFile: "testdata/python/vulnerabilities.json", 160 manifestPath: "python/introduce/requirements.txt", 161 readWriter: pythonRW, 162 opts: options.DefaultRemediationOptions(), 163 wantFile: "testdata/python/introduce/patches.json", 164 }, 165 { 166 name: "python-non-constraining-dependency", 167 universeFile: "testdata/python/universe.yaml", 168 vulnsFile: "testdata/python/vulnerabilities.json", 169 manifestPath: "python/non-constraining/requirements.txt", 170 readWriter: pythonRW, 171 opts: options.DefaultRemediationOptions(), 172 wantFile: "testdata/python/non-constraining/patches.json", 173 }, 174 { 175 name: "python-deepen", 176 universeFile: "testdata/python/universe.yaml", 177 vulnsFile: "testdata/python/vulnerabilities.json", 178 manifestPath: "python/deepen/requirements.txt", 179 readWriter: pythonRW, 180 opts: options.DefaultRemediationOptions(), 181 wantFile: "testdata/python/deepen/patches.json", 182 }, 183 { 184 name: "python-max-depth", 185 universeFile: "testdata/python/universe.yaml", 186 vulnsFile: "testdata/python/vulnerabilities.json", 187 manifestPath: "python/max-depth/requirements.txt", 188 readWriter: pythonRW, 189 opts: options.RemediationOptions{ 190 MaxDepth: 3, 191 UpgradeConfig: upgrade.NewConfig(), 192 }, 193 wantFile: "testdata/python/max-depth/patches.json", 194 }, 195 } 196 197 for _, tt := range tests { 198 t.Run(tt.name, func(t *testing.T) { 199 wantFile, err := os.Open(tt.wantFile) 200 if err != nil { 201 t.Fatalf("failed opening wantFile: %v", err) 202 } 203 defer wantFile.Close() 204 var want []result.Patch 205 if err := json.NewDecoder(wantFile).Decode(&want); err != nil { 206 t.Fatalf("failed decoding wantFile: %v", err) 207 } 208 209 fsys := scalibrfs.DirFS("./testdata") 210 m, err := tt.readWriter.Read(tt.manifestPath, fsys) 211 if err != nil { 212 t.Fatalf("failed reading manifest: %v", err) 213 } 214 215 cl := clienttest.NewMockResolutionClient(t, tt.universeFile) 216 ve := vulnenrichertest.NewMockVulnerabilityEnricher(t, tt.vulnsFile) 217 resolved, err := remediation.ResolveManifest(t.Context(), cl, ve, m, &tt.opts) 218 if err != nil { 219 t.Fatalf("failed resolving manifest: %v", err) 220 } 221 gotFull, err := relax.ComputePatches(t.Context(), cl, ve, resolved, &tt.opts) 222 if err != nil { 223 t.Fatalf("failed computing patches: %v", err) 224 } 225 got := gotFull.Patches 226 227 // Type is not in exported to json, so just ignore it. 228 if diff := cmp.Diff(want, got, cmpopts.EquateEmpty(), cmpopts.IgnoreTypes(dep.Type{})); diff != "" { 229 t.Errorf("ComputePatches: unexpected diff (-want +got):\n%s", diff) 230 } 231 }) 232 } 233 }