github.com/google/osv-scalibr@v0.4.1/clients/datasource/pypi_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  	"net/url"
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/google/osv-scalibr/clients/clienttest"
    25  	"github.com/google/osv-scalibr/clients/datasource"
    26  	"github.com/google/osv-scalibr/clients/internal/pypi"
    27  )
    28  
    29  const jsonResp = `{
    30  		"files": [
    31  		  {
    32  			"core-metadata": false,
    33  			"data-dist-info-metadata": false,
    34  			"filename": "beautifulsoup4-4.0.1.tar.gz",
    35  			"hashes": {
    36  			  "sha256": "dc6bc8e8851a1c590c8cc8f25915180fdcce116e268d1f37fa991d2686ea38de"
    37  			},
    38  			"requires-python": null,
    39  			"size": 51024,
    40  			"upload-time": "2014-01-21T05:35:05.558877Z",
    41  			"url": "https://files.pythonhosted.org/packages/6f/be/99dcf74d947cc1e7abef5d0c4572abcb479c33ef791d94453a8fd7987d8f/beautifulsoup4-4.0.1.tar.gz",
    42  			"yanked": false
    43  		  },
    44  		  {
    45  			"core-metadata": false,
    46  			"data-dist-info-metadata": false,
    47  			"filename": "beautifulsoup4-4.0.2.tar.gz",
    48  			"hashes": {
    49  			  "sha256": "353792f8246a9551b232949fb14dce21d9b6ced9207bf9f4a69a4c4eb46c8127"
    50  			},
    51  			"requires-python": null,
    52  			"size": 51240,
    53  			"upload-time": "2014-01-21T05:35:09.581933Z",
    54  			"url": "https://files.pythonhosted.org/packages/a0/75/db36172ea767dd2f0c9817a99e24f7e9b79c2ce63eb2f8b867284cc60daf/beautifulsoup4-4.0.2.tar.gz",
    55  			"yanked": false
    56  		  },
    57  		  {
    58  			"core-metadata": {
    59  			  "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e"
    60  			},
    61  			"data-dist-info-metadata": {
    62  			  "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e"
    63  			},
    64  			"filename": "beautifulsoup4-4.12.3-py3-none-any.whl",
    65  			"hashes": {
    66  			  "sha256": "b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"
    67  			},
    68  			"requires-python": ">=3.6.0",
    69  			"size": 147925,
    70  			"upload-time": "2024-01-17T16:53:12.779164Z",
    71  			"url": "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl",
    72  			"yanked": false
    73  		  },
    74  		  {
    75  			"core-metadata": false,
    76  			"data-dist-info-metadata": false,
    77  			"filename": "beautifulsoup4-4.12.3.tar.gz",
    78  			"hashes": {
    79  			  "sha256": "74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"
    80  			},
    81  			"requires-python": ">=3.6.0",
    82  			"size": 581181,
    83  			"upload-time": "2024-01-17T16:53:17.902970Z",
    84  			"url": "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz",
    85  			"yanked": false
    86  		  },
    87  		  {
    88  			"core-metadata": {
    89  			  "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5"
    90  			},
    91  			"data-dist-info-metadata": {
    92  			  "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5"
    93  			},
    94  			"filename": "beautifulsoup4-4.13.0b2-py3-none-any.whl",
    95  			"hashes": {
    96  			  "sha256": "7e05ad0b6c26108d9990e2235e8a9b4e2c03ead6f391ceb60347f8ebea6b80ba"
    97  			},
    98  			"requires-python": ">=3.6.0",
    99  			"size": 179607,
   100  			"upload-time": "2024-03-20T13:00:33.355932Z",
   101  			"url": "https://files.pythonhosted.org/packages/14/7e/e4313dad823c3a0751c99b9bc0182b1dd19aea164ce7445e9a70429b9e92/beautifulsoup4-4.13.0b2-py3-none-any.whl",
   102  			"yanked": false
   103  		  },
   104  		  {
   105  			"core-metadata": false,
   106  			"data-dist-info-metadata": false,
   107  			"filename": "beautifulsoup4-4.13.0b2.tar.gz",
   108  			"hashes": {
   109  			  "sha256": "c684ddec071aa120819889aa9e8940f85c3f3cdaa08e23b9fa26510387897bd5"
   110  			},
   111  			"requires-python": ">=3.6.0",
   112  			"size": 550258,
   113  			"upload-time": "2024-03-20T13:00:31.245327Z",
   114  			"url": "https://files.pythonhosted.org/packages/81/bd/c97d94e2b96f03d1c50bc9de04130e014eda89322ba604923e0c251eb02e/beautifulsoup4-4.13.0b2.tar.gz",
   115  			"yanked": false
   116  		  },
   117  		  {
   118  			"filename": "beautifulsoup4-4.14.tar.gz",
   119  			"yanked": "yanked"
   120  		  }
   121  		],
   122  		"meta": {
   123  		  "_last-serial": 22406780,
   124  		  "api-version": "1.1"
   125  		},
   126  		"name": "beautifulsoup4",
   127  		"versions": [
   128  		  "4.0.1",
   129  		  "4.0.2",
   130  		  "4.12.3",
   131  		  "4.13.0b2",
   132  		  "4.14"
   133  		]
   134  }`
   135  
   136  func TestGetVersions(t *testing.T) {
   137  	srv := clienttest.NewMockHTTPServer(t)
   138  	client := datasource.NewPyPIRegistryAPIClient(srv.URL, "")
   139  	srv.SetResponse(t, "/beautifulsoup4/", []byte(jsonResp))
   140  
   141  	got, err := client.GetIndex(t.Context(), "beautifulsoup4")
   142  	if err != nil {
   143  		t.Fatalf("failed to get versions of PyPI project %s: %v", "beautifulsoup4", err)
   144  	}
   145  	want := pypi.IndexResponse{
   146  		Name: "beautifulsoup4",
   147  		Files: []pypi.File{
   148  			{
   149  				Name: "beautifulsoup4-4.0.1.tar.gz",
   150  				URL:  "https://files.pythonhosted.org/packages/6f/be/99dcf74d947cc1e7abef5d0c4572abcb479c33ef791d94453a8fd7987d8f/beautifulsoup4-4.0.1.tar.gz",
   151  			},
   152  			{
   153  				Name: "beautifulsoup4-4.0.2.tar.gz",
   154  				URL:  "https://files.pythonhosted.org/packages/a0/75/db36172ea767dd2f0c9817a99e24f7e9b79c2ce63eb2f8b867284cc60daf/beautifulsoup4-4.0.2.tar.gz",
   155  			},
   156  			{
   157  				Name: "beautifulsoup4-4.12.3-py3-none-any.whl",
   158  				URL:  "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl",
   159  			},
   160  			{
   161  				Name: "beautifulsoup4-4.12.3.tar.gz",
   162  				URL:  "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz",
   163  			},
   164  			{
   165  				Name: "beautifulsoup4-4.13.0b2-py3-none-any.whl",
   166  				URL:  "https://files.pythonhosted.org/packages/14/7e/e4313dad823c3a0751c99b9bc0182b1dd19aea164ce7445e9a70429b9e92/beautifulsoup4-4.13.0b2-py3-none-any.whl",
   167  			},
   168  			{
   169  				Name: "beautifulsoup4-4.13.0b2.tar.gz",
   170  				URL:  "https://files.pythonhosted.org/packages/81/bd/c97d94e2b96f03d1c50bc9de04130e014eda89322ba604923e0c251eb02e/beautifulsoup4-4.13.0b2.tar.gz",
   171  			},
   172  			{
   173  				Name:   "beautifulsoup4-4.14.tar.gz",
   174  				Yanked: pypi.Yanked{Value: true},
   175  			},
   176  		},
   177  		Versions: []string{
   178  			"4.0.1",
   179  			"4.0.2",
   180  			"4.12.3",
   181  			"4.13.0b2",
   182  			"4.14",
   183  		},
   184  	}
   185  	if diff := cmp.Diff(want, got); diff != "" {
   186  		t.Errorf("GetIndex(%s) mismatch (-want +got):\n%s", "beautifulsoup4", diff)
   187  	}
   188  }
   189  
   190  func TestPyPILocalRegistry(t *testing.T) {
   191  	tempDir := t.TempDir()
   192  	srv := clienttest.NewMockHTTPServer(t)
   193  	client := datasource.NewPyPIRegistryAPIClient(srv.URL, tempDir)
   194  	srv.SetResponse(t, "/beautifulsoup4/", []byte(jsonResp))
   195  
   196  	_, err := client.GetIndex(t.Context(), "beautifulsoup4")
   197  	if err != nil {
   198  		t.Fatalf("failed to get versions of PyPI project %s: %v", "beautifulsoup4", err)
   199  	}
   200  
   201  	// Check that the JSON response is stored locally.
   202  	parsed, err := url.Parse(srv.URL)
   203  	if err != nil {
   204  		t.Fatalf("failed to parse URL %s: %v", srv.URL, err)
   205  	}
   206  	filePath := filepath.Join(tempDir, "pypi", parsed.Hostname(), "beautifulsoup4")
   207  	content, err := os.ReadFile(filePath)
   208  	if err != nil {
   209  		t.Fatalf("failed to read file: %v", err)
   210  	}
   211  	if string(content) != jsonResp {
   212  		t.Errorf("unexpected file content: got %s, want %s", string(content), jsonResp)
   213  	}
   214  }