diff --git a/gcp/api/cloudbuild.yaml b/gcp/api/cloudbuild.yaml index 7190530a129..754459fbf7e 100644 --- a/gcp/api/cloudbuild.yaml +++ b/gcp/api/cloudbuild.yaml @@ -43,4 +43,13 @@ steps: - CLOUDBUILD=1 waitFor: ['init', 'sync'] +- name: 'gcr.io/oss-vdb/ci' + id: 'api-snapshot-tests' + dir: gcp/api + args: ['bash', '-ex', 'run_tests_e2e.sh', '/workspace/dummy.json'] + env: + - CLOUDBUILD=1 + # Don't run at the same time as api-tests + waitFor: ['init', 'sync', 'api-tests'] + timeout: 7200s diff --git a/gcp/api/run_apitester.py b/gcp/api/run_apitester.py new file mode 100644 index 00000000000..281063a320b --- /dev/null +++ b/gcp/api/run_apitester.py @@ -0,0 +1,72 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Run E2E golang cassette API tests.""" + +import os +import sys +import subprocess +import time +import test_server + +_PORT = 8080 + + +def main(): + if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} path/to/credential.json') + sys.exit(1) + + credential_path = sys.argv[1] + + # Ensure Docker image is pulled + subprocess.run( + ['docker', 'pull', 'gcr.io/endpoints-release/endpoints-runtime:2'], + check=True) + + print("Starting test server...") + server = test_server.start(credential_path, port=_PORT) + + # Wait for server to start up + time.sleep(10) + + try: + # Determine API URL + if os.getenv('CLOUDBUILD'): + host = test_server.get_cloudbuild_esp_host() + else: + host = 'localhost' + + api_base_url = f"{host}:{_PORT}" + print(f"Running Go tests against {api_base_url}") + + env = os.environ.copy() + env['OSV_API_BASE_URL'] = api_base_url + + # Go tests path + # Assuming this script is in gcp/api/ + go_test_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), '../../tools/apitester')) + + cmd = ['go', 'test', './...'] + print(f"Executing: {' '.join(cmd)} in {go_test_dir}") + + subprocess.run(cmd, cwd=go_test_dir, env=env, check=True) + + finally: + print("Stopping test server...") + server.stop() + + +if __name__ == '__main__': + main() diff --git a/gcp/api/run_tests_e2e.sh b/gcp/api/run_tests_e2e.sh new file mode 100755 index 00000000000..0ebd0bd8bdf --- /dev/null +++ b/gcp/api/run_tests_e2e.sh @@ -0,0 +1,28 @@ +#!/bin/bash -x +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [ $# -lt 1 ]; then + echo "Usage: $0 /path/to/credential.json" + exit 1 +fi + +export GOOGLE_CLOUD_PROJECT=oss-vdb-test OSV_VULNERABILITIES_BUCKET=osv-test-vulnerabilities + +# Try to start docker if not running (mostly for CI) +service docker start || true + +set -e + +poetry run python run_apitester.py "$1" diff --git a/tools/apitester/README.md b/tools/apitester/README.md index 6b7d142f140..719ae68737a 100644 --- a/tools/apitester/README.md +++ b/tools/apitester/README.md @@ -46,4 +46,5 @@ Before the test suite is actually run, the cassettes will be "cleaned" so that - the `response` is property is not present, to reduce the size of each cassette By default, requests are made against the local instance of the API, but you can -use the `OSV_API_BASE_URL` to point it against other instances. +use the `OSV_API_BASE_URL` to point it against other instances. +E.g. `OSV_API_BASE_URL=api.test.osv.dev go test ./...` diff --git a/tools/apitester/__snapshots__/cassette_TestCommand.snap b/tools/apitester/__snapshots__/cassette_TestCommand.snap index 59066590159..cb8a161e484 100755 --- a/tools/apitester/__snapshots__/cassette_TestCommand.snap +++ b/tools/apitester/__snapshots__/cassette_TestCommand.snap @@ -3141,11 +3141,11 @@ }, { "id": "PYSEC-2023-192", - "modified": "" + "modified": "" }, { "id": "PYSEC-2023-212", - "modified": "" + "modified": "" } ] }, @@ -3189,11 +3189,11 @@ }, { "id": "PYSEC-2023-192", - "modified": "" + "modified": "" }, { "id": "PYSEC-2023-212", - "modified": "" + "modified": "" } ] }, @@ -3619,11 +3619,11 @@ }, { "id": "PYSEC-2023-192", - "modified": "" + "modified": "" }, { "id": "PYSEC-2023-212", - "modified": "" + "modified": "" } ] }, @@ -3667,11 +3667,11 @@ }, { "id": "PYSEC-2023-192", - "modified": "" + "modified": "" }, { "id": "PYSEC-2023-212", - "modified": "" + "modified": "" } ] }, diff --git a/tools/apitester/__snapshots__/cassette_TestCommand_CallAnalysis.snap b/tools/apitester/__snapshots__/cassette_TestCommand_CallAnalysis.snap index c7fa922010d..012857de860 100755 --- a/tools/apitester/__snapshots__/cassette_TestCommand_CallAnalysis.snap +++ b/tools/apitester/__snapshots__/cassette_TestCommand_CallAnalysis.snap @@ -6,7 +6,7 @@ "vulns": [ { "id": "GHSA-c3h9-896r-86jm", - "modified": "" + "modified": "" }, { "id": "GO-2021-0053", @@ -74,7 +74,7 @@ "vulns": [ { "id": "GHSA-c3h9-896r-86jm", - "modified": "" + "modified": "" }, { "id": "GO-2021-0053", @@ -94,7 +94,7 @@ "vulns": [ { "id": "GHSA-c3h9-896r-86jm", - "modified": "" + "modified": "" }, { "id": "GO-2021-0053", diff --git a/tools/apitester/__snapshots__/cassette_TestCommand_Transitive.snap b/tools/apitester/__snapshots__/cassette_TestCommand_Transitive.snap index 66c1c427573..59adbdea6f7 100755 --- a/tools/apitester/__snapshots__/cassette_TestCommand_Transitive.snap +++ b/tools/apitester/__snapshots__/cassette_TestCommand_Transitive.snap @@ -634,11 +634,11 @@ }, { "id": "PYSEC-2023-192", - "modified": "" + "modified": "" }, { "id": "PYSEC-2023-212", - "modified": "" + "modified": "" } ] }, diff --git a/tools/apitester/__snapshots__/cassette_single_query.snap b/tools/apitester/__snapshots__/cassette_single_query.snap index 680b407c069..5181c5d1562 100755 --- a/tools/apitester/__snapshots__/cassette_single_query.snap +++ b/tools/apitester/__snapshots__/cassette_single_query.snap @@ -1467,7 +1467,6 @@ "related": [ "ALSA-2025:1671", "ALSA-2025:1673", - "CGA-962m-89hc-rmjq", "RLSA-2025:1673", "SUSE-SU-2024:2784-1", "SUSE-SU-2024:2930-1", @@ -1546,7 +1545,6 @@ "modified": "", "published": "2024-09-11T10:15:02.883Z", "related": [ - "CGA-g55g-qx76-5fjj", "SUSE-SU-2024:3202-1", "SUSE-SU-2024:3203-1", "SUSE-SU-2024:3204-1", @@ -1710,6 +1708,7 @@ "modified": "", "published": "2025-02-05T10:15:22.710Z", "related": [ + "CGA-gr5c-pjrp-3fmw", "MGASA-2025-0123", "SUSE-SU-2025:0369-1", "SUSE-SU-2025:0370-1", @@ -1768,7 +1767,11 @@ "aliases": ["CURL-CVE-2025-0665"], "modified": "", "published": "2025-02-05T10:15:22.857Z", - "related": ["MGASA-2025-0123", "openSUSE-SU-2025:14809-1"], + "related": [ + "CGA-h2f8-6v5h-2qcp", + "MGASA-2025-0123", + "openSUSE-SU-2025:14809-1" + ], "references": [ { "type": "ADVISORY", @@ -1830,6 +1833,7 @@ "modified": "", "published": "2025-02-05T10:15:22.980Z", "related": [ + "CGA-378j-cghq-mmhg", "MGASA-2025-0123", "SUSE-SU-2025:0369-1", "SUSE-SU-2025:0370-1", diff --git a/tools/apitester/internal/vcr/interactions.go b/tools/apitester/internal/vcr/interactions.go index b61b1bdbc6b..c3d41e49961 100644 --- a/tools/apitester/internal/vcr/interactions.go +++ b/tools/apitester/internal/vcr/interactions.go @@ -3,6 +3,7 @@ package vcr import ( "net/http" "os" + "strings" "testing" "gopkg.in/dnaeon/go-vcr.v4/pkg/cassette" @@ -32,10 +33,11 @@ func Play(t *testing.T, interaction *cassette.Interaction) *http.Response { } req.URL.Host = fetchAPIBaseURL() + req.Host = req.URL.Host req.Header.Set("User-Agent", "osv.dev/apitester") req.ContentLength = -1 - if req.URL.Hostname() == "localhost" || req.URL.Hostname() == "127.0.0.1" { + if req.URL.Hostname() == "localhost" || req.URL.Hostname() == "127.0.0.1" || strings.HasPrefix(req.URL.Hostname(), "192.168.") { req.URL.Scheme = "http" }