Skip to content

Commit 911561c

Browse files
tests(spanner): update prerelease_deps nox session to match system tests (#16764)
This PR fixes 2 failing tests which appear in #16762 In the system test presubmit `Kokoro System Tests`, the assertion is too strict. Protobuf objects may fail equality (==) if one has extra metadata populated by the server that the other lacks. If the server fills in a default field in one response but not another, the assertion fails even if the ID is identical. In addition, we need to cater for the chance that other spanner instances are created while tests are running. For example, in [one test result](https://btx.cloud.google.com/invocations/946e7000-b177-491a-947a-88f2daf52a68/log) we expected instance `projects/precise-truck-742/instances/google-cloud-python-systest` but found `projects/precise-truck-742/instances/multi-region-1776903230037` ``` =================================== FAILURES =================================== _____________________________ test_list_instances ______________________________ .. def test_list_instances( no_create_instance, spanner_client, existing_instances, shared_instance, ): instances = list(spanner_client.list_instances()) for instance in instances: > assert instance in existing_instances or instance is shared_instance ... tests/system/test_instance_api.py:40: AssertionError ``` The other failure only appears in the `Kokoro Pre-release Tests`. Updating the `prerelease_deps` nox session to match the system test session resolves the issue. ``` ==================================== ERRORS ==================================== ____________________ ERROR at setup of test_table_not_found ____________________ self = def setup(self) -> None: runner_fixture_id = f"_{self._loop_scope}_scoped_runner" if runner_fixture_id not in self.fixturenames: self.fixturenames.append(runner_fixture_id) > return super().setup() ^^^^^^^^^^^^^^^ .nox/8f8a4a6e/lib/python3.14/site-packages/pytest_asyncio/plugin.py:458: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ fixturedef = request = > @pytest.hookimpl(wrapper=True) def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: asyncio_mode = _get_asyncio_mode(request.config) if not _is_asyncio_fixture_function(fixturedef.func): if asyncio_mode == Mode.STRICT: # Ignore async fixtures without explicit asyncio mark in strict mode # This applies to pytest_trio fixtures, for example > return (yield) ^^^^^ E pytest.PytestRemovedIn9Warning: '' requested an async fixture 'instance_configs', with no plugin or hook that handled it. This is usually an error, as pytest does not natively support it. This will turn into an error in pytest 9. E See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture .nox/8f8a4a6e/lib/python3.14/site-packages/pytest_asyncio/plugin.py:728: PytestRemovedIn9Warning ``` --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 93ed424 commit 911561c

2 files changed

Lines changed: 90 additions & 20 deletions

File tree

packages/google-cloud-spanner/noxfile.py

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,12 @@ def prerelease_deps(session, protobuf_implementation, database_dialect):
655655
system_test_path = os.path.join("tests", "system.py")
656656
system_test_folder_path = os.path.join("tests", "system")
657657

658+
system_test_exists = os.path.exists(system_test_path)
659+
system_test_folder_exists = os.path.exists(system_test_folder_path)
660+
# Sanity check: only run tests if found.
661+
if not system_test_exists and not system_test_folder_exists:
662+
session.skip("System tests were not found")
663+
658664
if os.environ.get("SPANNER_EMULATOR_HOST"):
659665
# Run tests against the emulator
660666
run_system = True
@@ -675,24 +681,77 @@ def prerelease_deps(session, protobuf_implementation, database_dialect):
675681
run_system = False
676682

677683
if run_system:
678-
# Run the tests (deduplicated logic)
679-
test_path = (
680-
system_test_path
681-
if os.path.exists(system_test_path)
682-
else system_test_folder_path
683-
)
684-
session.run(
685-
"py.test",
686-
"--verbose",
687-
f"--junitxml=system_{session.python}_sponge_log.xml",
688-
test_path,
689-
*session.posargs,
690-
env={
691-
"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
692-
"SPANNER_DATABASE_DIALECT": database_dialect,
693-
"SKIP_BACKUP_TESTS": "true",
694-
},
695-
)
684+
# Run py.test against the system tests.
685+
if system_test_exists:
686+
args = [
687+
"py.test",
688+
"--quiet",
689+
"-o",
690+
"asyncio_mode=auto",
691+
f"--junitxml=system_{session.python}_sponge_log.xml",
692+
]
693+
if not session.posargs:
694+
args.append(system_test_path)
695+
args.extend(session.posargs)
696+
697+
session.run(
698+
*args,
699+
env={
700+
"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
701+
"SPANNER_DATABASE_DIALECT": database_dialect,
702+
"SKIP_BACKUP_TESTS": "true",
703+
},
704+
)
705+
elif system_test_folder_exists:
706+
# Run sync tests
707+
sync_args = [
708+
"py.test",
709+
"--quiet",
710+
"-o",
711+
"asyncio_mode=auto",
712+
f"--junitxml=system_{session.python}_sync_sponge_log.xml",
713+
]
714+
if not session.posargs:
715+
sync_args.append(system_test_folder_path)
716+
sync_args.append(
717+
f"--ignore={os.path.join(system_test_folder_path, '_async')}"
718+
)
719+
else:
720+
sync_args.extend(session.posargs)
721+
722+
session.run(
723+
*sync_args,
724+
env={
725+
"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
726+
"SPANNER_DATABASE_DIALECT": database_dialect,
727+
"SKIP_BACKUP_TESTS": "true",
728+
},
729+
)
730+
731+
# Run async tests
732+
async_args = [
733+
"py.test",
734+
"--quiet",
735+
"-o",
736+
"asyncio_mode=auto",
737+
f"--junitxml=system_{session.python}_async_sponge_log.xml",
738+
]
739+
if not session.posargs:
740+
async_args.append(os.path.join(system_test_folder_path, "_async"))
741+
else:
742+
# If posargs are provided, only run if they match async tests
743+
# or just skip if they were already run in sync.
744+
# For simplicity, we only run async folder if no posargs.
745+
return
746+
747+
session.run(
748+
*async_args,
749+
env={
750+
"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
751+
"SPANNER_DATABASE_DIALECT": database_dialect,
752+
"SKIP_BACKUP_TESTS": "true",
753+
},
754+
)
696755

697756

698757
@nox.session(python=ALL_PYTHON)

packages/google-cloud-spanner/tests/system/test_instance_api.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,21 @@ def test_list_instances(
3434
existing_instances,
3535
shared_instance,
3636
):
37+
# Retrieve all instances currently visible in the project.
3738
instances = list(spanner_client.list_instances())
3839

39-
for instance in instances:
40-
assert instance in existing_instances or instance is shared_instance
40+
# We use the instance name (the full resource path) rather than the object itself.
41+
# Protobuf objects may fail equality (==) if one has extra metadata populated
42+
# by the server (like 'edition' or 'backup_schedules') that the other lacks.
43+
instance_names = {instance.name for instance in instances}
44+
45+
# In a parallel CI environment, other tests may create instances
46+
# (like 'multi-region-...') simultaneously.
47+
# Only verify `shared_instance.name` is in the list of instances.
48+
assert shared_instance.name in instance_names, (
49+
f"Expected instance {shared_instance.name} was not found in the "
50+
f"list of project instances: {instance_names}"
51+
)
4152

4253

4354
def test_reload_instance(spanner_client, shared_instance_id, shared_instance):

0 commit comments

Comments
 (0)