Skip to content

Commit 7b95e3f

Browse files
Tighten view-collision tests
- Drop parametrization over (memory, sql, sql_without_rowcount): all three share the same SqlCatalog code path, and the helper itself is covered by unit tests in `tests/catalog/test_base.py`. Use a single InMemoryCatalog via a new `single_catalog` fixture. - Replace `lambda` patches with a recording fake so the tests assert *which* identifier was checked — matters for `rename_table`, where the destination (not the source) must be passed. - Add post-condition assertions: after the raise, the target table must not exist (proves the check ran before any commit-side effect), and for rename, the source must still be present at its original identifier.
1 parent 401c582 commit 7b95e3f

1 file changed

Lines changed: 65 additions & 41 deletions

File tree

tests/catalog/test_catalog_behaviors.py

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -461,75 +461,99 @@ def test_rename_table_to_missing_namespace(
461461
catalog.rename_table(test_table_identifier, another_table_identifier)
462462

463463

464-
# View-collision tests
465-
#
466-
# InMemoryCatalog and SqlCatalog don't support views (`view_exists` raises
467-
# `NotImplementedError`), so we monkeypatch it to assert the call sites invoke
468-
# the helper that maps a view at the target identifier to `TableAlreadyExistsError`.
464+
@pytest.fixture(name="single_catalog")
465+
def fixture_single_catalog(tmp_path: Path) -> Catalog:
466+
from pyiceberg.catalog.memory import InMemoryCatalog
467+
from pyiceberg.io import WAREHOUSE
468+
469+
return InMemoryCatalog("view_collision_tests", **{WAREHOUSE: tmp_path.absolute().as_posix()})
470+
471+
472+
def _recording_view_exists(target: Identifier) -> tuple[list[Identifier], Any]:
473+
"""Build a `view_exists` fake that records each call and returns True for `target`."""
474+
calls: list[Identifier] = []
475+
476+
def fake(identifier: str | Identifier) -> bool:
477+
seen = Catalog.identifier_to_tuple(identifier)
478+
calls.append(seen)
479+
return seen == target
480+
481+
return calls, fake
469482

470483

471484
def test_create_table_raises_when_view_exists_at_identifier(
472-
catalog: Catalog,
473-
test_table_identifier: Identifier,
485+
single_catalog: Catalog,
474486
table_schema_simple: Schema,
475487
monkeypatch: pytest.MonkeyPatch,
476488
) -> None:
477-
namespace = Catalog.namespace_from(test_table_identifier)
478-
catalog.create_namespace(namespace)
479-
monkeypatch.setattr(
480-
catalog, "view_exists", lambda identifier: Catalog.identifier_to_tuple(identifier) == test_table_identifier
481-
)
489+
identifier: Identifier = ("ns", "t")
490+
single_catalog.create_namespace(identifier[:-1])
491+
calls, fake = _recording_view_exists(identifier)
492+
monkeypatch.setattr(single_catalog, "view_exists", fake)
493+
482494
with pytest.raises(TableAlreadyExistsError, match="View with same name already exists"):
483-
catalog.create_table(test_table_identifier, table_schema_simple)
495+
single_catalog.create_table(identifier, table_schema_simple)
496+
497+
assert identifier in calls
498+
assert not single_catalog.table_exists(identifier)
484499

485500

486501
def test_create_table_transaction_raises_when_view_exists_at_identifier(
487-
catalog: Catalog,
488-
test_table_identifier: Identifier,
502+
single_catalog: Catalog,
489503
table_schema_simple: Schema,
490504
monkeypatch: pytest.MonkeyPatch,
491505
) -> None:
492-
namespace = Catalog.namespace_from(test_table_identifier)
493-
catalog.create_namespace(namespace)
494-
monkeypatch.setattr(
495-
catalog, "view_exists", lambda identifier: Catalog.identifier_to_tuple(identifier) == test_table_identifier
496-
)
506+
identifier: Identifier = ("ns", "t")
507+
single_catalog.create_namespace(identifier[:-1])
508+
calls, fake = _recording_view_exists(identifier)
509+
monkeypatch.setattr(single_catalog, "view_exists", fake)
510+
497511
with pytest.raises(TableAlreadyExistsError, match="View with same name already exists"):
498-
catalog.create_table_transaction(test_table_identifier, table_schema_simple)
512+
single_catalog.create_table_transaction(identifier, table_schema_simple)
513+
514+
assert identifier in calls
515+
assert not single_catalog.table_exists(identifier)
499516

500517

501518
def test_register_table_raises_when_view_exists_at_identifier(
502-
catalog: Catalog,
503-
test_table_identifier: Identifier,
519+
single_catalog: Catalog,
504520
metadata_location: str,
505521
monkeypatch: pytest.MonkeyPatch,
506522
) -> None:
507-
namespace = Catalog.namespace_from(test_table_identifier)
508-
catalog.create_namespace(namespace)
509-
monkeypatch.setattr(
510-
catalog, "view_exists", lambda identifier: Catalog.identifier_to_tuple(identifier) == test_table_identifier
511-
)
523+
identifier: Identifier = ("ns", "t")
524+
single_catalog.create_namespace(identifier[:-1])
525+
calls, fake = _recording_view_exists(identifier)
526+
monkeypatch.setattr(single_catalog, "view_exists", fake)
527+
512528
with pytest.raises(TableAlreadyExistsError, match="View with same name already exists"):
513-
catalog.register_table(test_table_identifier, metadata_location)
529+
single_catalog.register_table(identifier, metadata_location)
530+
531+
assert identifier in calls
532+
assert not single_catalog.table_exists(identifier)
514533

515534

516535
def test_rename_table_raises_when_view_exists_at_destination(
517-
catalog: Catalog,
536+
single_catalog: Catalog,
518537
table_schema_simple: Schema,
519-
test_table_identifier: Identifier,
520-
another_table_identifier: Identifier,
521538
monkeypatch: pytest.MonkeyPatch,
522539
) -> None:
523-
from_namespace = Catalog.namespace_from(test_table_identifier)
524-
to_namespace = Catalog.namespace_from(another_table_identifier)
525-
catalog.create_namespace(from_namespace)
526-
catalog.create_namespace(to_namespace)
527-
catalog.create_table(test_table_identifier, table_schema_simple)
528-
monkeypatch.setattr(
529-
catalog, "view_exists", lambda identifier: Catalog.identifier_to_tuple(identifier) == another_table_identifier
530-
)
540+
source: Identifier = ("ns", "src")
541+
destination: Identifier = ("ns", "dst")
542+
single_catalog.create_namespace(("ns",))
543+
single_catalog.create_table(source, table_schema_simple)
544+
545+
calls, fake = _recording_view_exists(destination)
546+
monkeypatch.setattr(single_catalog, "view_exists", fake)
547+
531548
with pytest.raises(TableAlreadyExistsError, match="View with same name already exists"):
532-
catalog.rename_table(test_table_identifier, another_table_identifier)
549+
single_catalog.rename_table(source, destination)
550+
551+
# The check must be on the destination, not the source.
552+
assert destination in calls
553+
assert source not in calls
554+
# Source table must still exist — the rename was rejected before any mutation.
555+
assert single_catalog.table_exists(source)
556+
assert not single_catalog.table_exists(destination)
533557

534558

535559
# Drop table tests

0 commit comments

Comments
 (0)