github.com/google/osv-scalibr@v0.4.1/clients/datasource/npm_registry_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 datasource_test 16 17 import ( 18 "cmp" 19 "path/filepath" 20 "strings" 21 "testing" 22 23 gocmp "github.com/google/go-cmp/cmp" 24 "github.com/google/go-cmp/cmp/cmpopts" 25 "github.com/google/osv-scalibr/clients/clienttest" 26 "github.com/google/osv-scalibr/clients/datasource" 27 "github.com/tidwall/gjson" 28 ) 29 30 func TestNpmRegistryClient(t *testing.T) { 31 //nolint:gosec // "Potential hardcoded credentials" :) 32 const ( 33 auth = "Y29vbDphdXRoCg==" 34 authToken = "bmljZS10b2tlbgo=" 35 ) 36 37 srv1 := clienttest.NewMockHTTPServer(t) 38 srv1.SetAuthorization(t, "Basic "+auth) 39 srv1.SetResponseFromFile(t, "/fake-package", "./testdata/npm_registry/fake-package.json") 40 srv1.SetResponseFromFile(t, "/fake-package/2.2.2", "./testdata/npm_registry/fake-package-2.2.2.json") 41 42 srv2 := clienttest.NewMockHTTPServer(t) 43 srv2.SetAuthorization(t, "Bearer "+authToken) 44 srv2.SetResponseFromFile(t, "/@fake-registry%2fa", "./testdata/npm_registry/fake-registry-a.json") 45 46 npmrcFile := createTempNpmrc(t, ".npmrc") 47 writeToNpmrc(t, npmrcFile, 48 "registry="+srv1.URL, 49 "//"+strings.TrimPrefix(srv1.URL, "http://")+"/:_auth="+auth, 50 "@fake-registry:registry="+srv2.URL, 51 "//"+strings.TrimPrefix(srv2.URL, "http://")+"/:_authToken="+authToken, 52 ) 53 54 cl, err := datasource.NewNPMRegistryAPIClient(filepath.Dir(npmrcFile)) 55 if err != nil { 56 t.Fatalf("failed creating npm api client: %v", err) 57 } 58 { 59 const pkg = "fake-package" 60 want := datasource.NPMRegistryVersions{ 61 Versions: []string{"1.0.0", "2.2.2"}, 62 Tags: map[string]string{ 63 "latest": "1.0.0", 64 "version1": "1.0.0", 65 "version2": "2.2.2", 66 }, 67 } 68 got, err := cl.Versions(t.Context(), pkg) 69 if err != nil { 70 t.Fatalf("failed getting versions: %v", err) 71 } 72 if diff := gocmp.Diff(want, got, cmpopts.SortSlices(cmp.Less[string])); diff != "" { 73 t.Errorf("Versions(\"%s\") (-want +got)\n%s", pkg, diff) 74 } 75 } 76 { 77 const pkg = "@fake-registry/a" 78 want := datasource.NPMRegistryVersions{ 79 Versions: []string{"1.2.3", "2.3.4"}, 80 Tags: map[string]string{"latest": "2.3.4"}, 81 } 82 got, err := cl.Versions(t.Context(), pkg) 83 if err != nil { 84 t.Fatalf("failed getting versions: %v", err) 85 } 86 if diff := gocmp.Diff(want, got, cmpopts.SortSlices(cmp.Less[string])); diff != "" { 87 t.Errorf("Versions(\"%s\") (-want +got)\n%s", pkg, diff) 88 } 89 } 90 91 { 92 const pkg = "fake-package" 93 const ver = "2.2.2" 94 want := datasource.NPMRegistryDependencies{ 95 Dependencies: map[string]string{ 96 "a": "^3.0.1", 97 "b": "^2.0.1", 98 "e": "^0.2.33", 99 "f": "npm:g@^2.0.1", 100 }, 101 DevDependencies: map[string]string{ 102 "c": "^1.1.1", 103 "d": "^1.0.2", 104 }, 105 PeerDependencies: map[string]string{ 106 "h": "^1.0.0", 107 }, 108 OptionalDependencies: map[string]string{ 109 "e": "^0.2.33", 110 "f": "npm:g@^2.0.1", 111 }, 112 BundleDependencies: []string{ 113 "a", 114 }, 115 } 116 got, err := cl.Dependencies(t.Context(), pkg, ver) 117 if err != nil { 118 t.Fatalf("failed getting dependencies: %v", err) 119 } 120 if diff := gocmp.Diff(want, got, cmpopts.SortSlices(cmp.Less[string])); diff != "" { 121 t.Errorf("Dependencies(\"%s\", \"%s\") (-want +got)\n%s", pkg, ver, diff) 122 } 123 } 124 { 125 const pkg = "fake-package" 126 const ver = "2.2.2" 127 want := gjson.Parse(`{ 128 "name": "fake-package", 129 "version": "2.2.2", 130 "main": "index.js", 131 "scripts": { 132 "test": "echo \"Error: no test specified\" && exit 1" 133 }, 134 "author": "", 135 "license": "ISC", 136 "dependencies": { 137 "a": "^3.0.1", 138 "b": "^2.0.1", 139 "e": "^0.2.33", 140 "f": "npm:g@^2.0.1" 141 }, 142 "devDependencies": { 143 "c": "^1.1.1", 144 "d": "^1.0.2" 145 }, 146 "optionalDependencies": { 147 "e": "^0.2.33", 148 "f": "npm:g@^2.0.1" 149 }, 150 "peerDependencies": { 151 "h": "^1.0.0" 152 }, 153 "bundleDependencies": [ 154 "a" 155 ], 156 "_id": "fake-package@2.2.2", 157 "_nodeVersion": "20.9.0", 158 "_npmVersion": "10.1.0", 159 "dist": { 160 "integrity": "sha512-NWvNE9fxykrzSQVr1CSKchzkQr5qwplvgn3O/0JL46qM6BhoGlKRjLiaZYdo1byXJWLGthghOgGpUZiEL04HQQ==", 161 "shasum": "8dc47515da4e67bb794a4c9c7f4750bb4d67c7fc", 162 "tarball": "http://localhost:4873/fake-package/-/fake-package-2.2.2.tgz" 163 }, 164 "contributors": [] 165 }`) 166 got, err := cl.FullJSON(t.Context(), pkg, ver) 167 if err != nil { 168 t.Fatalf("failed getting full json: %v", err) 169 } 170 wantMap := want.Value().(map[string]any) 171 gotMap := got.Value().(map[string]any) 172 if diff := gocmp.Diff(wantMap, gotMap, cmpopts.SortSlices(cmp.Less[string])); diff != "" { 173 t.Errorf("FullJSON(\"%s\", \"%s\") (-want +got)\n%s", pkg, ver, diff) 174 } 175 } 176 }