""" Stress tests for djarea.shapes — edge cases and deep nesting. Models: Publisher → Author → Book → Chapter → Section (5 levels deep), two FKs to same model, slug PK, UUID PK, self-referential FK, M2M, nullable FKs, abstract bases, empty/zero/false values. """ import pytest from typing import get_type_hints from django.test import TestCase from djarea.shapes import Shape, Diff, NestedDiff import uuid from tests.models import ( Publisher, Author, Book, Chapter, Section, Tag, Category, ) # ============================================================================= # Shapes — varying projections # ============================================================================= class TagShape(Shape[Tag]): slug: str label: str class FlatAuthorShape(Shape[Author]): id: int | None = None name: str class FlatBookShape(Shape[Book]): id: int | None = None title: str is_published: bool class BookCardShape(Shape[Book]): id: int | None = None title: str isbn: str page_count: int is_published: bool author: FlatAuthorShape # single nested, not list class AuthorCardShape(Shape[Author]): id: int | None = None name: str bio: str books: list[FlatBookShape] = [] class SectionShape(Shape[Section]): id: uuid.UUID | None = None heading: str body: str position: int class ChapterShape(Shape[Chapter]): id: int | None = None number: int title: str word_count: int sections: list[SectionShape] = [] class BookDetailShape(Shape[Book]): id: int | None = None title: str isbn: str page_count: int is_published: bool author: FlatAuthorShape chapters: list[ChapterShape] = [] tags: list[TagShape] = [] class AuthorDetailShape(Shape[Author]): id: int | None = None name: str bio: str books: list[BookDetailShape] = [] class PublisherDetailShape(Shape[Publisher]): id: int | None = None name: str country: str authors: list[AuthorDetailShape] = [] class BookWithEditorShape(Shape[Book]): """Two FKs to the same model (author + editor).""" id: int | None = None title: str author: FlatAuthorShape editor: FlatAuthorShape | None = None # CategoryShape is commented out — self-referential forward refs crash # get_type_hints() at __init_subclass__ time. Known gap. # # class CategoryShape(Shape[Category]): # id: int | None = None # name: str # children: list["CategoryShape"] = [] # ============================================================================= # Shape class creation # ============================================================================= class TestShapeClassCreation(TestCase): def test_flat_shape_has_no_nested(self): self.assertEqual(FlatAuthorShape._nested, {}) self.assertEqual(FlatAuthorShape._field_names, ["id", "name"]) def test_nested_shape_detected(self): self.assertIn("books", AuthorCardShape._nested) self.assertIs(AuthorCardShape._nested["books"], FlatBookShape) def test_deep_nesting_spec_depth(self): """PublisherDetailShape → Author → Book → Chapter → Section.""" nested_keys = { k for d in PublisherDetailShape._spec if isinstance(d, dict) for k in d } self.assertIn("authors", nested_keys) author_spec = next( d["authors"] for d in PublisherDetailShape._spec if isinstance(d, dict) and "authors" in d ) author_nested = {k for d in author_spec if isinstance(d, dict) for k in d} self.assertIn("books", author_nested) def test_pk_field_resolution_integer(self): self.assertEqual(FlatAuthorShape._pk_field, "id") def test_pk_field_resolution_slug(self): self.assertEqual(TagShape._pk_field, "slug") def test_pk_field_resolution_uuid(self): self.assertEqual(SectionShape._pk_field, "id") def test_single_nested_not_list(self): self.assertIn("author", BookCardShape._nested) self.assertIs(BookCardShape._nested["author"], FlatAuthorShape) def test_optional_nested(self): """BookWithEditorShape.editor is FlatAuthorShape | None. _extract_shape_class needs to handle Optional/Union.""" # If this doesn't detect editor as nested, it's a known gap if "editor" in BookWithEditorShape._nested: self.assertIs(BookWithEditorShape._nested["editor"], FlatAuthorShape) else: self.skipTest( "_extract_shape_class does not unwrap Optional[Shape] — known gap" ) def test_self_referential_shape(self): """CategoryShape.children references itself. Currently crashes at class definition time — known gap.""" self.skipTest( "Self-referential forward ref crashes get_type_hints() at __init_subclass__ time" ) def test_multiple_shapes_same_model_independent(self): self.assertLess(len(FlatBookShape._field_names), len(BookDetailShape._field_names)) self.assertNotEqual(FlatBookShape._spec, BookDetailShape._spec) # ============================================================================= # Queries # ============================================================================= class TestShapeQuery(TestCase): @classmethod def setUpTestData(cls): cls.publisher = Publisher.objects.create(name="Orbit", country="UK") cls.mentor = Author.objects.create( name="Ursula", bio="Legend", publisher=cls.publisher ) cls.author = Author.objects.create( name="Ann Leckie", bio="Imperial Radch", publisher=cls.publisher, mentor=cls.mentor, ) cls.editor = Author.objects.create( name="Devi Pillai", bio="Editor", publisher=cls.publisher ) cls.tag_sf = Tag.objects.create(slug="sci-fi", label="Science Fiction") cls.tag_space = Tag.objects.create(slug="space-opera", label="Space Opera") cls.book = Book.objects.create( title="Ancillary Justice", isbn="9780316246620", page_count=386, is_published=True, author=cls.author, editor=cls.editor, ) cls.book.tags.add(cls.tag_sf, cls.tag_space) cls.ch1 = Chapter.objects.create( book=cls.book, number=1, title="The Body", word_count=5200 ) cls.ch2 = Chapter.objects.create( book=cls.book, number=2, title="The Ship", word_count=4800 ) Section.objects.create(chapter=cls.ch1, heading="Opening", body="...", position=0) Section.objects.create(chapter=cls.ch1, heading="Discovery", body="...", position=1) cls.root_cat = Category.objects.create(name="Fiction") cls.child_cat = Category.objects.create(name="Sci-Fi", parent=cls.root_cat) Category.objects.create(name="Hard SF", parent=cls.child_cat) # ── Flat ── def test_flat_query_returns_minimal_fields(self): results = FlatAuthorShape.query() self.assertEqual(len(results), 3) for r in results: self.assertTrue(hasattr(r, "name")) self.assertTrue(hasattr(r, "id")) def test_flat_query_with_lambda_filter(self): results = FlatAuthorShape.query(lambda qs: qs.filter(name="Ann Leckie")) self.assertEqual(len(results), 1) self.assertEqual(results[0].name, "Ann Leckie") def test_flat_query_with_raw_queryset(self): qs = Author.objects.filter(mentor__isnull=False) results = FlatAuthorShape.query(qs) self.assertEqual(len(results), 1) self.assertEqual(results[0].name, "Ann Leckie") # ── Nested ── def test_single_nested_fk(self): results = BookCardShape.query(lambda qs: qs.filter(pk=self.book.pk)) self.assertEqual(len(results), 1) self.assertEqual(results[0].author.name, "Ann Leckie") def test_list_nested_reverse_fk(self): results = AuthorCardShape.query(lambda qs: qs.filter(pk=self.author.pk)) self.assertEqual(len(results), 1) self.assertEqual(len(results[0].books), 1) self.assertEqual(results[0].books[0].title, "Ancillary Justice") def test_deep_nesting_book_chapters_sections(self): results = BookDetailShape.query(lambda qs: qs.filter(pk=self.book.pk)) self.assertEqual(len(results), 1) book = results[0] self.assertEqual(len(book.chapters), 2) ch1 = next(c for c in book.chapters if c.number == 1) self.assertEqual(len(ch1.sections), 2) def test_full_depth_publisher_to_section(self): """5 levels: Publisher → Author → Book → Chapter → Section.""" results = PublisherDetailShape.query(lambda qs: qs.filter(pk=self.publisher.pk)) self.assertEqual(len(results), 1) pub = results[0] self.assertEqual(len(pub.authors), 3) leckie = next(a for a in pub.authors if a.name == "Ann Leckie") self.assertEqual(len(leckie.books), 1) self.assertEqual(len(leckie.books[0].chapters), 2) def test_two_fks_to_same_model(self): results = BookWithEditorShape.query(lambda qs: qs.filter(pk=self.book.pk)) self.assertEqual(len(results), 1) self.assertEqual(results[0].author.name, "Ann Leckie") if "editor" in BookWithEditorShape._nested: self.assertIsNotNone(results[0].editor) self.assertEqual(results[0].editor.name, "Devi Pillai") def test_nullable_fk_returns_none(self): book_no_editor = Book.objects.create( title="Provenance", isbn="9780316246699", page_count=448, is_published=True, author=self.author, editor=None, ) results = BookWithEditorShape.query(lambda qs: qs.filter(pk=book_no_editor.pk)) self.assertEqual(len(results), 1) if "editor" in BookWithEditorShape._nested: self.assertIsNone(results[0].editor) def test_m2m_tags(self): results = BookDetailShape.query(lambda qs: qs.filter(pk=self.book.pk)) book = results[0] self.assertEqual(len(book.tags), 2) slugs = {t.slug for t in book.tags} self.assertEqual(slugs, {"sci-fi", "space-opera"}) def test_slug_pk_shape(self): results = TagShape.query() self.assertEqual(len(results), 2) self.assertTrue(all(isinstance(r.slug, str) for r in results)) def test_relation_qs_filters_nested(self): results = AuthorCardShape.query( lambda qs: qs.filter(pk=self.author.pk), books=lambda qs: qs.filter(is_published=True), ) self.assertEqual(len(results), 1) self.assertTrue(all(b.is_published for b in results[0].books)) def test_empty_nested_list(self): results = AuthorCardShape.query(lambda qs: qs.filter(pk=self.editor.pk)) self.assertEqual(len(results), 1) self.assertEqual(results[0].books, []) # ── Query efficiency ── def test_flat_query_is_single_query(self): with self.assertNumQueries(1): FlatAuthorShape.query() def test_nested_query_uses_prefetch(self): with self.assertNumQueries(2): AuthorCardShape.query() # ============================================================================= # Diff # ============================================================================= class TestDiff(TestCase): @classmethod def setUpTestData(cls): cls.publisher = Publisher.objects.create(name="Tor", country="US") cls.author = Author.objects.create( name="Brandon Sanderson", bio="Cosmere", publisher=cls.publisher ) cls.book = Book.objects.create( title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=cls.author, ) cls.ch1 = Chapter.objects.create( book=cls.book, number=1, title="Ash", word_count=6000 ) cls.ch2 = Chapter.objects.create( book=cls.book, number=2, title="Mist", word_count=5500 ) # ── Single item ── def test_diff_no_changes(self): shape = BookCardShape( id=self.book.pk, title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), ) d = shape.diff() self.assertFalse(d.is_new) self.assertEqual(d.changed, {}) def test_diff_detects_field_change(self): shape = BookCardShape( id=self.book.pk, title="Mistborn: The Final Empire", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), ) d = shape.diff() self.assertIn("title", d.changed) self.assertEqual(d.changed["title"], "Mistborn: The Final Empire") def test_diff_multiple_field_changes(self): shape = BookCardShape( id=self.book.pk, title="Mistborn: TFE", isbn="9780765311788", page_count=600, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), ) d = shape.diff() self.assertIn("title", d.changed) self.assertIn("page_count", d.changed) self.assertNotIn("isbn", d.changed) def test_diff_new_item(self): shape = FlatBookShape(id=None, title="Elantris", is_published=True) d = shape.diff() self.assertTrue(d.is_new) self.assertIn("title", d.changed) def test_diff_nonexistent_pk_raises(self): shape = FlatBookShape(id=999999, title="Nope", is_published=False) with self.assertRaises(Book.DoesNotExist): shape.diff() # ── Nested ── def test_nested_diff_detects_updated_chapter(self): shape = BookDetailShape( id=self.book.pk, title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), chapters=[ ChapterShape(id=self.ch1.pk, number=1, title="Ash Falls", word_count=6000, sections=[]), ChapterShape(id=self.ch2.pk, number=2, title="Mist", word_count=5500, sections=[]), ], tags=[], ) d = shape.diff() self.assertEqual(len(d.chapters.updated), 1) self.assertEqual(d.chapters.updated[0].title, "Ash Falls") def test_nested_diff_detects_created(self): shape = BookDetailShape( id=self.book.pk, title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), chapters=[ ChapterShape(id=self.ch1.pk, number=1, title="Ash", word_count=6000, sections=[]), ChapterShape(id=self.ch2.pk, number=2, title="Mist", word_count=5500, sections=[]), ChapterShape(id=None, number=3, title="New Chapter", word_count=0, sections=[]), ], tags=[], ) d = shape.diff() self.assertEqual(len(d.chapters.created), 1) def test_nested_diff_detects_deleted(self): shape = BookDetailShape( id=self.book.pk, title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), chapters=[ ChapterShape(id=self.ch1.pk, number=1, title="Ash", word_count=6000, sections=[]), ], tags=[], ) d = shape.diff() self.assertIn(self.ch2.pk, d.chapters.deleted) def test_nested_diff_combined_operations(self): shape = BookDetailShape( id=self.book.pk, title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), chapters=[ ChapterShape(id=self.ch1.pk, number=1, title="Ash Rewritten", word_count=7000, sections=[]), ChapterShape(id=None, number=3, title="Epilogue", word_count=2000, sections=[]), ], tags=[], ) d = shape.diff() self.assertEqual(len(d.chapters.updated), 1) self.assertEqual(len(d.chapters.deleted), 1) self.assertEqual(len(d.chapters.created), 1) # ── Strict Diff access ── def test_diff_strict_getattr_raises_on_typo(self): shape = FlatBookShape(id=self.book.pk, title="Mistborn", is_published=True) d = shape.diff() with self.assertRaises(AttributeError): _ = d.chapterz def test_diff_strict_nested_raises_on_typo(self): shape = FlatBookShape(id=self.book.pk, title="Mistborn", is_published=True) d = shape.diff() with self.assertRaises(KeyError): d.nested("chapterz") def test_diff_strict_shows_valid_names(self): shape = BookDetailShape( id=self.book.pk, title="Mistborn", isbn="9780765311788", page_count=541, is_published=True, author=FlatAuthorShape(id=self.author.pk, name="Brandon Sanderson"), chapters=[], tags=[], ) d = shape.diff() with self.assertRaises(AttributeError) as ctx: _ = d.bogus self.assertIn("chapters", str(ctx.exception)) # ── diff_many ── def test_diff_many_single_query_for_existing(self): items = [FlatBookShape(id=self.book.pk, title="Renamed", is_published=True)] results = FlatBookShape.diff_many(items) self.assertEqual(len(results), 1) _, d = results[0] self.assertIn("title", d.changed) def test_diff_many_mixed_new_and_existing(self): items = [ FlatBookShape(id=self.book.pk, title="Mistborn", is_published=True), FlatBookShape(id=None, title="New Book", is_published=False), ] results = FlatBookShape.diff_many(items) new = [d for _, d in results if d.is_new] existing = [d for _, d in results if not d.is_new] self.assertEqual(len(new), 1) self.assertEqual(len(existing), 1) def test_diff_many_nonexistent_raises(self): items = [FlatBookShape(id=999999, title="Ghost", is_published=False)] with self.assertRaises(Book.DoesNotExist): FlatBookShape.diff_many(items) def test_diff_many_batched_query(self): book2 = Book.objects.create( title="Warbreaker", isbn="9780765320308", page_count=592, is_published=True, author=self.author, ) items = [ FlatBookShape(id=self.book.pk, title="Mistborn", is_published=True), FlatBookShape(id=book2.pk, title="Warbreaker Updated", is_published=True), ] with self.assertNumQueries(1): FlatBookShape.diff_many(items) def test_diff_many_empty(self): self.assertEqual(FlatBookShape.diff_many([]), []) # ============================================================================= # Edge cases # ============================================================================= class TestEdgeCases(TestCase): @classmethod def setUpTestData(cls): cls.publisher = Publisher.objects.create(name="Edge Cases Ltd", country="XX") cls.author = Author.objects.create( name="Edge Author", bio="", publisher=cls.publisher ) def test_empty_table_returns_empty_list(self): Tag.objects.all().delete() results = TagShape.query() self.assertEqual(results, []) def test_empty_string_fields(self): results = AuthorCardShape.query(lambda qs: qs.filter(pk=self.author.pk)) self.assertEqual(results[0].bio, "") def test_boolean_false_is_not_missing(self): book = Book.objects.create( title="Unpublished", isbn="0000000000000", page_count=0, is_published=False, author=self.author, ) results = FlatBookShape.query(lambda qs: qs.filter(pk=book.pk)) self.assertIs(results[0].is_published, False) def test_zero_integer_is_not_missing(self): book = Book.objects.create( title="Empty", isbn="0000000000001", page_count=0, is_published=False, author=self.author, ) results = BookCardShape.query(lambda qs: qs.filter(pk=book.pk)) self.assertEqual(results[0].page_count, 0) def test_large_queryset(self): books = [ Book( title=f"Book {i}", isbn=f"{i:013d}", page_count=i * 10, is_published=i % 2 == 0, author=self.author, ) for i in range(100) ] Book.objects.bulk_create(books) results = FlatBookShape.query(lambda qs: qs.filter(author=self.author)) self.assertGreaterEqual(len(results), 100) def test_diff_on_boolean_change(self): book = Book.objects.create( title="Toggle", isbn="1111111111111", page_count=100, is_published=False, author=self.author, ) shape = FlatBookShape(id=book.pk, title="Toggle", is_published=True) d = shape.diff() self.assertIn("is_published", d.changed) self.assertIs(d.changed["is_published"], True) def test_diff_unchanged_returns_empty(self): book = Book.objects.create( title="Same", isbn="2222222222222", page_count=200, is_published=True, author=self.author, ) shape = FlatBookShape(id=book.pk, title="Same", is_published=True) d = shape.diff() self.assertEqual(d.changed, {}) self.assertFalse(d.is_new)