Skip to content

Commit 993051d

Browse files
committed
test: Implement comprehensive test suite for all major features
1 parent 2803fca commit 993051d

File tree

1 file changed

+82
-44
lines changed

1 file changed

+82
-44
lines changed

inventory/tests.py

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
# sherlock-python/inventory/tests.py
22

3-
from django.test import TestCase
3+
from django.test import TestCase, Client
44
from django.contrib.auth.models import User
55
from django.urls import reverse
66
from django.utils import timezone
77
from datetime import timedelta
88

99
from .models import Section, Space, Item, Student, CheckoutLog, CheckInLog, ItemLog
1010

11+
# ==============================================================================
12+
# MODEL TESTS
13+
# ==============================================================================
14+
1115
class ModelTests(TestCase):
12-
"""
13-
A suite of tests to verify the custom logic within our models.
14-
"""
15-
def setUp(self):
16-
"""Set up a common state for all model tests."""
17-
self.user = User.objects.create_user(username='testuser', password='password')
18-
self.student = Student.objects.create(name='Test Student', admission_number='T001', student_class='X', section='A')
19-
self.section = Section.objects.create(name='Test Section', section_code=1)
20-
self.space = Space.objects.create(name='Test Space', section=self.section, space_code=1)
21-
self.item = Item.objects.create(
16+
"""Tests for custom logic within the application's models."""
17+
18+
@classmethod
19+
def setUpTestData(cls):
20+
"""Set up non-modified objects used by all test methods."""
21+
cls.user = User.objects.create_user(username='testuser', password='password')
22+
cls.student = Student.objects.create(name='Test Student', admission_number='T001', student_class='X', section='A')
23+
cls.section = Section.objects.create(name='Test Section', section_code=1)
24+
cls.space = Space.objects.create(name='Test Space', section=cls.section, space_code=1, original_section_code=1)
25+
cls.item = Item.objects.create(
2226
name='Test Item',
23-
space=self.space,
27+
space=cls.space,
2428
item_code=1,
2529
quantity=20,
2630
buffer_quantity=5
@@ -31,9 +35,9 @@ def test_item_quantity_properties(self):
3135
self.assertEqual(self.item.checked_out_quantity, 0)
3236
self.assertEqual(self.item.available_quantity, 15)
3337

34-
log = CheckoutLog.objects.create(item=self.item, student=self.student, quantity=8, due_date=timezone.now())
38+
CheckoutLog.objects.create(item=self.item, student=self.student, quantity=8, due_date=timezone.now())
3539
self.assertEqual(self.item.checked_out_quantity, 8)
36-
self.assertEqual(self.item.available_quantity, 7)
40+
self.assertEqual(self.item.available_quantity, 7)
3741

3842
def test_checkout_log_is_overdue(self):
3943
"""Test the is_overdue property of the CheckoutLog model."""
@@ -50,81 +54,115 @@ def test_checkout_log_is_overdue(self):
5054
def test_checkout_log_partial_returns(self):
5155
"""Test the quantity calculation properties of the CheckoutLog model."""
5256
log = CheckoutLog.objects.create(item=self.item, student=self.student, quantity=10, due_date=timezone.now())
53-
5457
self.assertEqual(log.quantity_returned_so_far, 0)
5558
self.assertEqual(log.quantity_still_on_loan, 10)
5659

5760
CheckInLog.objects.create(checkout_log=log, quantity_returned=4)
61+
log.refresh_from_db()
5862
self.assertEqual(log.quantity_returned_so_far, 4)
5963
self.assertEqual(log.quantity_still_on_loan, 6)
6064

61-
class ViewTests(TestCase):
65+
# ==============================================================================
66+
# VIEW & WORKFLOW TESTS
67+
# ==============================================================================
68+
69+
class ViewAndWorkflowTests(TestCase):
6270
"""
63-
A suite of tests to verify that all pages load correctly and are protected.
71+
A suite of tests to verify that pages load, are protected, and that
72+
key user workflows function correctly from end-to-end.
6473
"""
6574
def setUp(self):
66-
"""Set up a user and some data for view tests."""
75+
"""Set up a logged-in client and base data for all view tests."""
6776
self.user = User.objects.create_user(username='testuser', password='password123')
6877
self.client.login(username='testuser', password='password123')
78+
6979
self.section = Section.objects.create(name='Test Section', section_code=1)
70-
self.space = Space.objects.create(name='Test Space', section=self.section, space_code=1)
80+
self.space = Space.objects.create(name='Test Space', section=self.section, space_code=1, original_section_code=1)
7181
self.item = Item.objects.create(name='Test Item', space=self.space, item_code=1, quantity=10)
82+
self.student = Student.objects.create(name='Test Student', admission_number='T001', student_class='X', section='A')
7283

7384
def test_all_pages_load_correctly(self):
7485
"""Test that all main pages return a 200 OK status code for a logged-in user."""
7586
urls = [
7687
reverse('inventory:dashboard'),
77-
reverse('inventory:section_list'),
88+
reverse('inventory:inventory_browser'),
7889
reverse('inventory:section_detail', args=[self.section.section_code]),
79-
reverse('inventory:space_list', args=[self.section.section_code]),
8090
reverse('inventory:space_detail', args=[self.section.section_code, self.space.space_code]),
81-
reverse('inventory:item_list', args=[self.section.section_code, self.space.space_code]),
8291
reverse('inventory:item_detail', args=[self.section.section_code, self.space.space_code, self.item.item_code]),
8392
reverse('inventory:student_list'),
93+
reverse('inventory:student_detail', args=[self.student.id]),
8494
reverse('inventory:on_loan_dashboard'),
8595
reverse('inventory:overdue_report'),
96+
reverse('inventory:low_stock_report'),
8697
reverse('inventory:search'),
98+
reverse('inventory:print_queue'),
8799
]
88100
for url in urls:
89101
response = self.client.get(url)
90102
self.assertEqual(response.status_code, 200, f"Failed to load page: {url}")
91103

92104
def test_pages_redirect_if_not_logged_in(self):
93-
"""Test that protected pages redirect to the login screen for an anonymous user."""
105+
"""Test that a protected page redirects to the login screen for an anonymous user."""
94106
self.client.logout()
95107
response = self.client.get(reverse('inventory:dashboard'))
96-
self.assertEqual(response.status_code, 302)
108+
self.assertEqual(response.status_code, 302)
97109
self.assertIn(reverse('login'), response.url)
98110

99-
class StockAdjustmentWorkflowTests(TestCase):
100-
"""
101-
An end-to-end test for the full stock adjustment workflow.
102-
"""
103-
def setUp(self):
104-
self.user = User.objects.create_user(username='testuser', password='password123')
105-
self.client.login(username='testuser', password='password123')
106-
self.section = Section.objects.create(name='Test Section', section_code=1)
107-
self.space = Space.objects.create(name='Test Space', section=self.section, space_code=1)
108-
self.item = Item.objects.create(name='Test Item', space=self.space, item_code=1, quantity=10)
109-
110111
def test_receive_stock_workflow(self):
111-
"""Test the process of receiving new stock for an item."""
112+
"""Test the end-to-end process of receiving new stock for an item."""
112113
self.assertEqual(Item.objects.get(id=self.item.id).quantity, 10)
113114
self.assertEqual(ItemLog.objects.count(), 0)
114115

115116
url = reverse('inventory:adjust_stock', args=[self.section.section_code, self.space.space_code, self.item.item_code, 'RECEIVED'])
117+
response = self.client.post(url, {'quantity': '5', 'notes': 'New shipment arrived'})
116118

117-
response = self.client.post(url, {
118-
'quantity': '5',
119-
'notes': 'New shipment arrived'
120-
})
121-
122-
self.assertEqual(response.status_code, 302)
123119
self.assertRedirects(response, self.item.get_absolute_url())
124-
125120
self.assertEqual(Item.objects.get(id=self.item.id).quantity, 15)
126121
self.assertEqual(ItemLog.objects.count(), 1)
127122
log = ItemLog.objects.first()
128123
self.assertEqual(log.action, 'RECEIVED')
129124
self.assertEqual(log.quantity_change, 5)
130-
self.assertEqual(log.notes, 'New shipment arrived')
125+
126+
def test_full_checkout_and_return_workflow(self):
127+
"""Test a complete checkout, check-in, and loan history workflow."""
128+
# 1. Start a checkout session for the student
129+
checkout_url = reverse('inventory:checkout_session', args=[self.student.id])
130+
session = self.client.session
131+
session['checkout_items'] = {str(self.item.id): 2}
132+
session.save()
133+
134+
# 2. Complete the checkout
135+
response = self.client.post(checkout_url, {
136+
'complete_checkout': 'true',
137+
'due_date_option': 'days',
138+
'days_to_return': '7',
139+
'notes': 'Project work',
140+
})
141+
self.assertRedirects(response, reverse('inventory:student_detail', args=[self.student.id]))
142+
143+
# Verify the checkout log was created
144+
self.assertEqual(CheckoutLog.objects.count(), 1)
145+
log = CheckoutLog.objects.first()
146+
self.assertEqual(log.student, self.student)
147+
self.assertEqual(log.item, self.item)
148+
self.assertEqual(log.quantity, 2)
149+
self.assertIsNone(log.return_date)
150+
151+
# 3. Process a partial return
152+
check_in_url = reverse('inventory:process_check_in', args=[log.id])
153+
response = self.client.post(check_in_url, {'quantity_returned': '1'})
154+
self.assertRedirects(response, reverse('inventory:check_in_page', args=[log.id]))
155+
156+
# Verify the state after partial return
157+
log.refresh_from_db()
158+
self.assertEqual(log.quantity_still_on_loan, 1)
159+
self.assertIsNone(log.return_date) # Should still be on loan
160+
161+
# 4. Process the final return
162+
response = self.client.post(check_in_url, {'quantity_returned': '1'})
163+
self.assertRedirects(response, reverse('inventory:on_loan_dashboard'))
164+
165+
# Verify the loan is now closed
166+
log.refresh_from_db()
167+
self.assertEqual(log.quantity_still_on_loan, 0)
168+
self.assertIsNotNone(log.return_date)

0 commit comments

Comments
 (0)