From 8ac708ba21033a3938571c29a17d7d6aff7ea555 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 23 Sep 2026 18:10:41 +0000 Subject: [PATCH 1/8] [Better Tests] Bootstrap and cover the GCE metadata emulator Publishes the emulator's default address from py_unittest before any test imports the Google auth libraries, which resolve that address once and keep it. Without this, an emulator started later in a test is unreachable from already-imported code. Adds integration tests covering what the emulator is for: google.auth and clusterfuzz's own credentials wrapper resolving to the trusted identity, a refresh yielding the configured token, and a trusted and an untrusted emulator serving distinct identities side by side. --- .../gce_metadata_emulator_test.py | 200 ++++++++++++++++++ src/local/butler/py_unittest.py | 5 + 2 files changed, 205 insertions(+) create mode 100644 src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py diff --git a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py new file mode 100644 index 00000000000..0d178f54349 --- /dev/null +++ b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py @@ -0,0 +1,200 @@ +# Copyright 2026 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. +"""Tests for the fake GCE metadata server.""" + +import importlib +import json +import os +import re + +import google.auth +from google.auth import compute_engine +from google.auth.transport import requests as google_auth_requests +import pytest +import requests + +from clusterfuzz._internal.google_cloud_utils import compute_metadata +from clusterfuzz._internal.google_cloud_utils import credentials +from clusterfuzz._internal.tests.test_libs import gce_metadata_emulator + + +# TODO(b/555371391) Move this to the new integration test suite +@pytest.mark.skip( + reason='Experimental: pending migration to the new integration test suite.') +class TestGceMetadataEmulator: + """Integration tests for the fake GCE metadata server.""" + + # TODO(b/555371391) Remove this setup once we have our own butler.py command + @pytest.fixture(scope='class', autouse=True) + def _emulators(self, request): + gce_metadata_emulator.bootstrap() + importlib.reload(compute_engine._metadata) # pylint: disable=protected-access + importlib.reload(compute_metadata) + with gce_metadata_emulator.trusted_untrusted_pair() as (tworker, uworker): + request.cls.tworker = tworker + request.cls.uworker = uworker + yield + + def test_default_credentials_come_from_the_trusted_emulator(self): + """Verifies that google.auth.default() resolves Compute Engine credentials from the tworker emulator, not uworker.""" + assert self.tworker.hostport == os.environ['GCE_METADATA_HOST'] + assert self.uworker.hostport != os.environ['GCE_METADATA_HOST'] + + creds, project_id = google.auth.default() + + assert isinstance(creds, compute_engine.Credentials) + assert project_id == 'test-clusterfuzz' + + def test_refreshing_credentials_yields_the_configured_token(self): + """Verifies that refreshing Compute Engine credentials returns the token and email from tworker.json.""" + creds = compute_engine.Credentials() + creds.refresh(google_auth_requests.Request()) + + assert creds.token == 'fake-tworker-access-token' + assert creds.service_account_email == ( + 'test-clusterfuzz-service-account-email') + + def test_clusterfuzz_credentials_wrapper_resolves_from_the_emulator(self): + """Verifies that clusterfuzz's own credentials.get_default() wrapper, not just raw google.auth, lands on the emulator's service account.""" + # get_default() is memoized process-wide, so force a miss rather than + # depend on whichever test class ran first. + creds, _ = credentials.get_default(__memoize_force__=True) + creds.refresh(google_auth_requests.Request()) + + assert isinstance(creds, compute_engine.Credentials) + assert creds.service_account_email == ( + 'test-clusterfuzz-service-account-email') + + def test_trusted_and_untrusted_emulators_serve_distinct_identities(self): + """Verifies that tworker and uworker run on separate ports and serve their respective service account emails and tokens.""" + assert self.tworker.hostport != self.uworker.hostport + for emulator in (self.tworker, self.uworker): + assert re.match(r'^127\.0\.0\.1:\d+$', emulator.hostport) + + assert self.tworker.get('instance/service-accounts/default/email') == ( + 'test-clusterfuzz-service-account-email') + assert self.uworker.get('instance/service-accounts/default/email') == ( + 'test-unpriv-clusterfuzz-service-account-email') + + tworker_token = json.loads( + self.tworker.get('instance/service-accounts/default/token')) + uworker_token = json.loads( + self.uworker.get('instance/service-accounts/default/token')) + assert tworker_token['access_token'] == 'fake-tworker-access-token' + assert uworker_token['access_token'] == 'fake-uworker-access-token' + + def test_get_reads_from_the_trusted_emulator(self): + """Verifies that compute_metadata.get() is answered by the tworker emulator, the in-process identity, and returns tworker.json's values.""" + assert compute_metadata.get('instance/zone') == ( + 'projects/1234567890/zones/us-central1-f') + + assert compute_metadata.get('instance/id') == self.tworker.get( + 'instance/id') + assert compute_metadata.get('instance/id') != self.uworker.get( + 'instance/id') + + def test_is_gce_connects_to_the_running_emulator(self): + """Verifies that compute_metadata.is_gce() opens a real connection to the emulator's ephemeral port and finds it listening.""" + assert compute_metadata._METADATA_SERVER == self.tworker.hostport # pylint: disable=protected-access + assert compute_metadata.is_gce() + + def test_unknown_attribute_is_a_404(self): + """Verifies that a key absent from both instance and project attributes is a 404.""" + with pytest.raises(requests.exceptions.HTTPError) as caught: + self.tworker.get('instance/attributes/does-not-exist') + + assert caught.value.response.status_code == 404 + + def test_instance_attributes_fall_back_to_project_attributes(self): + """Verifies that querying instance/attributes/ falls back to project/attributes/ when not set on the instance.""" + assert compute_metadata.get('instance/attributes/deployment-bucket') == ( + 'test-deployment-bucket') + assert compute_metadata.get('project/attributes/deployment-bucket') == ( + 'test-deployment-bucket') + + def test_get_reads_project_and_instance_attributes(self): + """Verifies that compute_metadata.get() reads both project/attributes/ and instance/attributes/.""" + assert compute_metadata.get('project/attributes/deployment-bucket') == ( + 'test-deployment-bucket') + assert compute_metadata.get('project/attributes/deployment-zip') == ( + 'linux-3.zip') + assert compute_metadata.get( + 'instance/attributes/override_tworker_queue') == 'jobs-linux-test' + + def test_metadata_flavor_header_is_required(self): + """Verifies that the emulator rejects requests missing the 'Metadata-Flavor: Google' header with HTTP 403 Forbidden.""" + response = self.tworker.session.get( + f'{self.tworker.url}/computeMetadata/v1/instance/zone', timeout=10) + + assert response.status_code == 403 + assert response.text == '' + + def test_a_second_emulator_cannot_take_a_held_address(self): + """Verifies that opening an emulator on an address another one already holds raises, instead of failing on a port collision.""" + with pytest.raises(RuntimeError) as caught: + with gce_metadata_emulator.untrusted_as_default(): + pass + + assert self.tworker.hostport in str(caught.value) + assert 'uworker' in str(caught.value) + + +# TODO(b/555371391) Move this to the new integration test suite +@pytest.mark.skip( + reason='Experimental: pending migration to the new integration test suite.') +class TestGceMetadataEmulatorUntrustedDefault: + """Integration tests for a test class that runs as the untrusted worker.""" + + # TODO(b/555371391) Remove this setup once we have our own butler.py command + @pytest.fixture(scope='class', autouse=True) + def _emulator(self, request): + gce_metadata_emulator.bootstrap() + importlib.reload(compute_engine._metadata) # pylint: disable=protected-access + importlib.reload(compute_metadata) + with gce_metadata_emulator.untrusted_as_default() as uworker: + request.cls.uworker = uworker + yield + + def test_default_credentials_come_from_the_untrusted_emulator(self): + """Verifies that google.auth.default() resolves the uworker identity when uworker holds the default address.""" + assert self.uworker.hostport == os.environ['GCE_METADATA_HOST'] + + creds, project_id = google.auth.default() + creds.refresh(google_auth_requests.Request()) + + assert isinstance(creds, compute_engine.Credentials) + assert project_id == 'test-clusterfuzz' + assert creds.service_account_email == ( + 'test-unpriv-clusterfuzz-service-account-email') + assert creds.token == 'fake-uworker-access-token' + + def test_clusterfuzz_credentials_wrapper_resolves_to_the_untrusted_identity( + self): + """Verifies that clusterfuzz's credentials.get_default() wrapper also follows the default address to uworker.""" + # get_default() is memoized process-wide, so a tworker result cached by an + # earlier test class would otherwise be handed back here, and refreshing it + # would ask uworker for a service account it does not serve. + creds, _ = credentials.get_default(__memoize_force__=True) + creds.refresh(google_auth_requests.Request()) + + assert creds.service_account_email == ( + 'test-unpriv-clusterfuzz-service-account-email') + + def test_get_reads_from_the_untrusted_emulator(self): + """Verifies that compute_metadata.get() is answered by uworker, whose instance/id differs from tworker's.""" + assert compute_metadata.get('instance/id') == '2222222222222222222' + assert compute_metadata.get('instance/id') == self.uworker.get( + 'instance/id') + assert compute_metadata.get('instance/hostname') == ( + 'uworker-1.c.test-clusterfuzz.internal') diff --git a/src/local/butler/py_unittest.py b/src/local/butler/py_unittest.py index d401f42764b..1b4ad4cb968 100644 --- a/src/local/butler/py_unittest.py +++ b/src/local/butler/py_unittest.py @@ -283,6 +283,11 @@ def execute(args): if args.pattern is None: args.pattern = '*_test.py' + # TODO(b/555371391) Move this initialization to the new integration test suite + if os.getenv('GCE_METADATA_EMULATOR'): + from clusterfuzz._internal.tests.test_libs import gce_metadata_emulator + gce_metadata_emulator.bootstrap() + if args.parallel: run_tests_parallel(args, test_directory, top_level_dir) else: From 66602dda0cb46dc61e920cab7beedc933eadb343 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Fri, 25 Sep 2026 02:10:19 +0000 Subject: [PATCH 2/8] [Better test] Removes warning in a fixture --- .../google_cloud_utils/gce_metadata_emulator_test.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py index 0d178f54349..d43bc7ea1d6 100644 --- a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py +++ b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py @@ -37,13 +37,14 @@ class TestGceMetadataEmulator: # TODO(b/555371391) Remove this setup once we have our own butler.py command @pytest.fixture(scope='class', autouse=True) - def _emulators(self, request): + @classmethod + def _emulators(cls): gce_metadata_emulator.bootstrap() importlib.reload(compute_engine._metadata) # pylint: disable=protected-access importlib.reload(compute_metadata) with gce_metadata_emulator.trusted_untrusted_pair() as (tworker, uworker): - request.cls.tworker = tworker - request.cls.uworker = uworker + cls.tworker = tworker + cls.uworker = uworker yield def test_default_credentials_come_from_the_trusted_emulator(self): @@ -158,12 +159,13 @@ class TestGceMetadataEmulatorUntrustedDefault: # TODO(b/555371391) Remove this setup once we have our own butler.py command @pytest.fixture(scope='class', autouse=True) - def _emulator(self, request): + @classmethod + def _emulator(cls): gce_metadata_emulator.bootstrap() importlib.reload(compute_engine._metadata) # pylint: disable=protected-access importlib.reload(compute_metadata) with gce_metadata_emulator.untrusted_as_default() as uworker: - request.cls.uworker = uworker + cls.uworker = uworker yield def test_default_credentials_come_from_the_untrusted_emulator(self): From 1ec262e0c576ab5acb9a208eacb22ab0f433f02d Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 30 Sep 2026 17:11:02 +0000 Subject: [PATCH 3/8] [Better tests] Expect the WireMock container error when an emulator address is taken --- .../core/google_cloud_utils/gce_metadata_emulator_test.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py index d43bc7ea1d6..1530bde6b52 100644 --- a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py +++ b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py @@ -23,6 +23,7 @@ from google.auth.transport import requests as google_auth_requests import pytest import requests +from wiremock.testing.testcontainer import WireMockContainerException from clusterfuzz._internal.google_cloud_utils import compute_metadata from clusterfuzz._internal.google_cloud_utils import credentials @@ -142,14 +143,11 @@ def test_metadata_flavor_header_is_required(self): assert response.text == '' def test_a_second_emulator_cannot_take_a_held_address(self): - """Verifies that opening an emulator on an address another one already holds raises, instead of failing on a port collision.""" - with pytest.raises(RuntimeError) as caught: + """Verifies that opening an emulator on an address another one already holds fails.""" + with pytest.raises(WireMockContainerException): with gce_metadata_emulator.untrusted_as_default(): pass - assert self.tworker.hostport in str(caught.value) - assert 'uworker' in str(caught.value) - # TODO(b/555371391) Move this to the new integration test suite @pytest.mark.skip( From 8e8433d9897943e4621fd0ba7bcecc712e9a7f2a Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 30 Sep 2026 17:59:36 +0000 Subject: [PATCH 4/8] [Better-tests] Asserts against a dict instead of hardcoded values --- .../gce_metadata_emulator_test.py | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py index 1530bde6b52..5d49f5ab911 100644 --- a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py +++ b/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py @@ -40,9 +40,12 @@ class TestGceMetadataEmulator: @pytest.fixture(scope='class', autouse=True) @classmethod def _emulators(cls): + """Starts the tworker/uworker pair and loads the configs they serve.""" gce_metadata_emulator.bootstrap() importlib.reload(compute_engine._metadata) # pylint: disable=protected-access importlib.reload(compute_metadata) + cls.tworker_cfg = gce_metadata_emulator.load_config('tworker') + cls.uworker_cfg = gce_metadata_emulator.load_config('uworker') with gce_metadata_emulator.trusted_untrusted_pair() as (tworker, uworker): cls.tworker = tworker cls.uworker = uworker @@ -56,16 +59,19 @@ def test_default_credentials_come_from_the_trusted_emulator(self): creds, project_id = google.auth.default() assert isinstance(creds, compute_engine.Credentials) - assert project_id == 'test-clusterfuzz' + assert project_id == self.tworker_cfg['metadata']['project']['project-id'] def test_refreshing_credentials_yields_the_configured_token(self): """Verifies that refreshing Compute Engine credentials returns the token and email from tworker.json.""" creds = compute_engine.Credentials() + assert creds.token is None + assert creds.service_account_email == 'default' + creds.refresh(google_auth_requests.Request()) - assert creds.token == 'fake-tworker-access-token' + assert creds.token == self.tworker_cfg['credentials']['access_token'] assert creds.service_account_email == ( - 'test-clusterfuzz-service-account-email') + self.tworker_cfg['credentials']['email']) def test_clusterfuzz_credentials_wrapper_resolves_from_the_emulator(self): """Verifies that clusterfuzz's own credentials.get_default() wrapper, not just raw google.auth, lands on the emulator's service account.""" @@ -76,7 +82,7 @@ def test_clusterfuzz_credentials_wrapper_resolves_from_the_emulator(self): assert isinstance(creds, compute_engine.Credentials) assert creds.service_account_email == ( - 'test-clusterfuzz-service-account-email') + self.tworker_cfg['credentials']['email']) def test_trusted_and_untrusted_emulators_serve_distinct_identities(self): """Verifies that tworker and uworker run on separate ports and serve their respective service account emails and tokens.""" @@ -85,21 +91,23 @@ def test_trusted_and_untrusted_emulators_serve_distinct_identities(self): assert re.match(r'^127\.0\.0\.1:\d+$', emulator.hostport) assert self.tworker.get('instance/service-accounts/default/email') == ( - 'test-clusterfuzz-service-account-email') + self.tworker_cfg['credentials']['email']) assert self.uworker.get('instance/service-accounts/default/email') == ( - 'test-unpriv-clusterfuzz-service-account-email') + self.uworker_cfg['credentials']['email']) tworker_token = json.loads( self.tworker.get('instance/service-accounts/default/token')) uworker_token = json.loads( self.uworker.get('instance/service-accounts/default/token')) - assert tworker_token['access_token'] == 'fake-tworker-access-token' - assert uworker_token['access_token'] == 'fake-uworker-access-token' + assert tworker_token['access_token'] == ( + self.tworker_cfg['credentials']['access_token']) + assert uworker_token['access_token'] == ( + self.uworker_cfg['credentials']['access_token']) def test_get_reads_from_the_trusted_emulator(self): """Verifies that compute_metadata.get() is answered by the tworker emulator, the in-process identity, and returns tworker.json's values.""" assert compute_metadata.get('instance/zone') == ( - 'projects/1234567890/zones/us-central1-f') + self.tworker_cfg['metadata']['instance']['zone']) assert compute_metadata.get('instance/id') == self.tworker.get( 'instance/id') @@ -121,18 +129,23 @@ def test_unknown_attribute_is_a_404(self): def test_instance_attributes_fall_back_to_project_attributes(self): """Verifies that querying instance/attributes/ falls back to project/attributes/ when not set on the instance.""" assert compute_metadata.get('instance/attributes/deployment-bucket') == ( - 'test-deployment-bucket') + self.tworker_cfg['metadata']['project']['attributes'][ + 'deployment-bucket']) assert compute_metadata.get('project/attributes/deployment-bucket') == ( - 'test-deployment-bucket') + self.tworker_cfg['metadata']['project']['attributes'][ + 'deployment-bucket']) def test_get_reads_project_and_instance_attributes(self): """Verifies that compute_metadata.get() reads both project/attributes/ and instance/attributes/.""" assert compute_metadata.get('project/attributes/deployment-bucket') == ( - 'test-deployment-bucket') + self.tworker_cfg['metadata']['project']['attributes'][ + 'deployment-bucket']) assert compute_metadata.get('project/attributes/deployment-zip') == ( - 'linux-3.zip') + self.tworker_cfg['metadata']['project']['attributes']['deployment-zip']) assert compute_metadata.get( - 'instance/attributes/override_tworker_queue') == 'jobs-linux-test' + 'instance/attributes/override_tworker_queue') == ( + self.tworker_cfg['metadata']['instance']['attributes'][ + 'override_tworker_queue']) def test_metadata_flavor_header_is_required(self): """Verifies that the emulator rejects requests missing the 'Metadata-Flavor: Google' header with HTTP 403 Forbidden.""" @@ -162,6 +175,7 @@ def _emulator(cls): gce_metadata_emulator.bootstrap() importlib.reload(compute_engine._metadata) # pylint: disable=protected-access importlib.reload(compute_metadata) + cls.uworker_cfg = gce_metadata_emulator.load_config('uworker') with gce_metadata_emulator.untrusted_as_default() as uworker: cls.uworker = uworker yield @@ -174,10 +188,10 @@ def test_default_credentials_come_from_the_untrusted_emulator(self): creds.refresh(google_auth_requests.Request()) assert isinstance(creds, compute_engine.Credentials) - assert project_id == 'test-clusterfuzz' + assert project_id == self.uworker_cfg['metadata']['project']['project-id'] assert creds.service_account_email == ( - 'test-unpriv-clusterfuzz-service-account-email') - assert creds.token == 'fake-uworker-access-token' + self.uworker_cfg['credentials']['email']) + assert creds.token == self.uworker_cfg['credentials']['access_token'] def test_clusterfuzz_credentials_wrapper_resolves_to_the_untrusted_identity( self): @@ -189,12 +203,13 @@ def test_clusterfuzz_credentials_wrapper_resolves_to_the_untrusted_identity( creds.refresh(google_auth_requests.Request()) assert creds.service_account_email == ( - 'test-unpriv-clusterfuzz-service-account-email') + self.uworker_cfg['credentials']['email']) def test_get_reads_from_the_untrusted_emulator(self): """Verifies that compute_metadata.get() is answered by uworker, whose instance/id differs from tworker's.""" - assert compute_metadata.get('instance/id') == '2222222222222222222' + assert compute_metadata.get('instance/id') == ( + self.uworker_cfg['metadata']['instance']['id']) assert compute_metadata.get('instance/id') == self.uworker.get( 'instance/id') assert compute_metadata.get('instance/hostname') == ( - 'uworker-1.c.test-clusterfuzz.internal') + self.uworker_cfg['metadata']['instance']['hostname']) From 040618de04ce45b7563a1bc5ece5e8a61bfa822a Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 30 Sep 2026 18:52:33 +0000 Subject: [PATCH 5/8] [Better tests] Move test to dir for the new test suite --- src/local/butler/py_unittest.py | 4 ++-- .../fixtures/gce_metadata_server_test.py | 24 +++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) rename src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py => test_suites/fixtures/gce_metadata_server_test.py (91%) diff --git a/src/local/butler/py_unittest.py b/src/local/butler/py_unittest.py index 1b4ad4cb968..43b48668920 100644 --- a/src/local/butler/py_unittest.py +++ b/src/local/butler/py_unittest.py @@ -285,8 +285,8 @@ def execute(args): # TODO(b/555371391) Move this initialization to the new integration test suite if os.getenv('GCE_METADATA_EMULATOR'): - from clusterfuzz._internal.tests.test_libs import gce_metadata_emulator - gce_metadata_emulator.bootstrap() + from test_suites.fixtures import gce_metadata_server + gce_metadata_server.bootstrap() if args.parallel: run_tests_parallel(args, test_directory, top_level_dir) diff --git a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py b/test_suites/fixtures/gce_metadata_server_test.py similarity index 91% rename from src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py rename to test_suites/fixtures/gce_metadata_server_test.py index 5d49f5ab911..4e499f1db31 100644 --- a/src/clusterfuzz/_internal/tests/core/google_cloud_utils/gce_metadata_emulator_test.py +++ b/test_suites/fixtures/gce_metadata_server_test.py @@ -27,12 +27,9 @@ from clusterfuzz._internal.google_cloud_utils import compute_metadata from clusterfuzz._internal.google_cloud_utils import credentials -from clusterfuzz._internal.tests.test_libs import gce_metadata_emulator +from test_suites.fixtures import gce_metadata_server -# TODO(b/555371391) Move this to the new integration test suite -@pytest.mark.skip( - reason='Experimental: pending migration to the new integration test suite.') class TestGceMetadataEmulator: """Integration tests for the fake GCE metadata server.""" @@ -41,12 +38,12 @@ class TestGceMetadataEmulator: @classmethod def _emulators(cls): """Starts the tworker/uworker pair and loads the configs they serve.""" - gce_metadata_emulator.bootstrap() + gce_metadata_server.bootstrap() importlib.reload(compute_engine._metadata) # pylint: disable=protected-access importlib.reload(compute_metadata) - cls.tworker_cfg = gce_metadata_emulator.load_config('tworker') - cls.uworker_cfg = gce_metadata_emulator.load_config('uworker') - with gce_metadata_emulator.trusted_untrusted_pair() as (tworker, uworker): + cls.tworker_cfg = gce_metadata_server.load_config('tworker') + cls.uworker_cfg = gce_metadata_server.load_config('uworker') + with gce_metadata_server.trusted_untrusted_pair() as (tworker, uworker): cls.tworker = tworker cls.uworker = uworker yield @@ -158,13 +155,10 @@ def test_metadata_flavor_header_is_required(self): def test_a_second_emulator_cannot_take_a_held_address(self): """Verifies that opening an emulator on an address another one already holds fails.""" with pytest.raises(WireMockContainerException): - with gce_metadata_emulator.untrusted_as_default(): + with gce_metadata_server.untrusted_as_default(): pass -# TODO(b/555371391) Move this to the new integration test suite -@pytest.mark.skip( - reason='Experimental: pending migration to the new integration test suite.') class TestGceMetadataEmulatorUntrustedDefault: """Integration tests for a test class that runs as the untrusted worker.""" @@ -172,11 +166,11 @@ class TestGceMetadataEmulatorUntrustedDefault: @pytest.fixture(scope='class', autouse=True) @classmethod def _emulator(cls): - gce_metadata_emulator.bootstrap() + gce_metadata_server.bootstrap() importlib.reload(compute_engine._metadata) # pylint: disable=protected-access importlib.reload(compute_metadata) - cls.uworker_cfg = gce_metadata_emulator.load_config('uworker') - with gce_metadata_emulator.untrusted_as_default() as uworker: + cls.uworker_cfg = gce_metadata_server.load_config('uworker') + with gce_metadata_server.untrusted_as_default() as uworker: cls.uworker = uworker yield From 2d8dba54fc811bf5b72058edc407629dd1938b68 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 30 Sep 2026 19:13:37 +0000 Subject: [PATCH 6/8] [Better tests] Format docstrings in metadata emulator test for line length --- .../fixtures/gce_metadata_server_test.py | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/test_suites/fixtures/gce_metadata_server_test.py b/test_suites/fixtures/gce_metadata_server_test.py index 4e499f1db31..e8956fc7302 100644 --- a/test_suites/fixtures/gce_metadata_server_test.py +++ b/test_suites/fixtures/gce_metadata_server_test.py @@ -39,7 +39,8 @@ class TestGceMetadataEmulator: def _emulators(cls): """Starts the tworker/uworker pair and loads the configs they serve.""" gce_metadata_server.bootstrap() - importlib.reload(compute_engine._metadata) # pylint: disable=protected-access + # pylint: disable=protected-access + importlib.reload(compute_engine._metadata) importlib.reload(compute_metadata) cls.tworker_cfg = gce_metadata_server.load_config('tworker') cls.uworker_cfg = gce_metadata_server.load_config('uworker') @@ -49,7 +50,8 @@ def _emulators(cls): yield def test_default_credentials_come_from_the_trusted_emulator(self): - """Verifies that google.auth.default() resolves Compute Engine credentials from the tworker emulator, not uworker.""" + """Verifies that google.auth.default() resolves Compute Engine credentials + from the tworker emulator, not uworker.""" assert self.tworker.hostport == os.environ['GCE_METADATA_HOST'] assert self.uworker.hostport != os.environ['GCE_METADATA_HOST'] @@ -59,7 +61,8 @@ def test_default_credentials_come_from_the_trusted_emulator(self): assert project_id == self.tworker_cfg['metadata']['project']['project-id'] def test_refreshing_credentials_yields_the_configured_token(self): - """Verifies that refreshing Compute Engine credentials returns the token and email from tworker.json.""" + """Verifies that refreshing Compute Engine credentials returns the token and + email from tworker.json.""" creds = compute_engine.Credentials() assert creds.token is None assert creds.service_account_email == 'default' @@ -71,7 +74,8 @@ def test_refreshing_credentials_yields_the_configured_token(self): self.tworker_cfg['credentials']['email']) def test_clusterfuzz_credentials_wrapper_resolves_from_the_emulator(self): - """Verifies that clusterfuzz's own credentials.get_default() wrapper, not just raw google.auth, lands on the emulator's service account.""" + """Verifies that clusterfuzz's own credentials.get_default() wrapper, not + just raw google.auth, lands on the emulator's service account.""" # get_default() is memoized process-wide, so force a miss rather than # depend on whichever test class ran first. creds, _ = credentials.get_default(__memoize_force__=True) @@ -82,7 +86,8 @@ def test_clusterfuzz_credentials_wrapper_resolves_from_the_emulator(self): self.tworker_cfg['credentials']['email']) def test_trusted_and_untrusted_emulators_serve_distinct_identities(self): - """Verifies that tworker and uworker run on separate ports and serve their respective service account emails and tokens.""" + """Verifies that tworker and uworker run on separate ports and serve their + respective service account emails and tokens.""" assert self.tworker.hostport != self.uworker.hostport for emulator in (self.tworker, self.uworker): assert re.match(r'^127\.0\.0\.1:\d+$', emulator.hostport) @@ -102,7 +107,8 @@ def test_trusted_and_untrusted_emulators_serve_distinct_identities(self): self.uworker_cfg['credentials']['access_token']) def test_get_reads_from_the_trusted_emulator(self): - """Verifies that compute_metadata.get() is answered by the tworker emulator, the in-process identity, and returns tworker.json's values.""" + """Verifies that compute_metadata.get() is answered by the tworker emulator, + the in-process identity, and returns tworker.json's values.""" assert compute_metadata.get('instance/zone') == ( self.tworker_cfg['metadata']['instance']['zone']) @@ -112,19 +118,23 @@ def test_get_reads_from_the_trusted_emulator(self): 'instance/id') def test_is_gce_connects_to_the_running_emulator(self): - """Verifies that compute_metadata.is_gce() opens a real connection to the emulator's ephemeral port and finds it listening.""" - assert compute_metadata._METADATA_SERVER == self.tworker.hostport # pylint: disable=protected-access + """Verifies that compute_metadata.is_gce() opens a real connection to the + emulator's ephemeral port and finds it listening.""" + # pylint: disable=protected-access + assert compute_metadata._METADATA_SERVER == self.tworker.hostport assert compute_metadata.is_gce() def test_unknown_attribute_is_a_404(self): - """Verifies that a key absent from both instance and project attributes is a 404.""" + """Verifies that a key absent from both instance and project attributes is a + 404.""" with pytest.raises(requests.exceptions.HTTPError) as caught: self.tworker.get('instance/attributes/does-not-exist') assert caught.value.response.status_code == 404 def test_instance_attributes_fall_back_to_project_attributes(self): - """Verifies that querying instance/attributes/ falls back to project/attributes/ when not set on the instance.""" + """Verifies that querying instance/attributes/ falls back to + project/attributes/ when not set on the instance.""" assert compute_metadata.get('instance/attributes/deployment-bucket') == ( self.tworker_cfg['metadata']['project']['attributes'][ 'deployment-bucket']) @@ -133,7 +143,8 @@ def test_instance_attributes_fall_back_to_project_attributes(self): 'deployment-bucket']) def test_get_reads_project_and_instance_attributes(self): - """Verifies that compute_metadata.get() reads both project/attributes/ and instance/attributes/.""" + """Verifies that compute_metadata.get() reads both project/attributes/ + and instance/attributes/.""" assert compute_metadata.get('project/attributes/deployment-bucket') == ( self.tworker_cfg['metadata']['project']['attributes'][ 'deployment-bucket']) @@ -145,7 +156,8 @@ def test_get_reads_project_and_instance_attributes(self): 'override_tworker_queue']) def test_metadata_flavor_header_is_required(self): - """Verifies that the emulator rejects requests missing the 'Metadata-Flavor: Google' header with HTTP 403 Forbidden.""" + """Verifies that the emulator rejects requests missing the 'Metadata-Flavor: + Google' header with HTTP 403 Forbidden.""" response = self.tworker.session.get( f'{self.tworker.url}/computeMetadata/v1/instance/zone', timeout=10) @@ -153,12 +165,14 @@ def test_metadata_flavor_header_is_required(self): assert response.text == '' def test_a_second_emulator_cannot_take_a_held_address(self): - """Verifies that opening an emulator on an address another one already holds fails.""" + """Verifies that opening an emulator on an address another one already holds + fails.""" with pytest.raises(WireMockContainerException): with gce_metadata_server.untrusted_as_default(): pass + class TestGceMetadataEmulatorUntrustedDefault: """Integration tests for a test class that runs as the untrusted worker.""" @@ -166,8 +180,10 @@ class TestGceMetadataEmulatorUntrustedDefault: @pytest.fixture(scope='class', autouse=True) @classmethod def _emulator(cls): + """Starts the uworker emulator and loads the config it serves.""" gce_metadata_server.bootstrap() - importlib.reload(compute_engine._metadata) # pylint: disable=protected-access + # pylint: disable=protected-access + importlib.reload(compute_engine._metadata) importlib.reload(compute_metadata) cls.uworker_cfg = gce_metadata_server.load_config('uworker') with gce_metadata_server.untrusted_as_default() as uworker: @@ -175,7 +191,8 @@ def _emulator(cls): yield def test_default_credentials_come_from_the_untrusted_emulator(self): - """Verifies that google.auth.default() resolves the uworker identity when uworker holds the default address.""" + """Verifies that google.auth.default() resolves the uworker identity when + uworker holds the default address.""" assert self.uworker.hostport == os.environ['GCE_METADATA_HOST'] creds, project_id = google.auth.default() @@ -189,7 +206,8 @@ def test_default_credentials_come_from_the_untrusted_emulator(self): def test_clusterfuzz_credentials_wrapper_resolves_to_the_untrusted_identity( self): - """Verifies that clusterfuzz's credentials.get_default() wrapper also follows the default address to uworker.""" + """Verifies that credentials.get_default() follows the default address to + uworker.""" # get_default() is memoized process-wide, so a tworker result cached by an # earlier test class would otherwise be handed back here, and refreshing it # would ask uworker for a service account it does not serve. @@ -200,7 +218,8 @@ def test_clusterfuzz_credentials_wrapper_resolves_to_the_untrusted_identity( self.uworker_cfg['credentials']['email']) def test_get_reads_from_the_untrusted_emulator(self): - """Verifies that compute_metadata.get() is answered by uworker, whose instance/id differs from tworker's.""" + """Verifies that compute_metadata.get() is answered by uworker, whose + instance/id differs from tworker's.""" assert compute_metadata.get('instance/id') == ( self.uworker_cfg['metadata']['instance']['id']) assert compute_metadata.get('instance/id') == self.uworker.get( From d4a77b37f6ffe3ac33be33469e95db44176834c3 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 30 Sep 2026 19:14:24 +0000 Subject: [PATCH 7/8] [Better tests] Format docstrings in metadata emulator test for line length --- test_suites/fixtures/gce_metadata_server_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test_suites/fixtures/gce_metadata_server_test.py b/test_suites/fixtures/gce_metadata_server_test.py index e8956fc7302..959e90c86e6 100644 --- a/test_suites/fixtures/gce_metadata_server_test.py +++ b/test_suites/fixtures/gce_metadata_server_test.py @@ -172,7 +172,6 @@ def test_a_second_emulator_cannot_take_a_held_address(self): pass - class TestGceMetadataEmulatorUntrustedDefault: """Integration tests for a test class that runs as the untrusted worker.""" From d92f6b36f345c551a24851ea1fa779962765fd78 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Tue, 6 Oct 2026 18:10:44 +0000 Subject: [PATCH 8/8] [Better tests] Makes pydoc string in 404 test more clear --- test_suites/fixtures/gce_metadata_server_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_suites/fixtures/gce_metadata_server_test.py b/test_suites/fixtures/gce_metadata_server_test.py index 959e90c86e6..8f320291370 100644 --- a/test_suites/fixtures/gce_metadata_server_test.py +++ b/test_suites/fixtures/gce_metadata_server_test.py @@ -125,8 +125,7 @@ def test_is_gce_connects_to_the_running_emulator(self): assert compute_metadata.is_gce() def test_unknown_attribute_is_a_404(self): - """Verifies that a key absent from both instance and project attributes is a - 404.""" + """Verifies that a key absent throws 404.""" with pytest.raises(requests.exceptions.HTTPError) as caught: self.tworker.get('instance/attributes/does-not-exist')