mirror of
https://github.com/daniviga/django-ram.git
synced 2026-02-03 17:40:39 +01:00
Add AGENTS.md and tests generated via opencode
This commit is contained in:
@@ -1,3 +1,436 @@
|
||||
from decimal import Decimal
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError
|
||||
|
||||
# Create your tests here.
|
||||
from bookshelf.models import (
|
||||
Author,
|
||||
Publisher,
|
||||
Book,
|
||||
Catalog,
|
||||
Magazine,
|
||||
MagazineIssue,
|
||||
TocEntry,
|
||||
)
|
||||
from metadata.models import Manufacturer, Scale
|
||||
|
||||
|
||||
class AuthorTestCase(TestCase):
|
||||
"""Test cases for Author model."""
|
||||
|
||||
def test_author_creation(self):
|
||||
"""Test creating an author."""
|
||||
author = Author.objects.create(
|
||||
first_name="John",
|
||||
last_name="Smith",
|
||||
)
|
||||
|
||||
self.assertEqual(str(author), "Smith, John")
|
||||
self.assertEqual(author.first_name, "John")
|
||||
self.assertEqual(author.last_name, "Smith")
|
||||
|
||||
def test_author_short_name(self):
|
||||
"""Test author short name property."""
|
||||
author = Author.objects.create(
|
||||
first_name="John",
|
||||
last_name="Smith",
|
||||
)
|
||||
|
||||
self.assertEqual(author.short_name, "Smith J.")
|
||||
|
||||
def test_author_ordering(self):
|
||||
"""Test author ordering by last name, first name."""
|
||||
a1 = Author.objects.create(first_name="John", last_name="Smith")
|
||||
a2 = Author.objects.create(first_name="Jane", last_name="Doe")
|
||||
a3 = Author.objects.create(first_name="Bob", last_name="Smith")
|
||||
|
||||
authors = list(Author.objects.all())
|
||||
self.assertEqual(authors[0], a2) # Doe comes first
|
||||
self.assertEqual(authors[1], a3) # Smith, Bob
|
||||
self.assertEqual(authors[2], a1) # Smith, John
|
||||
|
||||
|
||||
class PublisherTestCase(TestCase):
|
||||
"""Test cases for Publisher model."""
|
||||
|
||||
def test_publisher_creation(self):
|
||||
"""Test creating a publisher."""
|
||||
publisher = Publisher.objects.create(
|
||||
name="Model Railroader",
|
||||
country="US",
|
||||
website="https://www.modelrailroader.com",
|
||||
)
|
||||
|
||||
self.assertEqual(str(publisher), "Model Railroader")
|
||||
self.assertEqual(publisher.country.code, "US")
|
||||
|
||||
def test_publisher_ordering(self):
|
||||
"""Test publisher ordering by name."""
|
||||
p1 = Publisher.objects.create(name="Zebra Publishing")
|
||||
p2 = Publisher.objects.create(name="Alpha Books")
|
||||
p3 = Publisher.objects.create(name="Model Railroader")
|
||||
|
||||
publishers = list(Publisher.objects.all())
|
||||
self.assertEqual(publishers[0], p2)
|
||||
self.assertEqual(publishers[1], p3)
|
||||
self.assertEqual(publishers[2], p1)
|
||||
|
||||
|
||||
class BookTestCase(TestCase):
|
||||
"""Test cases for Book model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.publisher = Publisher.objects.create(
|
||||
name="Kalmbach Publishing",
|
||||
country="US",
|
||||
)
|
||||
|
||||
self.author = Author.objects.create(
|
||||
first_name="Tony",
|
||||
last_name="Koester",
|
||||
)
|
||||
|
||||
def test_book_creation(self):
|
||||
"""Test creating a book."""
|
||||
book = Book.objects.create(
|
||||
title="Model Railroad Planning",
|
||||
publisher=self.publisher,
|
||||
ISBN="978-0-89024-567-8",
|
||||
language="en",
|
||||
number_of_pages=128,
|
||||
publication_year=2010,
|
||||
price=Decimal("24.95"),
|
||||
)
|
||||
|
||||
self.assertEqual(str(book), "Model Railroad Planning")
|
||||
self.assertEqual(book.publisher_name, "Kalmbach Publishing")
|
||||
self.assertTrue(book.published) # Default from BaseModel
|
||||
|
||||
def test_book_authors_relationship(self):
|
||||
"""Test many-to-many relationship with authors."""
|
||||
book = Book.objects.create(
|
||||
title="Test Book",
|
||||
publisher=self.publisher,
|
||||
)
|
||||
|
||||
author2 = Author.objects.create(
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
|
||||
book.authors.add(self.author, author2)
|
||||
|
||||
self.assertEqual(book.authors.count(), 2)
|
||||
self.assertIn(self.author, book.authors.all())
|
||||
|
||||
def test_book_authors_list_property(self):
|
||||
"""Test authors_list property."""
|
||||
book = Book.objects.create(
|
||||
title="Test Book",
|
||||
publisher=self.publisher,
|
||||
)
|
||||
|
||||
book.authors.add(self.author)
|
||||
|
||||
self.assertEqual(book.authors_list, "Koester T.")
|
||||
|
||||
def test_book_ordering(self):
|
||||
"""Test book ordering by title."""
|
||||
b1 = Book.objects.create(
|
||||
title="Zebra Book",
|
||||
publisher=self.publisher,
|
||||
)
|
||||
b2 = Book.objects.create(
|
||||
title="Alpha Book",
|
||||
publisher=self.publisher,
|
||||
)
|
||||
|
||||
books = list(Book.objects.all())
|
||||
self.assertEqual(books[0], b2)
|
||||
self.assertEqual(books[1], b1)
|
||||
|
||||
|
||||
class CatalogTestCase(TestCase):
|
||||
"""Test cases for Catalog model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.manufacturer = Manufacturer.objects.create(
|
||||
name="Bachmann",
|
||||
category="model",
|
||||
country="US",
|
||||
)
|
||||
|
||||
self.scale_ho = Scale.objects.create(
|
||||
scale="HO",
|
||||
ratio="1:87",
|
||||
tracks=16.5,
|
||||
)
|
||||
|
||||
self.scale_n = Scale.objects.create(
|
||||
scale="N",
|
||||
ratio="1:160",
|
||||
tracks=9.0,
|
||||
)
|
||||
|
||||
def test_catalog_creation(self):
|
||||
"""Test creating a catalog."""
|
||||
catalog = Catalog.objects.create(
|
||||
manufacturer=self.manufacturer,
|
||||
years="2023",
|
||||
publication_year=2023,
|
||||
)
|
||||
catalog.scales.add(self.scale_ho)
|
||||
|
||||
# Refresh to get the correct string representation
|
||||
catalog.refresh_from_db()
|
||||
|
||||
self.assertIn("Bachmann", str(catalog))
|
||||
self.assertIn("2023", str(catalog))
|
||||
|
||||
def test_catalog_multiple_scales(self):
|
||||
"""Test catalog with multiple scales."""
|
||||
catalog = Catalog.objects.create(
|
||||
manufacturer=self.manufacturer,
|
||||
years="2023",
|
||||
)
|
||||
|
||||
catalog.scales.add(self.scale_ho, self.scale_n)
|
||||
|
||||
scales_str = catalog.get_scales()
|
||||
self.assertIn("HO", scales_str)
|
||||
self.assertIn("N", scales_str)
|
||||
|
||||
def test_catalog_ordering(self):
|
||||
"""Test catalog ordering by manufacturer and year."""
|
||||
man2 = Manufacturer.objects.create(
|
||||
name="Atlas",
|
||||
category="model",
|
||||
)
|
||||
|
||||
c1 = Catalog.objects.create(
|
||||
manufacturer=self.manufacturer,
|
||||
years="2023",
|
||||
publication_year=2023,
|
||||
)
|
||||
c2 = Catalog.objects.create(
|
||||
manufacturer=man2,
|
||||
years="2023",
|
||||
publication_year=2023,
|
||||
)
|
||||
c3 = Catalog.objects.create(
|
||||
manufacturer=self.manufacturer,
|
||||
years="2022",
|
||||
publication_year=2022,
|
||||
)
|
||||
|
||||
catalogs = list(Catalog.objects.all())
|
||||
# Should be ordered by manufacturer name, then year
|
||||
self.assertEqual(catalogs[0], c2) # Atlas
|
||||
|
||||
|
||||
class MagazineTestCase(TestCase):
|
||||
"""Test cases for Magazine model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.publisher = Publisher.objects.create(
|
||||
name="Kalmbach Publishing",
|
||||
country="US",
|
||||
)
|
||||
|
||||
def test_magazine_creation(self):
|
||||
"""Test creating a magazine."""
|
||||
magazine = Magazine.objects.create(
|
||||
name="Model Railroader",
|
||||
publisher=self.publisher,
|
||||
website="https://www.modelrailroader.com",
|
||||
ISBN="0746-9896",
|
||||
language="en",
|
||||
)
|
||||
|
||||
self.assertEqual(str(magazine), "Model Railroader")
|
||||
self.assertEqual(magazine.publisher, self.publisher)
|
||||
|
||||
def test_magazine_website_short(self):
|
||||
"""Test website_short method."""
|
||||
magazine = Magazine.objects.create(
|
||||
name="Model Railroader",
|
||||
publisher=self.publisher,
|
||||
website="https://www.modelrailroader.com",
|
||||
)
|
||||
|
||||
self.assertEqual(magazine.website_short(), "modelrailroader.com")
|
||||
|
||||
def test_magazine_get_cover_no_image(self):
|
||||
"""Test get_cover when magazine has no image."""
|
||||
magazine = Magazine.objects.create(
|
||||
name="Test Magazine",
|
||||
publisher=self.publisher,
|
||||
)
|
||||
|
||||
# Should return None if no cover image exists
|
||||
self.assertIsNone(magazine.get_cover())
|
||||
|
||||
|
||||
class MagazineIssueTestCase(TestCase):
|
||||
"""Test cases for MagazineIssue model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.publisher = Publisher.objects.create(
|
||||
name="Kalmbach Publishing",
|
||||
)
|
||||
|
||||
self.magazine = Magazine.objects.create(
|
||||
name="Model Railroader",
|
||||
publisher=self.publisher,
|
||||
published=True,
|
||||
)
|
||||
|
||||
def test_magazine_issue_creation(self):
|
||||
"""Test creating a magazine issue."""
|
||||
issue = MagazineIssue.objects.create(
|
||||
magazine=self.magazine,
|
||||
issue_number="January 2023",
|
||||
publication_year=2023,
|
||||
publication_month=1,
|
||||
number_of_pages=96,
|
||||
)
|
||||
|
||||
self.assertEqual(str(issue), "Model Railroader - January 2023")
|
||||
self.assertEqual(issue.obj_label, "Magazine Issue")
|
||||
|
||||
def test_magazine_issue_unique_together(self):
|
||||
"""Test that magazine+issue_number must be unique."""
|
||||
MagazineIssue.objects.create(
|
||||
magazine=self.magazine,
|
||||
issue_number="January 2023",
|
||||
)
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
MagazineIssue.objects.create(
|
||||
magazine=self.magazine,
|
||||
issue_number="January 2023",
|
||||
)
|
||||
|
||||
def test_magazine_issue_validation(self):
|
||||
"""Test that published issue requires published magazine."""
|
||||
unpublished_magazine = Magazine.objects.create(
|
||||
name="Unpublished Magazine",
|
||||
publisher=self.publisher,
|
||||
published=False,
|
||||
)
|
||||
|
||||
issue = MagazineIssue(
|
||||
magazine=unpublished_magazine,
|
||||
issue_number="Test Issue",
|
||||
published=True,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
issue.clean()
|
||||
|
||||
def test_magazine_issue_publisher_property(self):
|
||||
"""Test that issue inherits publisher from magazine."""
|
||||
issue = MagazineIssue.objects.create(
|
||||
magazine=self.magazine,
|
||||
issue_number="January 2023",
|
||||
)
|
||||
|
||||
self.assertEqual(issue.publisher, self.publisher)
|
||||
|
||||
|
||||
class TocEntryTestCase(TestCase):
|
||||
"""Test cases for TocEntry model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
publisher = Publisher.objects.create(name="Test Publisher")
|
||||
|
||||
self.book = Book.objects.create(
|
||||
title="Test Book",
|
||||
publisher=publisher,
|
||||
number_of_pages=200,
|
||||
)
|
||||
|
||||
def test_toc_entry_creation(self):
|
||||
"""Test creating a table of contents entry."""
|
||||
entry = TocEntry.objects.create(
|
||||
book=self.book,
|
||||
title="Introduction to Model Railroading",
|
||||
subtitle="Getting Started",
|
||||
authors="John Doe",
|
||||
page=10,
|
||||
)
|
||||
|
||||
self.assertIn("Introduction to Model Railroading", str(entry))
|
||||
self.assertIn("Getting Started", str(entry))
|
||||
self.assertIn("p. 10", str(entry))
|
||||
|
||||
def test_toc_entry_without_subtitle(self):
|
||||
"""Test TOC entry without subtitle."""
|
||||
entry = TocEntry.objects.create(
|
||||
book=self.book,
|
||||
title="Chapter One",
|
||||
page=5,
|
||||
)
|
||||
|
||||
self.assertEqual(str(entry), "Chapter One (p. 5)")
|
||||
|
||||
def test_toc_entry_page_validation_required(self):
|
||||
"""Test that page number is required."""
|
||||
entry = TocEntry(
|
||||
book=self.book,
|
||||
title="Test Entry",
|
||||
page=None,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
entry.clean()
|
||||
|
||||
def test_toc_entry_page_validation_min(self):
|
||||
"""Test that page number must be >= 1."""
|
||||
entry = TocEntry(
|
||||
book=self.book,
|
||||
title="Test Entry",
|
||||
page=0,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
entry.clean()
|
||||
|
||||
def test_toc_entry_page_validation_exceeds_book(self):
|
||||
"""Test that page number cannot exceed book's page count."""
|
||||
entry = TocEntry(
|
||||
book=self.book,
|
||||
title="Test Entry",
|
||||
page=250, # Book has 200 pages
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
entry.clean()
|
||||
|
||||
def test_toc_entry_ordering(self):
|
||||
"""Test TOC entries are ordered by page number."""
|
||||
e1 = TocEntry.objects.create(
|
||||
book=self.book,
|
||||
title="Chapter Three",
|
||||
page=30,
|
||||
)
|
||||
e2 = TocEntry.objects.create(
|
||||
book=self.book,
|
||||
title="Chapter One",
|
||||
page=10,
|
||||
)
|
||||
e3 = TocEntry.objects.create(
|
||||
book=self.book,
|
||||
title="Chapter Two",
|
||||
page=20,
|
||||
)
|
||||
|
||||
entries = list(TocEntry.objects.all())
|
||||
self.assertEqual(entries[0], e2) # Page 10
|
||||
self.assertEqual(entries[1], e3) # Page 20
|
||||
self.assertEqual(entries[2], e1) # Page 30
|
||||
|
||||
@@ -1,3 +1,315 @@
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError
|
||||
|
||||
# Create your tests here.
|
||||
from consist.models import Consist, ConsistItem
|
||||
from roster.models import RollingClass, RollingStock
|
||||
from metadata.models import Company, Scale, RollingStockType
|
||||
|
||||
|
||||
class ConsistTestCase(TestCase):
|
||||
"""Test cases for Consist model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.company = Company.objects.create(
|
||||
name="Rio Grande Southern",
|
||||
country="US",
|
||||
)
|
||||
|
||||
self.scale = Scale.objects.create(
|
||||
scale="HOn3",
|
||||
ratio="1:87",
|
||||
tracks=10.5,
|
||||
)
|
||||
|
||||
def test_consist_creation(self):
|
||||
"""Test creating a consist."""
|
||||
consist = Consist.objects.create(
|
||||
identifier="RGS Freight #1",
|
||||
company=self.company,
|
||||
scale=self.scale,
|
||||
era="1930s",
|
||||
)
|
||||
|
||||
self.assertEqual(str(consist), "Rio Grande Southern RGS Freight #1")
|
||||
self.assertEqual(consist.identifier, "RGS Freight #1")
|
||||
self.assertEqual(consist.era, "1930s")
|
||||
|
||||
def test_consist_country_property(self):
|
||||
"""Test that consist inherits country from company."""
|
||||
consist = Consist.objects.create(
|
||||
identifier="Test Consist",
|
||||
company=self.company,
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
self.assertEqual(consist.country, self.company.country)
|
||||
|
||||
def test_consist_dcc_address(self):
|
||||
"""Test consist with DCC address."""
|
||||
consist = Consist.objects.create(
|
||||
identifier="DCC Consist",
|
||||
company=self.company,
|
||||
scale=self.scale,
|
||||
consist_address=99,
|
||||
)
|
||||
|
||||
self.assertEqual(consist.consist_address, 99)
|
||||
|
||||
def test_consist_get_absolute_url(self):
|
||||
"""Test get_absolute_url returns correct URL."""
|
||||
consist = Consist.objects.create(
|
||||
identifier="Test Consist",
|
||||
company=self.company,
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
url = consist.get_absolute_url()
|
||||
self.assertIn(str(consist.uuid), url)
|
||||
|
||||
|
||||
class ConsistItemTestCase(TestCase):
|
||||
"""Test cases for ConsistItem model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.company = Company.objects.create(name="RGS", country="US")
|
||||
|
||||
self.scale_hon3 = Scale.objects.create(
|
||||
scale="HOn3",
|
||||
ratio="1:87",
|
||||
tracks=10.5,
|
||||
)
|
||||
|
||||
self.scale_ho = Scale.objects.create(
|
||||
scale="HO",
|
||||
ratio="1:87",
|
||||
tracks=16.5,
|
||||
)
|
||||
|
||||
self.stock_type = RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=1,
|
||||
)
|
||||
|
||||
self.rolling_class = RollingClass.objects.create(
|
||||
identifier="C-19",
|
||||
type=self.stock_type,
|
||||
company=self.company,
|
||||
)
|
||||
|
||||
self.consist = Consist.objects.create(
|
||||
identifier="Test Consist",
|
||||
company=self.company,
|
||||
scale=self.scale_hon3,
|
||||
published=True,
|
||||
)
|
||||
|
||||
self.rolling_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale_hon3,
|
||||
published=True,
|
||||
)
|
||||
|
||||
def test_consist_item_creation(self):
|
||||
"""Test creating a consist item."""
|
||||
item = ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=1,
|
||||
load=False,
|
||||
)
|
||||
|
||||
self.assertEqual(str(item), "RGS C-19 340")
|
||||
self.assertEqual(item.order, 1)
|
||||
self.assertFalse(item.load)
|
||||
|
||||
def test_consist_item_unique_constraint(self):
|
||||
"""Test that consist+rolling_stock must be unique."""
|
||||
ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=1,
|
||||
)
|
||||
|
||||
# Cannot add same rolling stock to same consist twice
|
||||
with self.assertRaises(IntegrityError):
|
||||
ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=2,
|
||||
)
|
||||
|
||||
def test_consist_item_scale_validation(self):
|
||||
"""Test that consist item scale must match consist scale."""
|
||||
different_scale_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="341",
|
||||
scale=self.scale_ho, # Different scale
|
||||
)
|
||||
|
||||
item = ConsistItem(
|
||||
consist=self.consist,
|
||||
rolling_stock=different_scale_stock,
|
||||
order=1,
|
||||
load=False,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
item.clean()
|
||||
|
||||
def test_consist_item_load_ratio_validation(self):
|
||||
"""Test that load ratio must match consist ratio."""
|
||||
different_scale = Scale.objects.create(
|
||||
scale="N",
|
||||
ratio="1:160", # Different ratio
|
||||
tracks=9.0,
|
||||
)
|
||||
|
||||
load_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="342",
|
||||
scale=different_scale,
|
||||
)
|
||||
|
||||
item = ConsistItem(
|
||||
consist=self.consist,
|
||||
rolling_stock=load_stock,
|
||||
order=1,
|
||||
load=True,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
item.clean()
|
||||
|
||||
def test_consist_item_published_validation(self):
|
||||
"""Test that unpublished stock cannot be in published consist."""
|
||||
unpublished_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="343",
|
||||
scale=self.scale_hon3,
|
||||
published=False,
|
||||
)
|
||||
|
||||
item = ConsistItem(
|
||||
consist=self.consist,
|
||||
rolling_stock=unpublished_stock,
|
||||
order=1,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
item.clean()
|
||||
|
||||
def test_consist_item_properties(self):
|
||||
"""Test consist item properties."""
|
||||
item = ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=1,
|
||||
)
|
||||
|
||||
self.assertEqual(item.scale, self.rolling_stock.scale)
|
||||
self.assertEqual(item.company, self.rolling_stock.company)
|
||||
self.assertEqual(item.type, self.stock_type.type)
|
||||
|
||||
def test_consist_length_calculation(self):
|
||||
"""Test consist length calculation."""
|
||||
# Add three items (not loads)
|
||||
for i in range(3):
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number=str(340 + i),
|
||||
scale=self.scale_hon3,
|
||||
)
|
||||
ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=stock,
|
||||
order=i + 1,
|
||||
load=False,
|
||||
)
|
||||
|
||||
self.assertEqual(self.consist.length, 3)
|
||||
|
||||
def test_consist_length_excludes_loads(self):
|
||||
"""Test that consist length excludes loads."""
|
||||
# Add one regular item
|
||||
ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=1,
|
||||
load=False,
|
||||
)
|
||||
|
||||
# Add one load (same ratio, different scale tracks OK for loads)
|
||||
load_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="LOAD-1",
|
||||
scale=self.scale_hon3,
|
||||
)
|
||||
ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=load_stock,
|
||||
order=2,
|
||||
load=True,
|
||||
)
|
||||
|
||||
# Length should only count non-load items
|
||||
self.assertEqual(self.consist.length, 1)
|
||||
|
||||
def test_consist_item_ordering(self):
|
||||
"""Test consist items are ordered by order field."""
|
||||
stock2 = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="341",
|
||||
scale=self.scale_hon3,
|
||||
)
|
||||
stock3 = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="342",
|
||||
scale=self.scale_hon3,
|
||||
)
|
||||
|
||||
item3 = ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=stock3,
|
||||
order=3,
|
||||
)
|
||||
item1 = ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=1,
|
||||
)
|
||||
item2 = ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=stock2,
|
||||
order=2,
|
||||
)
|
||||
|
||||
items = list(self.consist.consist_item.all())
|
||||
self.assertEqual(items[0], item1)
|
||||
self.assertEqual(items[1], item2)
|
||||
self.assertEqual(items[2], item3)
|
||||
|
||||
def test_unpublish_consist_signal(self):
|
||||
"""Test that unpublishing rolling stock unpublishes consists."""
|
||||
# Create a consist item
|
||||
ConsistItem.objects.create(
|
||||
consist=self.consist,
|
||||
rolling_stock=self.rolling_stock,
|
||||
order=1,
|
||||
)
|
||||
|
||||
self.assertTrue(self.consist.published)
|
||||
|
||||
# Unpublish the rolling stock
|
||||
self.rolling_stock.published = False
|
||||
self.rolling_stock.save()
|
||||
|
||||
# Reload consist from database
|
||||
self.consist.refresh_from_db()
|
||||
|
||||
# Consist should now be unpublished
|
||||
self.assertFalse(self.consist.published)
|
||||
|
||||
@@ -1,3 +1,371 @@
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError
|
||||
|
||||
# Create your tests here.
|
||||
from metadata.models import (
|
||||
Manufacturer,
|
||||
Company,
|
||||
Scale,
|
||||
RollingStockType,
|
||||
Decoder,
|
||||
Shop,
|
||||
Tag,
|
||||
calculate_ratio,
|
||||
)
|
||||
|
||||
|
||||
class ManufacturerTestCase(TestCase):
|
||||
"""Test cases for Manufacturer model."""
|
||||
|
||||
def test_manufacturer_creation(self):
|
||||
"""Test creating a manufacturer."""
|
||||
manufacturer = Manufacturer.objects.create(
|
||||
name="Blackstone Models",
|
||||
category="model",
|
||||
country="US",
|
||||
website="https://www.blackstonemodels.com",
|
||||
)
|
||||
|
||||
self.assertEqual(str(manufacturer), "Blackstone Models")
|
||||
self.assertEqual(manufacturer.slug, "blackstone-models")
|
||||
self.assertEqual(manufacturer.category, "model")
|
||||
|
||||
def test_manufacturer_slug_auto_generation(self):
|
||||
"""Test that slug is automatically generated."""
|
||||
manufacturer = Manufacturer.objects.create(
|
||||
name="Baldwin Locomotive Works",
|
||||
category="real",
|
||||
)
|
||||
|
||||
self.assertEqual(manufacturer.slug, "baldwin-locomotive-works")
|
||||
|
||||
def test_manufacturer_unique_constraint(self):
|
||||
"""Test that name+category must be unique."""
|
||||
Manufacturer.objects.create(
|
||||
name="Baldwin",
|
||||
category="real",
|
||||
)
|
||||
|
||||
# Should not be able to create another with same name+category
|
||||
with self.assertRaises(IntegrityError):
|
||||
Manufacturer.objects.create(
|
||||
name="Baldwin",
|
||||
category="real",
|
||||
)
|
||||
|
||||
def test_manufacturer_different_categories(self):
|
||||
"""Test that same name is allowed with different categories."""
|
||||
Manufacturer.objects.create(
|
||||
name="Baldwin",
|
||||
category="real",
|
||||
)
|
||||
|
||||
# Should be able to create with different category
|
||||
manufacturer2 = Manufacturer.objects.create(
|
||||
name="Alco",
|
||||
category="model",
|
||||
)
|
||||
|
||||
self.assertEqual(manufacturer2.name, "Alco")
|
||||
self.assertIsNotNone(manufacturer2.pk)
|
||||
|
||||
def test_manufacturer_website_short(self):
|
||||
"""Test website_short extracts domain."""
|
||||
manufacturer = Manufacturer.objects.create(
|
||||
name="Test Manufacturer",
|
||||
category="model",
|
||||
website="https://www.example.com/path",
|
||||
)
|
||||
|
||||
self.assertEqual(manufacturer.website_short(), "example.com")
|
||||
|
||||
def test_manufacturer_ordering(self):
|
||||
"""Test manufacturer ordering by category and slug."""
|
||||
m1 = Manufacturer.objects.create(name="Zebra", category="model")
|
||||
m2 = Manufacturer.objects.create(name="Alpha", category="accessory")
|
||||
m3 = Manufacturer.objects.create(name="Beta", category="model")
|
||||
|
||||
manufacturers = list(Manufacturer.objects.all())
|
||||
# Ordered by category, then slug
|
||||
self.assertEqual(manufacturers[0], m2) # accessory comes first
|
||||
self.assertTrue(manufacturers.index(m3) < manufacturers.index(m1))
|
||||
|
||||
|
||||
class CompanyTestCase(TestCase):
|
||||
"""Test cases for Company model."""
|
||||
|
||||
def test_company_creation(self):
|
||||
"""Test creating a company."""
|
||||
company = Company.objects.create(
|
||||
name="RGS",
|
||||
extended_name="Rio Grande Southern Railroad",
|
||||
country="US",
|
||||
freelance=False,
|
||||
)
|
||||
|
||||
self.assertEqual(str(company), "RGS")
|
||||
self.assertEqual(company.slug, "rgs")
|
||||
self.assertEqual(company.extended_name, "Rio Grande Southern Railroad")
|
||||
|
||||
def test_company_slug_generation(self):
|
||||
"""Test automatic slug generation."""
|
||||
company = Company.objects.create(
|
||||
name="Denver & Rio Grande Western",
|
||||
country="US",
|
||||
)
|
||||
|
||||
self.assertEqual(company.slug, "denver-rio-grande-western")
|
||||
|
||||
def test_company_unique_name(self):
|
||||
"""Test that company name must be unique."""
|
||||
Company.objects.create(name="RGS", country="US")
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
Company.objects.create(name="RGS", country="GB")
|
||||
|
||||
def test_company_extended_name_pp(self):
|
||||
"""Test extended name pretty print."""
|
||||
company = Company.objects.create(
|
||||
name="RGS",
|
||||
extended_name="Rio Grande Southern Railroad",
|
||||
country="US",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
company.extended_name_pp(),
|
||||
"(Rio Grande Southern Railroad)"
|
||||
)
|
||||
|
||||
def test_company_extended_name_pp_empty(self):
|
||||
"""Test extended name pretty print when empty."""
|
||||
company = Company.objects.create(name="RGS", country="US")
|
||||
|
||||
self.assertEqual(company.extended_name_pp(), "")
|
||||
|
||||
def test_company_freelance_flag(self):
|
||||
"""Test freelance flag."""
|
||||
company = Company.objects.create(
|
||||
name="Fake Railroad",
|
||||
country="US",
|
||||
freelance=True,
|
||||
)
|
||||
|
||||
self.assertTrue(company.freelance)
|
||||
|
||||
|
||||
class ScaleTestCase(TestCase):
|
||||
"""Test cases for Scale model."""
|
||||
|
||||
def test_scale_creation(self):
|
||||
"""Test creating a scale."""
|
||||
scale = Scale.objects.create(
|
||||
scale="HOn3",
|
||||
ratio="1:87",
|
||||
tracks=10.5,
|
||||
gauge="3 ft",
|
||||
)
|
||||
|
||||
self.assertEqual(str(scale), "HOn3")
|
||||
self.assertEqual(scale.slug, "hon3")
|
||||
self.assertEqual(scale.ratio, "1:87")
|
||||
self.assertEqual(scale.tracks, 10.5)
|
||||
|
||||
def test_scale_ratio_calculation(self):
|
||||
"""Test automatic ratio_int calculation."""
|
||||
scale = Scale.objects.create(
|
||||
scale="HO",
|
||||
ratio="1:87",
|
||||
tracks=16.5,
|
||||
)
|
||||
|
||||
# 1/87 * 10000 = 114.94...
|
||||
self.assertAlmostEqual(scale.ratio_int, 114, delta=1)
|
||||
|
||||
def test_scale_ratio_validation_valid(self):
|
||||
"""Test that valid ratios are accepted."""
|
||||
ratios = ["1:87", "1:160", "1:22.5", "1:48"]
|
||||
|
||||
for ratio in ratios:
|
||||
result = calculate_ratio(ratio)
|
||||
self.assertIsInstance(result, (int, float))
|
||||
|
||||
def test_scale_ratio_validation_invalid(self):
|
||||
"""Test that invalid ratios raise ValidationError."""
|
||||
with self.assertRaises(ValidationError):
|
||||
calculate_ratio("invalid")
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
calculate_ratio("1:0") # Division by zero
|
||||
|
||||
def test_scale_ordering(self):
|
||||
"""Test scale ordering by ratio_int (descending)."""
|
||||
s1 = Scale.objects.create(scale="G", ratio="1:22.5", tracks=45.0)
|
||||
s2 = Scale.objects.create(scale="HO", ratio="1:87", tracks=16.5)
|
||||
s3 = Scale.objects.create(scale="N", ratio="1:160", tracks=9.0)
|
||||
|
||||
scales = list(Scale.objects.all())
|
||||
# Ordered by -ratio_int (larger ratios first)
|
||||
self.assertEqual(scales[0], s1) # G scale (largest)
|
||||
self.assertEqual(scales[1], s2) # HO scale
|
||||
self.assertEqual(scales[2], s3) # N scale (smallest)
|
||||
|
||||
|
||||
class RollingStockTypeTestCase(TestCase):
|
||||
"""Test cases for RollingStockType model."""
|
||||
|
||||
def test_rolling_stock_type_creation(self):
|
||||
"""Test creating a rolling stock type."""
|
||||
stock_type = RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=1,
|
||||
)
|
||||
|
||||
self.assertEqual(str(stock_type), "Steam Locomotive locomotive")
|
||||
self.assertEqual(stock_type.slug, "steam-locomotive-locomotive")
|
||||
|
||||
def test_rolling_stock_type_unique_constraint(self):
|
||||
"""Test that category+type must be unique."""
|
||||
RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=1,
|
||||
)
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=2,
|
||||
)
|
||||
|
||||
def test_rolling_stock_type_ordering(self):
|
||||
"""Test ordering by order field."""
|
||||
t3 = RollingStockType.objects.create(
|
||||
type="Caboose", category="railcar", order=3
|
||||
)
|
||||
t1 = RollingStockType.objects.create(
|
||||
type="Steam", category="locomotive", order=1
|
||||
)
|
||||
t2 = RollingStockType.objects.create(
|
||||
type="Boxcar", category="railcar", order=2
|
||||
)
|
||||
|
||||
types = list(RollingStockType.objects.all())
|
||||
self.assertEqual(types[0], t1)
|
||||
self.assertEqual(types[1], t2)
|
||||
self.assertEqual(types[2], t3)
|
||||
|
||||
|
||||
class DecoderTestCase(TestCase):
|
||||
"""Test cases for Decoder model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.manufacturer = Manufacturer.objects.create(
|
||||
name="ESU",
|
||||
category="accessory",
|
||||
country="DE",
|
||||
)
|
||||
|
||||
def test_decoder_creation(self):
|
||||
"""Test creating a decoder."""
|
||||
decoder = Decoder.objects.create(
|
||||
name="LokSound 5",
|
||||
manufacturer=self.manufacturer,
|
||||
version="5.0",
|
||||
sound=True,
|
||||
)
|
||||
|
||||
self.assertEqual(str(decoder), "ESU - LokSound 5")
|
||||
self.assertTrue(decoder.sound)
|
||||
|
||||
def test_decoder_without_sound(self):
|
||||
"""Test creating a non-sound decoder."""
|
||||
decoder = Decoder.objects.create(
|
||||
name="LokPilot 5",
|
||||
manufacturer=self.manufacturer,
|
||||
sound=False,
|
||||
)
|
||||
|
||||
self.assertFalse(decoder.sound)
|
||||
|
||||
def test_decoder_ordering(self):
|
||||
"""Test decoder ordering by manufacturer name and decoder name."""
|
||||
man2 = Manufacturer.objects.create(
|
||||
name="Digitrax",
|
||||
category="accessory",
|
||||
)
|
||||
|
||||
d1 = Decoder.objects.create(
|
||||
name="LokSound 5",
|
||||
manufacturer=self.manufacturer,
|
||||
)
|
||||
d2 = Decoder.objects.create(
|
||||
name="DZ123",
|
||||
manufacturer=man2,
|
||||
)
|
||||
d3 = Decoder.objects.create(
|
||||
name="LokPilot 5",
|
||||
manufacturer=self.manufacturer,
|
||||
)
|
||||
|
||||
decoders = list(Decoder.objects.all())
|
||||
# Ordered by manufacturer name, then decoder name
|
||||
self.assertEqual(decoders[0], d2) # Digitrax
|
||||
self.assertTrue(decoders.index(d3) < decoders.index(d1)) # LokPilot before LokSound
|
||||
|
||||
|
||||
class ShopTestCase(TestCase):
|
||||
"""Test cases for Shop model."""
|
||||
|
||||
def test_shop_creation(self):
|
||||
"""Test creating a shop."""
|
||||
shop = Shop.objects.create(
|
||||
name="Caboose Hobbies",
|
||||
country="US",
|
||||
website="https://www.caboosehobbies.com",
|
||||
on_line=True,
|
||||
active=True,
|
||||
)
|
||||
|
||||
self.assertEqual(str(shop), "Caboose Hobbies")
|
||||
self.assertTrue(shop.on_line)
|
||||
self.assertTrue(shop.active)
|
||||
|
||||
def test_shop_defaults(self):
|
||||
"""Test shop default values."""
|
||||
shop = Shop.objects.create(name="Local Shop")
|
||||
|
||||
self.assertTrue(shop.on_line) # Default True
|
||||
self.assertTrue(shop.active) # Default True
|
||||
|
||||
def test_shop_offline(self):
|
||||
"""Test creating an offline shop."""
|
||||
shop = Shop.objects.create(
|
||||
name="Brick and Mortar Store",
|
||||
on_line=False,
|
||||
)
|
||||
|
||||
self.assertFalse(shop.on_line)
|
||||
|
||||
|
||||
class TagTestCase(TestCase):
|
||||
"""Test cases for Tag model."""
|
||||
|
||||
def test_tag_creation(self):
|
||||
"""Test creating a tag."""
|
||||
tag = Tag.objects.create(
|
||||
name="Narrow Gauge",
|
||||
slug="narrow-gauge",
|
||||
)
|
||||
|
||||
self.assertEqual(str(tag), "Narrow Gauge")
|
||||
self.assertEqual(tag.slug, "narrow-gauge")
|
||||
|
||||
def test_tag_unique_name(self):
|
||||
"""Test that tag name must be unique."""
|
||||
Tag.objects.create(name="Narrow Gauge", slug="narrow-gauge")
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
Tag.objects.create(name="Narrow Gauge", slug="narrow-gauge")
|
||||
|
||||
139
ram/ram/tests.py
Normal file
139
ram/ram/tests.py
Normal file
@@ -0,0 +1,139 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from ram.utils import slugify
|
||||
|
||||
|
||||
class SimpleBaseModelTestCase(TestCase):
|
||||
"""Test cases for SimpleBaseModel."""
|
||||
|
||||
def test_obj_type_property(self):
|
||||
"""Test obj_type returns model name."""
|
||||
# We can't instantiate abstract models directly,
|
||||
# so we test via a concrete model that inherits from it
|
||||
from metadata.models import Company
|
||||
|
||||
company = Company.objects.create(name="Test", country="US")
|
||||
self.assertEqual(company.obj_type, "company")
|
||||
|
||||
def test_obj_label_property(self):
|
||||
"""Test obj_label returns object name."""
|
||||
from metadata.models import Company
|
||||
|
||||
company = Company.objects.create(name="Test", country="US")
|
||||
self.assertEqual(company.obj_label, "Company")
|
||||
|
||||
|
||||
class BaseModelTestCase(TestCase):
|
||||
"""Test cases for BaseModel."""
|
||||
|
||||
def test_base_model_fields(self):
|
||||
"""Test that BaseModel includes expected fields."""
|
||||
# Test via a concrete model
|
||||
from roster.models import RollingStock, RollingClass
|
||||
from metadata.models import Company, Scale, RollingStockType
|
||||
|
||||
company = Company.objects.create(name="Test", country="US")
|
||||
scale = Scale.objects.create(scale="HO", ratio="1:87", tracks=16.5)
|
||||
stock_type = RollingStockType.objects.create(
|
||||
type="Test", category="locomotive", order=1
|
||||
)
|
||||
rolling_class = RollingClass.objects.create(
|
||||
identifier="Test",
|
||||
type=stock_type,
|
||||
company=company,
|
||||
)
|
||||
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=rolling_class,
|
||||
road_number="123",
|
||||
scale=scale,
|
||||
description="<p>Test description</p>",
|
||||
notes="Test notes",
|
||||
)
|
||||
|
||||
# Check BaseModel fields exist
|
||||
self.assertIsNotNone(stock.uuid)
|
||||
self.assertTrue(stock.published)
|
||||
self.assertIsNotNone(stock.creation_time)
|
||||
self.assertIsNotNone(stock.updated_time)
|
||||
self.assertEqual(stock.description, "<p>Test description</p>")
|
||||
|
||||
def test_base_model_obj_properties(self):
|
||||
"""Test obj_type and obj_label properties."""
|
||||
from roster.models import RollingStock, RollingClass
|
||||
from metadata.models import Company, Scale, RollingStockType
|
||||
|
||||
company = Company.objects.create(name="Test", country="US")
|
||||
scale = Scale.objects.create(scale="HO", ratio="1:87", tracks=16.5)
|
||||
stock_type = RollingStockType.objects.create(
|
||||
type="Test", category="locomotive", order=1
|
||||
)
|
||||
rolling_class = RollingClass.objects.create(
|
||||
identifier="Test",
|
||||
type=stock_type,
|
||||
company=company,
|
||||
)
|
||||
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=rolling_class,
|
||||
road_number="123",
|
||||
scale=scale,
|
||||
)
|
||||
|
||||
self.assertEqual(stock.obj_type, "rollingstock")
|
||||
self.assertEqual(stock.obj_label, "RollingStock")
|
||||
|
||||
def test_base_model_published_default(self):
|
||||
"""Test that published defaults to True."""
|
||||
from roster.models import RollingStock, RollingClass
|
||||
from metadata.models import Company, Scale, RollingStockType
|
||||
|
||||
company = Company.objects.create(name="Test", country="US")
|
||||
scale = Scale.objects.create(scale="HO", ratio="1:87", tracks=16.5)
|
||||
stock_type = RollingStockType.objects.create(
|
||||
type="Test", category="locomotive", order=1
|
||||
)
|
||||
rolling_class = RollingClass.objects.create(
|
||||
identifier="Test",
|
||||
type=stock_type,
|
||||
company=company,
|
||||
)
|
||||
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=rolling_class,
|
||||
road_number="123",
|
||||
scale=scale,
|
||||
)
|
||||
|
||||
self.assertTrue(stock.published)
|
||||
|
||||
|
||||
class SlugifyTestCase(TestCase):
|
||||
"""Test cases for slugify utility function."""
|
||||
|
||||
def test_slugify_basic(self):
|
||||
"""Test basic slugification."""
|
||||
self.assertEqual(slugify("Hello World"), "hello-world")
|
||||
|
||||
def test_slugify_special_characters(self):
|
||||
"""Test slugification with special characters."""
|
||||
self.assertEqual(slugify("Hello & World!"), "hello-world")
|
||||
|
||||
def test_slugify_multiple_spaces(self):
|
||||
"""Test slugification with multiple spaces."""
|
||||
self.assertEqual(slugify("Hello World"), "hello-world")
|
||||
|
||||
def test_slugify_numbers(self):
|
||||
"""Test slugification with numbers."""
|
||||
self.assertEqual(slugify("Test 123 ABC"), "test-123-abc")
|
||||
|
||||
def test_slugify_underscores(self):
|
||||
"""Test slugification preserves underscores."""
|
||||
result = slugify("test_value")
|
||||
# Depending on implementation, may keep or convert underscores
|
||||
self.assertIn(result, ["test-value", "test_value"])
|
||||
|
||||
def test_slugify_empty_string(self):
|
||||
"""Test slugification of empty string."""
|
||||
result = slugify("")
|
||||
self.assertEqual(result, "")
|
||||
@@ -1,3 +1,340 @@
|
||||
from decimal import Decimal
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.conf import settings
|
||||
|
||||
# Create your tests here.
|
||||
from roster.models import RollingClass, RollingStock, RollingStockImage
|
||||
from metadata.models import (
|
||||
Company,
|
||||
Manufacturer,
|
||||
Scale,
|
||||
RollingStockType,
|
||||
Decoder,
|
||||
)
|
||||
|
||||
|
||||
class RollingClassTestCase(TestCase):
|
||||
"""Test cases for RollingClass model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
# Create a company
|
||||
self.company = Company.objects.create(
|
||||
name="Rio Grande Southern",
|
||||
country="US",
|
||||
)
|
||||
|
||||
# Create a rolling stock type
|
||||
self.stock_type = RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=1,
|
||||
)
|
||||
|
||||
# Create a real manufacturer
|
||||
self.real_manufacturer = Manufacturer.objects.create(
|
||||
name="Baldwin Locomotive Works",
|
||||
category="real",
|
||||
country="US",
|
||||
)
|
||||
|
||||
def test_rolling_class_creation(self):
|
||||
"""Test creating a rolling class."""
|
||||
rolling_class = RollingClass.objects.create(
|
||||
identifier="C-19",
|
||||
type=self.stock_type,
|
||||
company=self.company,
|
||||
description="<p>Narrow gauge steam locomotive</p>",
|
||||
)
|
||||
|
||||
self.assertEqual(str(rolling_class), "Rio Grande Southern C-19")
|
||||
self.assertEqual(rolling_class.identifier, "C-19")
|
||||
self.assertEqual(rolling_class.type, self.stock_type)
|
||||
self.assertEqual(rolling_class.company, self.company)
|
||||
|
||||
def test_rolling_class_country_property(self):
|
||||
"""Test that rolling class inherits country from company."""
|
||||
rolling_class = RollingClass.objects.create(
|
||||
identifier="C-19",
|
||||
type=self.stock_type,
|
||||
company=self.company,
|
||||
)
|
||||
|
||||
self.assertEqual(rolling_class.country, self.company.country)
|
||||
|
||||
def test_rolling_class_ordering(self):
|
||||
"""Test rolling class ordering by company and identifier."""
|
||||
company2 = Company.objects.create(name="D&RGW", country="US")
|
||||
|
||||
rc1 = RollingClass.objects.create(
|
||||
identifier="K-27", type=self.stock_type, company=company2
|
||||
)
|
||||
rc2 = RollingClass.objects.create(
|
||||
identifier="C-19", type=self.stock_type, company=self.company
|
||||
)
|
||||
rc3 = RollingClass.objects.create(
|
||||
identifier="K-28", type=self.stock_type, company=company2
|
||||
)
|
||||
|
||||
classes = list(RollingClass.objects.all())
|
||||
self.assertEqual(classes[0], rc1) # D&RGW K-27
|
||||
self.assertEqual(classes[1], rc3) # D&RGW K-28
|
||||
self.assertEqual(classes[2], rc2) # Rio Grande Southern comes last
|
||||
|
||||
def test_rolling_class_manufacturer_relationship(self):
|
||||
"""Test many-to-many relationship with manufacturers."""
|
||||
rolling_class = RollingClass.objects.create(
|
||||
identifier="C-19",
|
||||
type=self.stock_type,
|
||||
company=self.company,
|
||||
)
|
||||
|
||||
rolling_class.manufacturer.add(self.real_manufacturer)
|
||||
|
||||
self.assertEqual(rolling_class.manufacturer.count(), 1)
|
||||
self.assertIn(self.real_manufacturer, rolling_class.manufacturer.all())
|
||||
|
||||
|
||||
class RollingStockTestCase(TestCase):
|
||||
"""Test cases for RollingStock model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
# Create necessary related objects
|
||||
self.company = Company.objects.create(name="RGS", country="US")
|
||||
|
||||
self.stock_type = RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=1,
|
||||
)
|
||||
|
||||
self.rolling_class = RollingClass.objects.create(
|
||||
identifier="C-19",
|
||||
type=self.stock_type,
|
||||
company=self.company,
|
||||
)
|
||||
|
||||
self.scale = Scale.objects.create(
|
||||
scale="HOn3",
|
||||
ratio="1:87",
|
||||
tracks=10.5,
|
||||
gauge="3 ft",
|
||||
)
|
||||
|
||||
self.model_manufacturer = Manufacturer.objects.create(
|
||||
name="Blackstone Models",
|
||||
category="model",
|
||||
country="US",
|
||||
)
|
||||
|
||||
def test_rolling_stock_creation(self):
|
||||
"""Test creating rolling stock."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
manufacturer=self.model_manufacturer,
|
||||
)
|
||||
|
||||
self.assertEqual(str(stock), "RGS C-19 340")
|
||||
self.assertEqual(stock.road_number, "340")
|
||||
self.assertEqual(stock.road_number_int, 340)
|
||||
self.assertTrue(stock.published)
|
||||
|
||||
def test_road_number_int_extraction(self):
|
||||
"""Test automatic extraction of integer from road number."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="RGS-42",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
self.assertEqual(stock.road_number_int, 42)
|
||||
|
||||
def test_road_number_no_integer(self):
|
||||
"""Test road number with no integer defaults to 0."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="N/A",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
self.assertEqual(stock.road_number_int, 0)
|
||||
|
||||
def test_item_number_slug_generation(self):
|
||||
"""Test automatic slug generation from item number."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
item_number="BLI-123 ABC",
|
||||
)
|
||||
|
||||
self.assertEqual(stock.item_number_slug, "bli-123-abc")
|
||||
|
||||
def test_featured_limit_validation(self):
|
||||
"""Test that featured items are limited by FEATURED_ITEMS_MAX."""
|
||||
# Create FEATURED_ITEMS_MAX featured items
|
||||
for i in range(settings.FEATURED_ITEMS_MAX):
|
||||
RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number=str(i),
|
||||
scale=self.scale,
|
||||
featured=True,
|
||||
)
|
||||
|
||||
# Try to create one more featured item
|
||||
extra_stock = RollingStock(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="999",
|
||||
scale=self.scale,
|
||||
featured=True,
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError) as cm:
|
||||
extra_stock.clean()
|
||||
|
||||
self.assertIn("featured items", str(cm.exception))
|
||||
|
||||
def test_price_decimal_field(self):
|
||||
"""Test price field accepts decimal values."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
price=Decimal("249.99"),
|
||||
)
|
||||
|
||||
self.assertEqual(stock.price, Decimal("249.99"))
|
||||
|
||||
def test_decoder_interface_display(self):
|
||||
"""Test decoder interface display method."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
decoder_interface=1, # 21-pin interface
|
||||
)
|
||||
|
||||
interface = stock.get_decoder_interface()
|
||||
self.assertIsNotNone(interface)
|
||||
self.assertNotEqual(interface, "No interface")
|
||||
|
||||
def test_decoder_interface_none(self):
|
||||
"""Test decoder interface when not set."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
interface = stock.get_decoder_interface()
|
||||
self.assertEqual(interface, "No interface")
|
||||
|
||||
def test_country_and_company_properties(self):
|
||||
"""Test that country and company properties work."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
self.assertEqual(stock.country, self.company.country)
|
||||
self.assertEqual(stock.company, self.company)
|
||||
|
||||
def test_get_absolute_url(self):
|
||||
"""Test get_absolute_url returns correct URL."""
|
||||
stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
url = stock.get_absolute_url()
|
||||
self.assertIn(str(stock.uuid), url)
|
||||
|
||||
def test_published_filtering(self):
|
||||
"""Test PublicManager filters unpublished items."""
|
||||
published_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
published=True,
|
||||
)
|
||||
|
||||
unpublished_stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="341",
|
||||
scale=self.scale,
|
||||
published=False,
|
||||
)
|
||||
|
||||
# PublicManager should only return published items
|
||||
all_stock = RollingStock.objects.all()
|
||||
|
||||
# Note: This test assumes PublicManager is properly configured
|
||||
# to filter by published=True
|
||||
self.assertIn(published_stock, all_stock)
|
||||
|
||||
def test_ordering(self):
|
||||
"""Test rolling stock ordering."""
|
||||
stock1 = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
stock2 = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="342",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
stock3 = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="341",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
stocks = list(RollingStock.objects.all())
|
||||
self.assertEqual(stocks[0], stock1) # 340
|
||||
self.assertEqual(stocks[1], stock3) # 341
|
||||
self.assertEqual(stocks[2], stock2) # 342
|
||||
|
||||
|
||||
class RollingStockImageTestCase(TestCase):
|
||||
"""Test cases for RollingStockImage model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.company = Company.objects.create(name="RGS", country="US")
|
||||
self.stock_type = RollingStockType.objects.create(
|
||||
type="Steam Locomotive",
|
||||
category="locomotive",
|
||||
order=1,
|
||||
)
|
||||
self.rolling_class = RollingClass.objects.create(
|
||||
identifier="C-19",
|
||||
type=self.stock_type,
|
||||
company=self.company,
|
||||
)
|
||||
self.scale = Scale.objects.create(
|
||||
scale="HOn3",
|
||||
ratio="1:87",
|
||||
tracks=10.5,
|
||||
)
|
||||
self.stock = RollingStock.objects.create(
|
||||
rolling_class=self.rolling_class,
|
||||
road_number="340",
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
def test_image_ordering(self):
|
||||
"""Test that images are ordered by the order field."""
|
||||
# Note: Actual image upload testing would require test files
|
||||
# This test validates the relationship exists
|
||||
self.assertEqual(self.stock.image.count(), 0)
|
||||
|
||||
# The image model should have an order field
|
||||
self.assertTrue(hasattr(RollingStockImage, "order"))
|
||||
|
||||
Reference in New Issue
Block a user