from django.test import TestCase, Client, RequestFactory from django.urls import reverse from django.contrib.auth import get_user_model from cards.models import Card, Deck from trades.models import TradeOffer, TradeOfferHaveCard, TradeOfferWantCard from accounts.models import FriendCode from home.views import HomePageView import json from collections import OrderedDict from unittest.mock import patch, MagicMock from django.core.exceptions import ObjectDoesNotExist import importlib from tests.utils.rarity import RARITY_MAPPING User = get_user_model() class HomePageViewTests(TestCase): """Test suite for the HomePageView.""" @classmethod def setUpTestData(cls): """Set up data for all test methods.""" # Create a user cls.user = User.objects.create_user( username='testuser', email='testuser@example.com', password='testpass123' ) # Create a friend code for the user cls.friend_code = FriendCode.objects.create( user=cls.user, friend_code='SW-1234-5678-9012', in_game_name='TestTrainer' ) # Create decks cls.deck1 = Deck.objects.create( name='Test Deck 1', hex_color='#FF0000', cardset='TEST01' ) # Create cards with different rarities cls.common_card = Card.objects.create( name='Common Test Card', cardset='TEST01', cardnum=1, style='normal', rarity_icon='★', rarity_level=1 ) cls.common_card.decks.add(cls.deck1) cls.rare_card = Card.objects.create( name='Rare Test Card', cardset='TEST01', cardnum=2, style='normal', rarity_icon='★★★', rarity_level=3 ) cls.rare_card.decks.add(cls.deck1) cls.ultra_rare_card = Card.objects.create( name='Ultra Rare Test Card', cardset='TEST01', cardnum=3, style='normal', rarity_icon='★★★★', rarity_level=4 ) cls.ultra_rare_card.decks.add(cls.deck1) # Create trade offers with consistent rarities cls.common_trade = TradeOffer.objects.create( initiated_by=cls.friend_code, rarity_icon=RARITY_MAPPING[1], rarity_level=1 ) cls.rare_trade = TradeOffer.objects.create( initiated_by=cls.friend_code, rarity_icon=RARITY_MAPPING[3], rarity_level=3 ) # Add have and want cards with the SAME rarity for each trade TradeOfferHaveCard.objects.create( trade_offer=cls.common_trade, card=cls.common_card, quantity=2 ) TradeOfferHaveCard.objects.create( trade_offer=cls.rare_trade, card=cls.rare_card, quantity=1 ) # Add want cards with the SAME rarity as the have cards for each trade TradeOfferWantCard.objects.create( trade_offer=cls.common_trade, card=cls.common_card, quantity=1 ) TradeOfferWantCard.objects.create( trade_offer=cls.rare_trade, card=cls.rare_card, # Changed from ultra_rare_card to match the rarity quantity=1 ) def setUp(self): """Set up before each test method.""" self.client = Client() self.url = reverse('home') self.factory = RequestFactory() def test_home_page_status_code(self): """Test that the home page returns a 200 status code.""" response = self.client.get(self.url) self.assertEqual(response.status_code, 200) def test_home_page_template(self): """Test that the home page uses the correct template.""" response = self.client.get(self.url) self.assertTemplateUsed(response, 'home/home.html') def test_home_page_context_cards(self): """Test that the home page contains all cards in the context.""" response = self.client.get(self.url) self.assertIn('cards', response.context) self.assertEqual(response.context['cards'].count(), 3) def test_home_page_context_recent_offers(self): """Test that the home page contains recent offers in the context.""" response = self.client.get(self.url) self.assertIn('recent_offers', response.context) self.assertEqual(len(response.context['recent_offers']), 2) # Recent offers should be ordered by most recent first self.assertEqual(response.context['recent_offers'][0], self.rare_trade) def test_home_page_context_most_offered_cards(self): """Test that the home page contains most offered cards in the context.""" response = self.client.get(self.url) self.assertIn('most_offered_cards', response.context) most_offered = list(response.context['most_offered_cards']) self.assertEqual(len(most_offered), 2) # Common card should be most offered (quantity of 2) self.assertEqual(most_offered[0], self.common_card) def test_home_page_context_most_wanted_cards(self): """Test that the home page contains most wanted cards in the context.""" response = self.client.get(self.url) self.assertIn('most_wanted_cards', response.context) most_wanted = list(response.context['most_wanted_cards']) self.assertEqual(len(most_wanted), 2) def test_home_page_context_least_offered_cards(self): """Test that the home page contains least offered cards in the context.""" response = self.client.get(self.url) self.assertIn('least_offered_cards', response.context) def test_home_page_context_featured_offers(self): """Test that the home page contains featured offers in the context.""" response = self.client.get(self.url) self.assertIn('featured_offers', response.context) featured = response.context['featured_offers'] # Should be an OrderedDict self.assertIsInstance(featured, OrderedDict) # Should contain "All" category self.assertIn("All", featured) # Should contain both rarity icons self.assertIn('★★★', featured) self.assertIn('★', featured) # Higher rarity should come before lower rarity keys = list(featured.keys()) # First key should be "All" self.assertEqual(keys[0], "All") # Higher rarity (★★★) should come before lower rarity (★) self.assertIn('★★★', keys) self.assertIn('★', keys) self.assertTrue(keys.index('★★★') < keys.index('★')) def test_closed_offers_not_shown(self): """Test that closed offers are not shown on the home page.""" # Close one of the trade offers self.common_trade.is_closed = True self.common_trade.save() response = self.client.get(self.url) recent_offers = response.context['recent_offers'] # Should only show the rare trade now self.assertEqual(len(recent_offers), 1) self.assertEqual(recent_offers[0], self.rare_trade) def test_home_page_with_no_data(self): """Test home page rendering when there's no trade data.""" # Delete all trade offers TradeOffer.objects.all().delete() response = self.client.get(self.url) self.assertEqual(response.status_code, 200) # Should have empty lists for offers self.assertEqual(len(response.context['recent_offers']), 0) def test_home_page_with_authenticated_user(self): """Test that the home page works for authenticated users.""" self.client.login(username='testuser', password='testpass123') response = self.client.get(self.url) self.assertEqual(response.status_code, 200) def test_rarity_sorting_in_featured_offers(self): """Test that offers are sorted by rarity level in descending order.""" # Create a new ultra rare trade with consistent rarity ultra_trade = TradeOffer.objects.create( initiated_by=self.friend_code, rarity_icon='★★★★', rarity_level=4 ) # Add have and want cards with the same rarity TradeOfferHaveCard.objects.create( trade_offer=ultra_trade, card=self.ultra_rare_card, quantity=1 ) TradeOfferWantCard.objects.create( trade_offer=ultra_trade, card=self.ultra_rare_card, quantity=1 ) response = self.client.get(self.url) featured = response.context['featured_offers'] keys = list(featured.keys()) # Order should be: "All", "★★★★" (level 4), "★★★" (level 3), "★" (level 1) self.assertEqual(keys[0], "All") self.assertEqual(keys[1], "★★★★") self.assertEqual(keys[2], "★★★") self.assertEqual(keys[3], "★") class HomePageViewMockTests(TestCase): """Test suite using mocks for HomePageView.""" def setUp(self): self.factory = RequestFactory() self.view = HomePageView() @patch('trades.models.TradeOffer.objects') @patch('cards.models.Card.objects') def test_get_context_data_with_mocks(self, mock_card_objects, mock_offer_objects): """Test get_context_data using mocks.""" # Set up request request = self.factory.get(reverse('home')) self.view.request = request # Mock the queryset responses mock_offer_filter = MagicMock() mock_offer_objects.filter.return_value = mock_offer_filter mock_offer_filter.order_by.return_value = [] mock_card_filter = MagicMock() mock_card_objects.filter.return_value = mock_card_filter mock_card_objects.annotate.return_value = mock_card_filter mock_card_objects.all.return_value.order_by.return_value = [] mock_card_filter.annotate.return_value = mock_card_filter mock_card_filter.order_by.return_value = [] mock_offer_filter.values_list.return_value.distinct.return_value = [] # Call the method context = self.view.get_context_data() # Verify the expected context keys exist self.assertIn('cards', context) self.assertIn('recent_offers', context) self.assertIn('most_offered_cards', context) self.assertIn('most_wanted_cards', context) self.assertIn('least_offered_cards', context) self.assertIn('featured_offers', context) @patch('trades.models.TradeOffer.objects') def test_empty_featured_offers(self, mock_offer_objects): """Test handling of empty featured offers.""" # Set up request request = self.factory.get(reverse('home')) self.view.request = request # Configure mock to return empty queryset mock_offer_filter = MagicMock() mock_offer_objects.filter.return_value = mock_offer_filter mock_offer_filter.order_by.return_value = [] mock_offer_filter.values_list.return_value.distinct.return_value = [] # Call the method context = self.view.get_context_data() # Verify the featured_offers is an OrderedDict but with just the "All" key self.assertIsInstance(context['featured_offers'], OrderedDict) self.assertIn("All", context['featured_offers']) self.assertEqual(len(context['featured_offers']), 1) @patch('trades.models.TradeOffer.objects.filter') def test_exception_handling(self, mock_filter): """Test that exceptions are handled gracefully.""" # Set up request request = self.factory.get(reverse('home')) self.view.request = request # Configure mock to raise an exception mock_filter.side_effect = Exception("Database error") # Call the method - should not raise an exception with self.assertLogs(level='ERROR') as cm: context = self.view.get_context_data() # Check if error was logged self.assertIn("Unhandled error in HomePageView.get_context_data", cm.output[0]) # Verify fallback values were set self.assertEqual(len(context['cards']), 0) self.assertEqual(len(context['recent_offers']), 0) self.assertEqual(len(context['most_offered_cards']), 0) self.assertEqual(len(context['most_wanted_cards']), 0) self.assertEqual(len(context['least_offered_cards']), 0) self.assertIsInstance(context['featured_offers'], OrderedDict) self.assertEqual(len(context['featured_offers']), 1) self.assertIn("All", context['featured_offers']) class HomePageEdgeCaseTests(TestCase): """Test edge cases for the home page.""" def setUp(self): self.client = Client() self.url = reverse('home') # Create a user self.user = User.objects.create_user( username='testuser', email='testuser@example.com', password='testpass123' ) # Create a friend code for the user self.friend_code = FriendCode.objects.create( user=self.user, friend_code='SW-1234-5678-9012', in_game_name='TestTrainer' ) def test_home_page_with_no_cards(self): """Test home page with no cards in the database.""" response = self.client.get(self.url) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context['cards']), 0) def test_home_page_with_many_offers(self): """Test home page with many offers to verify pagination or limiting works.""" # Create a card card = Card.objects.create( name='Test Card', cardset='TEST01', cardnum=1, style='normal', rarity_icon='★', rarity_level=1 ) # Create 20 trade offers for i in range(20): trade = TradeOffer.objects.create( initiated_by=self.friend_code, rarity_icon='★', rarity_level=1 ) # Add have and want cards TradeOfferHaveCard.objects.create( trade_offer=trade, card=card, quantity=1 ) TradeOfferWantCard.objects.create( trade_offer=trade, card=card, quantity=1 ) response = self.client.get(self.url) # Check that recent_offers is limited to 6 as per the view self.assertEqual(len(response.context['recent_offers']), 6) def test_home_page_with_invalid_parameters(self): """Test home page with invalid GET parameters.""" # The view should ignore invalid parameters response = self.client.get(f"{self.url}?invalid=param&another=invalid") self.assertEqual(response.status_code, 200) def test_performance_with_large_dataset(self): """Test performance with a larger dataset (basic check).""" # Create a card card = Card.objects.create( name='Performance Test Card', cardset='PERF01', cardnum=1, style='normal', rarity_icon='★', rarity_level=1 ) # Create 50 trade offers with different rarities for i in range(50): rarity_level = (i % 5) + 1 # 1-5 rarity_icon = '★' * rarity_level trade = TradeOffer.objects.create( initiated_by=self.friend_code, rarity_icon=rarity_icon, rarity_level=rarity_level ) # Add have and want cards with the same rarity rarity_card = Card.objects.create( name=f'Performance Test Card {i}', cardset='PERF01', cardnum=i+10, style='normal', rarity_icon=rarity_icon, rarity_level=rarity_level ) TradeOfferHaveCard.objects.create( trade_offer=trade, card=rarity_card, quantity=1 ) TradeOfferWantCard.objects.create( trade_offer=trade, card=rarity_card, quantity=1 ) # Basic performance test - just checking it completes without timeout import time start = time.time() response = self.client.get(self.url) end = time.time() self.assertEqual(response.status_code, 200) # Should be reasonably fast (adjust threshold as needed) execution_time = end - start self.assertLess(execution_time, 2.0) # Should complete in under 2 seconds class TemplateRenderingTests(TestCase): """Tests focused on template rendering.""" @classmethod def setUpTestData(cls): # Create a user cls.user = User.objects.create_user( username='testuser', email='testuser@example.com', password='testpass123' ) # Create a friend code for the user cls.friend_code = FriendCode.objects.create( user=cls.user, friend_code='SW-1234-5678-9012', in_game_name='TestTrainer' ) # Create a card cls.card = Card.objects.create( name='Test Card', cardset='TEST01', cardnum=1, style='normal', rarity_icon='★', rarity_level=1 ) # Create a trade offer cls.trade = TradeOffer.objects.create( initiated_by=cls.friend_code, rarity_icon='★', rarity_level=1 ) # Add have and want cards TradeOfferHaveCard.objects.create( trade_offer=cls.trade, card=cls.card, quantity=1 ) TradeOfferWantCard.objects.create( trade_offer=cls.trade, card=cls.card, quantity=1 ) def setUp(self): self.client = Client() self.factory = RequestFactory() def test_template_used(self): """Test that the correct template is used.""" response = self.client.get(reverse('home')) self.assertTemplateUsed(response, 'home/home.html') def test_context_variables_exist(self): """Test that all expected context variables exist.""" response = self.client.get(reverse('home')) # Check all required context variables expected_keys = [ 'cards', 'recent_offers', 'most_offered_cards', 'most_wanted_cards', 'least_offered_cards', 'featured_offers', ] for key in expected_keys: self.assertIn(key, response.context) def test_view_with_pagination_params(self): """Test that view handles pagination parameters correctly, if applicable.""" # Create additional trade offers if pagination is implemented for i in range(10): trade = TradeOffer.objects.create( initiated_by=self.friend_code, rarity_icon='★', rarity_level=1 ) # Add have and want cards TradeOfferHaveCard.objects.create( trade_offer=trade, card=self.card, quantity=1 ) TradeOfferWantCard.objects.create( trade_offer=trade, card=self.card, quantity=1 ) # Test with page parameter response = self.client.get(f"{reverse('home')}?page=1") self.assertEqual(response.status_code, 200) # Test with invalid page parameter response = self.client.get(f"{reverse('home')}?page=999") self.assertEqual(response.status_code, 200) # Should still render with default page # Test with non-numeric page parameter response = self.client.get(f"{reverse('home')}?page=abc") self.assertEqual(response.status_code, 200) # Should handle gracefully @patch('home.views.HomePageView.get_context_data') def test_view_renders_with_missing_context(self, mock_get_context): """Test that view renders even with incomplete context data.""" # Return incomplete context mock_get_context.return_value = {'cards': []} # Should still render without error even with missing context variables response = self.client.get(reverse('home')) self.assertEqual(response.status_code, 200) def test_compatibility_with_multiple_django_versions(self): """Ensure compatibility with different Django versions.""" import django # Simply log the Django version - the test itself verifies the page renders # with the current version django_version = django.get_version() response = self.client.get(reverse('home')) self.assertEqual(response.status_code, 200)