Update shapes tests for pk abstraction, strict Diff, and diff_many
13 new tests covering three changes from claude.ai: - pk abstraction: _pk_field resolved from model._meta, _get_pk helper - Strict Diff.__getattr__: typos raise AttributeError with valid names, nested() method raises KeyError for explicit access - diff_many: batched query (assertNumQueries(1)), mixed new/existing, empty list, all-new, nonexistent raises 38 shapes tests total, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,18 @@ class ShapeMetaTests(TestCase):
|
||||
self.assertIsNotNone(BookShape._pair)
|
||||
self.assertEqual(len(BookShape._pair), 2) # (prepare, project)
|
||||
|
||||
def test_pk_field_resolved_from_model_meta(self):
|
||||
self.assertEqual(BookShape._pk_field, "id")
|
||||
self.assertEqual(AuthorShape._pk_field, "id")
|
||||
|
||||
def test_get_pk_reads_correct_field(self):
|
||||
shape = BookShape(id=42, title="Test", pages=1)
|
||||
self.assertEqual(BookShape._get_pk(shape), 42)
|
||||
|
||||
def test_get_pk_returns_none_for_new(self):
|
||||
shape = BookShape(title="New", pages=1)
|
||||
self.assertIsNone(BookShape._get_pk(shape))
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Querying
|
||||
@@ -273,13 +285,135 @@ class DiffNestedTests(TestCase):
|
||||
self.assertEqual(len(diff.books.updated), 1)
|
||||
self.assertEqual(diff.books.deleted, [self.book2.id])
|
||||
|
||||
def test_accessing_nonexistent_nested_returns_empty(self):
|
||||
def test_accessing_nonexistent_nested_raises_attribute_error(self):
|
||||
shape = BookShape(title="Simple", pages=10)
|
||||
diff = shape.diff()
|
||||
|
||||
# BookShape has no nested relations
|
||||
empty = diff.nonexistent_relation
|
||||
self.assertIsInstance(empty, NestedDiff)
|
||||
self.assertEqual(empty.created, [])
|
||||
self.assertEqual(empty.updated, [])
|
||||
self.assertEqual(empty.deleted, [])
|
||||
with self.assertRaises(AttributeError) as ctx:
|
||||
diff.nonexistent_relation
|
||||
|
||||
self.assertIn("nonexistent_relation", str(ctx.exception))
|
||||
|
||||
def test_nested_method_raises_key_error(self):
|
||||
shape = BookShape(title="Simple", pages=10)
|
||||
diff = shape.diff()
|
||||
|
||||
with self.assertRaises(KeyError) as ctx:
|
||||
diff.nested("nonexistent")
|
||||
|
||||
self.assertIn("nonexistent", str(ctx.exception))
|
||||
|
||||
def test_nested_method_returns_valid_nested_diff(self):
|
||||
shape = AuthorShape(
|
||||
id=self.author.id,
|
||||
name="Alice",
|
||||
bio="Writer",
|
||||
books=[
|
||||
BookShape(id=self.book1.id, title="Book One", pages=100),
|
||||
],
|
||||
)
|
||||
diff = shape.diff()
|
||||
books_diff = diff.nested("books")
|
||||
|
||||
self.assertIsInstance(books_diff, NestedDiff)
|
||||
self.assertEqual(diff.books.deleted, [self.book2.id])
|
||||
|
||||
def test_diff_error_message_lists_valid_names(self):
|
||||
shape = AuthorShape(
|
||||
id=self.author.id,
|
||||
name="Alice",
|
||||
bio="Writer",
|
||||
books=[],
|
||||
)
|
||||
diff = shape.diff()
|
||||
|
||||
with self.assertRaises(AttributeError) as ctx:
|
||||
diff.typo
|
||||
|
||||
self.assertIn("books", str(ctx.exception))
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# diff_many
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class DiffManyTests(TestCase):
|
||||
"""diff_many batches queries instead of N+1."""
|
||||
|
||||
def setUp(self):
|
||||
self.author = Author.objects.create(name="Alice", bio="Writer")
|
||||
self.book1 = Book.objects.create(title="Book One", pages=100, author=self.author)
|
||||
self.book2 = Book.objects.create(title="Book Two", pages=200, author=self.author)
|
||||
self.book3 = Book.objects.create(title="Book Three", pages=300, author=self.author)
|
||||
|
||||
def test_diff_many_no_changes(self):
|
||||
items = [
|
||||
BookShape(id=self.book1.id, title="Book One", pages=100),
|
||||
BookShape(id=self.book2.id, title="Book Two", pages=200),
|
||||
]
|
||||
results = BookShape.diff_many(items)
|
||||
|
||||
self.assertEqual(len(results), 2)
|
||||
for item, diff in results:
|
||||
self.assertFalse(diff.is_new)
|
||||
self.assertEqual(diff.changed, {})
|
||||
|
||||
def test_diff_many_with_changes(self):
|
||||
items = [
|
||||
BookShape(id=self.book1.id, title="Renamed", pages=100),
|
||||
BookShape(id=self.book2.id, title="Book Two", pages=999),
|
||||
]
|
||||
results = BookShape.diff_many(items)
|
||||
|
||||
diffs = {item.id: diff for item, diff in results}
|
||||
self.assertEqual(diffs[self.book1.id].changed, {"title": "Renamed"})
|
||||
self.assertEqual(diffs[self.book2.id].changed, {"pages": 999})
|
||||
|
||||
def test_diff_many_with_new_items(self):
|
||||
items = [
|
||||
BookShape(title="Brand New", pages=50),
|
||||
BookShape(id=self.book1.id, title="Book One", pages=100),
|
||||
]
|
||||
results = BookShape.diff_many(items)
|
||||
|
||||
self.assertEqual(len(results), 2)
|
||||
new_diffs = [(item, diff) for item, diff in results if diff.is_new]
|
||||
existing_diffs = [(item, diff) for item, diff in results if not diff.is_new]
|
||||
|
||||
self.assertEqual(len(new_diffs), 1)
|
||||
self.assertEqual(new_diffs[0][0].title, "Brand New")
|
||||
self.assertEqual(len(existing_diffs), 1)
|
||||
|
||||
def test_diff_many_nonexistent_raises(self):
|
||||
items = [
|
||||
BookShape(id=99999, title="Ghost", pages=0),
|
||||
]
|
||||
with self.assertRaises(Book.DoesNotExist):
|
||||
BookShape.diff_many(items)
|
||||
|
||||
def test_diff_many_single_query(self):
|
||||
"""diff_many should use one query for all existing items, not N queries."""
|
||||
items = [
|
||||
BookShape(id=self.book1.id, title="A", pages=1),
|
||||
BookShape(id=self.book2.id, title="B", pages=2),
|
||||
BookShape(id=self.book3.id, title="C", pages=3),
|
||||
]
|
||||
|
||||
with self.assertNumQueries(1):
|
||||
BookShape.diff_many(items)
|
||||
|
||||
def test_diff_many_empty_list(self):
|
||||
results = BookShape.diff_many([])
|
||||
self.assertEqual(results, [])
|
||||
|
||||
def test_diff_many_all_new(self):
|
||||
items = [
|
||||
BookShape(title="New A", pages=10),
|
||||
BookShape(title="New B", pages=20),
|
||||
]
|
||||
results = BookShape.diff_many(items)
|
||||
|
||||
self.assertEqual(len(results), 2)
|
||||
for item, diff in results:
|
||||
self.assertTrue(diff.is_new)
|
||||
|
||||
Reference in New Issue
Block a user