From ca973acd6c907130525ebadcb1f5961dae16afc7 Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sat, 6 Sep 2025 19:29:05 -0400 Subject: [PATCH 1/7] Fixed test cases + error return status code --- backend/market/views.py | 9 +- backend/tests/market/mock_items.json | 8 +- backend/tests/market/mock_sublets.json | 8 +- backend/tests/market/self_user_items.json | 2 +- backend/tests/market/self_user_sublets.json | 4 +- backend/tests/market/test_market.py | 1568 ++++++++--------- backend/tests/market/test_permissions.py | 0 backend/tests/market/user_1_items.json | 4 +- ...ser_2_sublets.json => user_1_sublets.json} | 4 +- backend/tests/market/user_2_items.json | 13 - 10 files changed, 766 insertions(+), 854 deletions(-) delete mode 100644 backend/tests/market/test_permissions.py rename backend/tests/market/{user_2_sublets.json => user_1_sublets.json} (85%) delete mode 100644 backend/tests/market/user_2_items.json diff --git a/backend/market/views.py b/backend/market/views.py index 07270cef..29c7ba9f 100644 --- a/backend/market/views.py +++ b/backend/market/views.py @@ -251,7 +251,7 @@ def create(self, request, *args, **kwargs): item_id = int(self.kwargs["item_id"]) queryset = self.get_queryset() if queryset.filter(id=item_id).exists(): - raise exceptions.NotAcceptable("Favorite already exists") + raise exceptions.ValidationError("Favorite already exists") item = get_object_or_404(Item, id=item_id) self.get_queryset().add(item) return Response(status=status.HTTP_201_CREATED) @@ -289,7 +289,7 @@ def create(self, request, *args, **kwargs): data = request.data request.POST._mutable = True if self.get_queryset().filter(user=self.request.user).exists(): - raise exceptions.NotAcceptable("Offer already exists") + raise exceptions.ValidationError("Offer already exists") data["item"] = int(self.kwargs["item_id"]) data["user"] = self.request.user.id serializer = self.get_serializer(data=request.data) @@ -299,7 +299,10 @@ def create(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs): queryset = self.get_queryset() - filter = {"user": self.request.user, "item": int(self.kwargs["item_id"])} + filter = { + "user": self.request.user, + "item": int(self.kwargs["item_id"]), + } obj = get_object_or_404(queryset, **filter) self.check_object_permissions(self.request, obj) self.perform_destroy(obj) diff --git a/backend/tests/market/mock_items.json b/backend/tests/market/mock_items.json index c104463d..35a39c5b 100644 --- a/backend/tests/market/mock_items.json +++ b/backend/tests/market/mock_items.json @@ -8,7 +8,7 @@ "price": 20.0, "negotiable": true, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" }, { "tags": ["New"], @@ -19,7 +19,7 @@ "price": 5.0, "negotiable": false, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-10-12T00:00:00-05:00" + "expires_at": "3000-10-12T00:00:00-05:00" }, { "tags": ["Laptop", "New"], @@ -30,7 +30,7 @@ "price": 2000.0, "negotiable": true, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-08-12T00:00:00-05:00" + "expires_at": "3000-08-12T00:00:00-05:00" }, { "tags": ["Couch"], @@ -41,6 +41,6 @@ "price": 400.0, "negotiable": true, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" } ] \ No newline at end of file diff --git a/backend/tests/market/mock_sublets.json b/backend/tests/market/mock_sublets.json index fbc81f13..8f6e522b 100644 --- a/backend/tests/market/mock_sublets.json +++ b/backend/tests/market/mock_sublets.json @@ -11,13 +11,13 @@ "price": 1350.0, "negotiable": false, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" }, "address": "Cira Green, Philadelphia, PA", "beds": 3.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00" + "end_date": "3000-05-31T00:00:00-04:00" }, { "item": { @@ -31,12 +31,12 @@ "price": 1350.0, "negotiable": false, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" }, "address": "3901 Locust Walk, Philadelphia, PA", "beds": 4.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00" + "end_date": "3000-05-31T00:00:00-04:00" } ] \ No newline at end of file diff --git a/backend/tests/market/self_user_items.json b/backend/tests/market/self_user_items.json index e34d06de..c12709a2 100644 --- a/backend/tests/market/self_user_items.json +++ b/backend/tests/market/self_user_items.json @@ -8,6 +8,6 @@ "price": 20.0, "negotiable": true, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" } ] \ No newline at end of file diff --git a/backend/tests/market/self_user_sublets.json b/backend/tests/market/self_user_sublets.json index 4cc66a5b..27698b38 100644 --- a/backend/tests/market/self_user_sublets.json +++ b/backend/tests/market/self_user_sublets.json @@ -11,12 +11,12 @@ "price": 1350.0, "negotiable": false, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" }, "address": "Cira Green, Philadelphia, PA", "beds": 3.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00" + "end_date": "3000-05-31T00:00:00-04:00" } ] \ No newline at end of file diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index 41210b4d..8564212a 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -1,4 +1,3 @@ -""" import datetime import json from unittest.mock import MagicMock @@ -6,7 +5,6 @@ import pytz from django.contrib.auth import get_user_model from django.core.files.storage import Storage -from django.db import connection from django.test import TestCase from django.utils.timezone import now from rest_framework.test import APIClient @@ -16,245 +14,279 @@ User = get_user_model() -class TestMarket(TestCase): +class BaseMarketTest(TestCase): def setUp(self): - self.user = User.objects.create_user("user", "user@seas.upenn.edu", "user") self.client = APIClient() - self.client.force_authenticate(self.user) - self.user1 = User.objects.create_user("user1", "user1@seas.upenn.edu", "user1") - self.user2 = User.objects.create_user("user2", "user2@seas.upenn.edu", "user2") - tags = ["New", "Used", "Couch", "Laptop", "Textbook", "Chair", "Apartment", "House"] - categories = ["Book", "Electronics", "Furniture", "Food", "Sublet", "Other"] - Tag.objects.bulk_create([Tag(name=tag) for tag in tags]) - Category.objects.bulk_create([Category(name=category) for category in categories]) + + def load_tags(self): + tags = [ + "New", + "Used", + "Couch", + "Laptop", + "Textbook", + "Chair", + "Apartment", + "House", + ] + tag_objects = Tag.objects.bulk_create([Tag(name=tag) for tag in tags]) + + return tag_objects + + def load_categories(self): + categories = [ + "Book", + "Electronics", + "Furniture", + "Food", + "Sublet", + "Other", + ] + category_objects = Category.objects.bulk_create([Category(name=cat) for cat in categories]) + return category_objects + + def load_user(self, username, email=None, password=None, is_self=False, auth=False): + user = User.objects.create_user(username, email, password) + if is_self: + self.user = user + if auth: + self.client.force_authenticate(user) + return user + + def load_items(self, filepath, user): items = [] - with open("tests/market/self_user_items.json") as data: - data = json.load(data) - for item in data: - item["seller"] = self.user - items.append(item) - with open("tests/market/user_2_items.json") as data: - data = json.load(data) - for item in data: - item["seller"] = user2 - items.append(item) - with open("tests/market/user_1_items.json") as data: + with open(filepath) as data: data = json.load(data) for item in data: - item["seller"] = user1 - items.append(item) - self.items = [] - for item in items: - created_item = Item.objects.create( - seller=item["seller"], - category=Category.objects.get(name=item["category"]), - title=item["title"], - description=item["description"], - price=item["price"], - negotiable=item["negotiable"], - created_at=now(), - expires_at=item["expires_at"], - external_link=item["external_link"], - ) - created_item.tags.set(Tag.objects.filter(name__in=item["tags"])) - created_item.save() - self.items.append(created_item) - sublets = [] - with open("tests/market/self_user_sublets.json") as data: - data = json.load(data) - for sublet in data: - sublet["item"]["seller"] = self.user - sublets.append(sublet) - with open("tests/market/user_2_sublets.json") as data: + created_item = Item.objects.create( + seller=user, + category=Category.objects.get(name=item["category"]), + title=item["title"], + description=item["description"], + price=item["price"], + negotiable=item["negotiable"], + created_at=now(), + expires_at=item["expires_at"], + external_link=item["external_link"], + ) + created_item.tags.set(Tag.objects.filter(name__in=item["tags"])) + created_item.save() + items.append(created_item) + return items + + def load_sublets(self, filepath, user): + items, sublets = [], [] + with open(filepath) as data: data = json.load(data) for sublet in data: - sublet["item"]["seller"] = user2 - sublets.append(sublet) - self.sublets = [] - for sublet in sublets: - created_item = Item.objects.create( - seller=sublet["item"]["seller"], - category=Category.objects.get(name="Sublet"), - title=sublet["item"]["title"], - description=sublet["item"]["description"], - price=sublet["item"]["price"], - negotiable=sublet["item"]["negotiable"], - created_at=now(), - expires_at=sublet["item"]["expires_at"], - external_link=sublet["item"]["external_link"], - ) - created_sublet = Sublet.objects.create( - item=created_item, - address=sublet["address"], - beds=sublet["beds"], - baths=sublet["baths"], - start_date=sublet["start_date"], - end_date=sublet["end_date"], - ) - created_item.tags.set(Tag.objects.filter(name__in=sublet["item"]["tags"])) - created_item.save() - created_sublet.save() - self.sublets.append(created_sublet) - self.item_ids = list(Item.objects.values_list("id", flat=True)) - self.sublet_ids = list(Sublet.objects.values_list("id", flat=True)) - self.user.items_favorited.set(Item.objects.filter(id__in=[1, 2, 3, 6])) - created_offer_1 = Offer.objects.create( - user=self.user, item=item[0], email="self_user@gmail.com" - ) - created_offer_1.save() - created_offer_2 = Offer.objects.create( - user=self.user, item=Item.objects.get(id=5), email="self_user@gmail.com" - ) - created_offer_2.save() - created_offer_3 = Offer.objects.create( - user=self.user1, item=Item.objects.get(id=5), email="user_1@gmail.com" - ) - created_offer_3.save() - created_offer_4 = Offer.objects.create( - user=self.user, item=Item.objects.get(id=4), email="self_user@gmail.com" - ) - created_offer_4.save() - created_offer_5 = Offer.objects.create( - user=self.user1, item=Item.objects.get(id=4), email="user_1@gmail.com" - ) - created_offer_5.save() + created_item = Item.objects.create( + seller=user, + category=Category.objects.get(name="Sublet"), + title=sublet["item"]["title"], + description=sublet["item"]["description"], + price=sublet["item"]["price"], + negotiable=sublet["item"]["negotiable"], + created_at=now(), + expires_at=sublet["item"]["expires_at"], + external_link=sublet["item"]["external_link"], + ) + created_sublet = Sublet.objects.create( + item=created_item, + address=sublet["address"], + beds=sublet["beds"], + baths=sublet["baths"], + start_date=sublet["start_date"], + end_date=sublet["end_date"], + ) + created_item.tags.set(Tag.objects.filter(name__in=sublet["item"]["tags"])) + created_item.save() + created_sublet.save() + items.append(created_item) + sublets.append(created_sublet) + return items, sublets - storage_mock = MagicMock(spec=Storage, name="StorageMock") - storage_mock.generate_filename = lambda filename: filename - storage_mock.save = MagicMock(side_effect=lambda name, *args, **kwargs: name) - storage_mock.url = MagicMock(name="url") - storage_mock.url.return_value = "http://penn-mobile.com/mock-image.png" - ItemImage._meta.get_field("image").storage = storage_mock + def assert_dict_equal_ignoring_keys(self, actual, expected, ignored_keys=(), unordered_keys=()): + ignored = set(ignored_keys) + unordered = set(unordered_keys) + + def sort_key(x): + if isinstance(x, dict): + return json.dumps(x, sort_keys=True) + else: + return str(x) + + def normalize(obj, path=""): + if isinstance(obj, dict): + out = {} + for k, v in obj.items(): + p = f"{path}{k}" + if p in ignored: + continue + val = normalize(v, p + ".") + if p in unordered and isinstance(val, list): + val = sorted(val, key=sort_key) + out[k] = val + return out + + elif isinstance(obj, list): + items = [normalize(e, path) for e in obj] + if path.rstrip(".") in unordered: + items = sorted(items, key=sort_key) + return items + + return obj + + self.assertEqual(normalize(actual), normalize(expected)) + + +class TestItemGet(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) def test_get_items(self): response = self.client.get("/market/items/") expected_response = [ { - "id": 1, - "seller": 1, + "id": self.items[0].id, + "seller": self.users[0].id, "tags": ["Textbook", "Used"], "category": "Book", "title": "Math Textbook", "price": 20.0, - "expires_at": "2025-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 1, - }, - { - "id": 2, - "seller": 3, - "tags": ["New"], - "category": "Food", - "title": "Bag of Doritos", - "price": 5.0, - "expires_at": "2025-10-12T01:00:00-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], - "favorite_count": 1, + "favorite_count": 0, }, { - "id": 3, - "seller": 2, + "id": self.items[1].id, + "seller": self.users[1].id, "tags": ["Laptop", "New"], "category": "Electronics", "title": "Macbook Pro", "price": 2000.0, - "expires_at": "2025-08-12T01:00:00-04:00", + "expires_at": "3000-08-12T01:00:00-04:00", "images": [], - "favorite_count": 1, + "favorite_count": 0, }, { - "id": 4, - "seller": 2, + "id": self.items[2].id, + "seller": self.users[1].id, "tags": ["Couch"], "category": "Furniture", "title": "Couch", "price": 400.0, - "expires_at": "2025-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], "favorite_count": 0, }, ] self.assertEqual(response.status_code, 200) - self.assertEqual( - sorted(response.json(), key=lambda d: d["id"]), - sorted(expected_response, key=lambda d: d["id"]), + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, [], ["", "tags", "images"] ) def test_get_item_seller(self): response = self.client.get("/market/items/?seller=true") expected_response = [ { - "id": 1, - "seller": 1, + "id": self.items[0].id, + "seller": self.users[0].id, "tags": ["Textbook", "Used"], "category": "Book", "title": "Math Textbook", "price": 20.0, - "expires_at": "2025-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], - "favorite_count": 1, + "favorite_count": 0, } ] self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, [], ["", "tags", "images"] + ) def test_get_single_item_own(self): - response = self.client.get("/market/items/1/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.get(f"/market/items/{self.items[0].id}/") + response_json = response.json() expected_response = { - "id": 1, + "id": self.items[0].id, "images": [], "title": "Math Textbook", "description": "2023 version", "external_link": "https://example.com/book", "price": 20.0, "negotiable": True, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "created_at": "2025-09-04T14:01:34.530659-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, "category": "Book", - "buyers": [1], + "buyers": [], "tags": ["Textbook", "Used"], - "favorites": [1], + "favorites": [], } self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response_json, + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response_json["created_at"]) + - datetime.datetime.now(pytz.UTC) + ), datetime.timedelta(minutes=10), ) def test_get_single_item_other(self): - response = self.client.get("/market/items/2/") + response = self.client.get(f"/market/items/{self.items[1].id}/") expected_response = { - "id": 2, - "seller": 3, + "id": self.items[1].id, + "seller": self.users[1].id, "buyer_count": 0, - "tags": ["New"], - "category": "Food", - "title": "Bag of Doritos", - "description": "Cool Ranch", - "external_link": "https://example.com/doritos", - "price": 5.0, - "negotiable": False, - "expires_at": "2025-10-12T01:00:00-04:00", + "tags": ["Laptop", "New"], + "category": "Electronics", + "title": "Macbook Pro", + "description": "M1 Chip", + "external_link": "https://example.com/macbook", + "price": 2000.0, + "negotiable": True, + "expires_at": "3000-08-12T01:00:00-04:00", "images": [], - "favorite_count": 1, + "favorite_count": 0, } self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, [], ["", "tags", "images"] + ) - def test_get_item_of_sublet(self): - response = self.client.get("/market/items/5/") - self.assertEqual(response.status_code, 404) - self.assertEqual(response.json(), {"detail": "No Item matches the given query."}) + +class TestItemPost(BaseMarketTest): + + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] def test_create_item_all_fields(self): payload = { "id": 88, - "seller": 2, + "seller": self.users[1].id, "buyers": [], "tags": ["New"], "category": "Book", @@ -264,42 +296,53 @@ def test_create_item_all_fields(self): "price": 20.0, "negotiable": True, "created_at": "2024-11-26T00:50:03.217587-05:00", - "expires_at": "2025-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], } response = self.client.post("/market/items/", payload, format="json") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - expected_response_without_created_at = { - "id": 7, - "seller": 1, - "buyers": [], - "tags": ["New"], - "category": "Book", + expected_response = { + "id": int(f"{response.json()['id']}"), + "images": [], "title": "Math Textbook", "description": "2023 version", "external_link": "https://example.com/listing", "price": 20.0, "negotiable": True, - "expires_at": "2025-12-12T00:00:00-05:00", - "images": [], + "created_at": "2025-09-04T15:00:58.895709-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["New"], "favorites": [], } self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response_without_created_at) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.UTC) + ), datetime.timedelta(minutes=10), ) - response = self.client.get("/market/items/7/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.get(f"/market/items/{response.json()['id']}/") self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response_without_created_at) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) @@ -311,57 +354,67 @@ def test_create_item_exclude_unrequired(self): "description": "2023 version", "price": 20.0, "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", } - response = self.client.post("/market/items/", payload) - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - expected_response_without_created_at = { - "id": 7, - "seller": 1, - "buyers": [], - "tags": ["New"], - "category": "Book", + response = self.client.post("/market/items/", payload, format="json") + expected_response = { + "id": int(f"{response.json()['id']}"), + "images": [], "title": "Math Textbook", "description": "2023 version", "external_link": None, "price": 20.0, "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", - "images": [], + "created_at": "2025-09-04T15:00:58.895709-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["New"], "favorites": [], } self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response_without_created_at) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.UTC) + ), datetime.timedelta(minutes=10), ) - response = self.client.get("/market/items/7/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.get(f"/market/items/{response.json()['id']}/") self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response_without_created_at) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) - def test_create_item_missing_filed(self): + def test_create_item_missing_field(self): payload = { "tags": ["New"], "category": "Book", "external_link": "https://example.com/listing", "price": 20.0, "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", } response = self.client.post("/market/items/", payload) - res_json = json.loads(response.content) self.assertEqual(response.status_code, 400) - self.assertEqual(res_json, {"title": ["This field is required."]}) + self.assertEqual(response.json(), {"title": ["This field is required."]}) def test_create_item_invalid_category(self): payload = { @@ -372,28 +425,15 @@ def test_create_item_invalid_category(self): "external_link": "https://example.com/listing", "price": 20.0, "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", - } - response = self.client.post("/market/items/", payload) - res_json = json.loads(response.content) - self.assertEqual(response.status_code, 400) - self.assertEqual(res_json, {"category": ['Invalid pk "Textbook" - object does not exist.']}) - - def test_create_item_invalid_tag(self): - payload = { - "tags": ["Not a tag"], - "category": "Book", - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", } response = self.client.post("/market/items/", payload) res_json = json.loads(response.content) self.assertEqual(response.status_code, 400) - self.assertEqual(res_json, {"tags": ['Invalid pk "Not a tag" - object does not exist.']}) + self.assertEqual( + res_json, + {"category": ['Invalid pk "Textbook" - object does not exist.']}, + ) def test_create_item_with_profanity_title(self): payload = { @@ -404,7 +444,7 @@ def test_create_item_with_profanity_title(self): "external_link": "https://example.com/listing", "price": 20.0, "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", } response = self.client.post("/market/items/", payload) self.assertEqual(response.status_code, 400) @@ -414,95 +454,81 @@ def test_create_item_with_profanity_title(self): self.assertIn("description", res_json) self.assertEqual(res_json["title"][0], "The title contains inappropriate language.") self.assertEqual( - res_json["description"][0], "The description contains inappropriate language." + res_json["description"][0], + "The description contains inappropriate language.", ) - def test_create_item_sublet_category(self): - payload = { - "tags": ["New"], - "category": "Sublet", - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", - } - response = self.client.post("/market/items/", payload) - res_json = json.loads(response.content) - response_without_created_at = res_json.copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - expected_response_without_created_at = { - "id": 7, - "seller": 1, - "buyers": [], - "tags": ["New"], - "category": "Sublet", - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "expires_at": "2024-12-12T00:00:00-05:00", - "images": [], - "favorites": [], - } - self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response_without_created_at) - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=10), - ) + +class TestItemPatch(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) def test_update_item_minimum_required(self): - # All fields included are strictly required. payload = { "category": "Book", "title": "Physics Textbook", "price": 25.0, - "expires_at": "2024-12-13T00:00:00-05:00", + "expires_at": "3000-12-13T00:00:00-05:00", } - response = self.client.patch("/market/items/1/", payload) - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) expected_response = { - "id": 1, - "seller": 1, - "buyers": [1], - "tags": ["Textbook", "Used"], + "id": self.items[0].id, + "seller": self.users[0].id, + "buyers": [], + "tags": ["Used", "Textbook"], "category": "Book", "title": "Physics Textbook", "description": "2023 version", "external_link": "https://example.com/book", "price": 25.0, "negotiable": True, - "expires_at": "2024-12-13T00:00:00-05:00", + "expires_at": "3000-12-13T00:00:00-05:00", "images": [], - "favorites": [1], + "favorites": [], } self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "favorites", "buyers"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) - response = self.client.get("/market/items/1/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.get(f"/market/items/{self.items[0].id}/") self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "favorites", "buyers"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) def test_update_item_all_fields(self): payload = { "id": 7, - "seller": 1, + "seller": self.users[1].id, "buyers": [], "tags": ["New"], "category": "Food", @@ -512,19 +538,14 @@ def test_update_item_all_fields(self): "price": 25.0, "negotiable": False, "created_at": "2024-11-26T00:50:03.217587-05:00", - "expires_at": "2024-12-14T00:00:00-05:00", + "expires_at": "3000-12-14T00:00:00-05:00", "images": [], } - original_created_at = Item.objects.get(id=1).created_at - response = self.client.patch("/market/items/1/", payload, format="json") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - self.assertEqual(response.status_code, 200) + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) expected_response = { - "id": 1, - "seller": 1, - "buyers": [1], + "id": self.items[0].id, + "seller": self.users[0].id, + "buyers": [], "tags": ["New"], "category": "Food", "title": "5 meal swipes", @@ -532,31 +553,54 @@ def test_update_item_all_fields(self): "external_link": "https://example.com/meal-swipes", "price": 25.0, "negotiable": False, - "expires_at": "2024-12-14T00:00:00-05:00", + "expires_at": "3000-12-14T00:00:00-05:00", "images": [], - "favorites": [1], + "favorites": [], } - self.assertEqual(response_without_created_at, expected_response) - self.assertEqual(original_created_at, created_at) - response = self.client.get("/market/items/1/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/items/{self.items[0].id}/") self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) - self.assertEqual(original_created_at, created_at) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) def test_update_item_invalid_category(self): payload = { "category": "Textbook", "title": "Physics Textbook", "price": 25.0, - "expires_at": "2024-12-13T00:00:00-05:00", + "expires_at": "3000-12-13T00:00:00-05:00", } - response = self.client.patch("/market/items/1/", payload) - res_json = json.loads(response.content) + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) self.assertEqual(response.status_code, 400) - self.assertEqual(res_json, {"category": ['Invalid pk "Textbook" - object does not exist.']}) + self.assertEqual( + response.json(), + {"category": ['Invalid pk "Textbook" - object does not exist.']}, + ) def test_update_item_invalid_tag(self): payload = { @@ -564,45 +608,98 @@ def test_update_item_invalid_tag(self): "category": "Book", "title": "Physics Textbook", "price": 25.0, - "expires_at": "2024-12-13T00:00:00-05:00", + "expires_at": "3000-12-13T00:00:00-05:00", } - response = self.client.patch("/market/items/1/", payload) - res_json = json.loads(response.content) + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) self.assertEqual(response.status_code, 400) - self.assertEqual(res_json, {"tags": ['Invalid pk "Not a tag" - object does not exist.']}) + self.assertEqual( + response.json(), + {"tags": ['Invalid pk "Not a tag" - object does not exist.']}, + ) def test_update_item_to_sublet(self): - payload = {"category": "Sublet"} - response = self.client.patch("/market/items/1/", payload) - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.patch(f"/market/items/{self.items[0].id}/", {"category": "Sublet"}) expected_response = { - "id": 1, + "id": self.items[0].id, "images": [], "title": "Math Textbook", "description": "2023 version", "external_link": "https://example.com/book", "price": 20.0, "negotiable": True, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, "category": "Sublet", - "buyers": [1], + "buyers": [], "tags": ["Textbook", "Used"], - "favorites": [1], + "favorites": [], } self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "buyers", "favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) + def test_update_item_not_owned(self): + payload = {"title": "New Title"} + response = self.client.patch(f"/market/items/{self.items[1].id}/", payload, format="json") + self.assertEqual(response.status_code, 403) + self.assertEqual( + response.json(), + {"detail": "You do not have permission to perform this action."}, + ) + + +class TestItemDelete(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + def test_delete_item(self): - response = self.client.delete("/market/items/1/") + self.assertTrue(Item.objects.filter(id=self.items[0].id).exists()) + response = self.client.delete(f"/market/items/{self.items[0].id}/") self.assertEqual(response.status_code, 204) - self.assertFalse(Item.objects.filter(id=1).exists()) + self.assertFalse(Item.objects.filter(id=self.items[0].id).exists()) + + def test_delete_item_not_owned(self): + response = self.client.delete(f"/market/items/{self.items[1].id}/") + self.assertEqual(response.status_code, 403) + self.assertEqual( + response.json(), + {"detail": "You do not have permission to perform this action."}, + ) + + +class TestSubletGet(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) + items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) + self.items = items1 + items2 + self.sublets = sublets1 + sublets2 def test_get_sublets(self): response = self.client.get("/market/sublets/") @@ -610,15 +707,15 @@ def test_get_sublets(self): self.assertEqual(len(response.json()), 2) expected_response = [ { - "id": 1, + "id": self.sublets[0].id, "item": { - "id": 5, - "seller": 1, + "id": self.items[0].id, + "seller": self.users[0].id, "tags": ["New"], "category": "Sublet", "title": "Cira Green Sublet", "price": 1350.0, - "expires_at": "2025-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], "favorite_count": 0, }, @@ -626,56 +723,54 @@ def test_get_sublets(self): "beds": 3.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", + "end_date": "3000-05-31T00:00:00-04:00", }, { - "id": 2, + "id": self.sublets[1].id, "item": { - "id": 6, - "seller": 3, + "id": self.items[1].id, + "seller": self.users[1].id, "tags": ["New"], "category": "Sublet", "title": "Rodin Quad", "price": 1350.0, - "expires_at": "2025-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], - "favorite_count": 1, + "favorite_count": 0, }, "address": "3901 Locust Walk, Philadelphia, PA", "beds": 4.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", + "end_date": "3000-05-31T00:00:00-04:00", }, ] - self.assertEqual( - sorted(response.json(), key=lambda d: d["id"]), - sorted(expected_response, key=lambda d: d["id"]), + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], ) def test_get_sublet_own(self): - response = self.client.get("/market/sublets/1/") + response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") self.assertEqual(response.status_code, 200) - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) expected_response = { - "id": 1, + "id": self.sublets[0].id, "item": { - "id": 5, + "id": self.items[0].id, "images": [], "title": "Cira Green Sublet", - "description": ( - "Fully furnished 3-bedroom apartment" - " available for sublet with all amenities included." - ), + "description": "Fully furnished 3-bedroom apartment available for sublet with all" + + " amenities included.", "external_link": "https://example.com/cira-green", "price": 1350.0, "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "created_at": "2025-09-05T00:14:15.141807-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, "category": "Sublet", - "buyers": [1, 2], + "buyers": [], "tags": ["New"], "favorites": [], }, @@ -683,118 +778,72 @@ def test_get_sublet_own(self): "beds": 3.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", + "end_date": "3000-05-31T00:00:00-04:00", } - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["", "item.tags", "item.images", "item.buyers", "item.favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) def test_get_sublet_other(self): - response = self.client.get("/market/sublets/2/") + response = self.client.get(f"/market/sublets/{self.sublets[1].id}/") self.assertEqual(response.status_code, 200) expected_response = { - "id": 2, + "id": self.sublets[1].id, "item": { - "id": 6, - "seller": 3, + "id": self.items[1].id, + "seller": self.users[1].id, "buyer_count": 0, "tags": ["New"], "category": "Sublet", "title": "Rodin Quad", - "description": ( - "Fully furnished 4-bedroom apartment available" - " for sublet with all amenities included." - ), + "description": "Fully furnished 3-bedroom apartment available for sublet with all" + + " amenities included.", + "external_link": "https://example.com/rodin-quad", "price": 1350.0, "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", "images": [], - "favorite_count": 1, - "external_link": "https://example.com/rodin-quad", + "favorite_count": 0, }, "address": "3901 Locust Walk, Philadelphia, PA", "beds": 4.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", + "end_date": "3000-05-31T00:00:00-04:00", } - self.assertEqual(response.json(), expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["", "item.tags", "item.images", "item.buyers", "item.favorites"], + ) def test_get_single_sublet_invalid_id(self): - response = self.client.get("/market/sublets/3/") + response = self.client.get(f"/market/sublets/{self.sublets[1].id+1}/") self.assertEqual(response.status_code, 404) - def test_create_sublet_required_fields(self): - payload = { - "item": { - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment available" - " for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "category": "Sublet", - "tags": ["New"], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - response = self.client.post("/market/sublets/", payload, format="json") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - expected_response = { - "id": 3, - "item": { - "id": 7, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment" - " available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, - "category": "Sublet", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response) - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=10), - ) - response = self.client.get("/market/sublets/3/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=10), - ) - def test_create_sublet_all_fields(self): +class TestSubletPost(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + + def test_create_sublet(self): payload = { "id": 3, "item": { @@ -809,7 +858,7 @@ def test_create_sublet_all_fields(self): "price": 1350.0, "negotiable": False, "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "seller": self.users[0].id, "category": "Sublet", "buyers": [], "tags": ["New"], @@ -819,27 +868,23 @@ def test_create_sublet_all_fields(self): "beds": 4.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", + "end_date": "3000-05-31T00:00:00-04:00", } response = self.client.post("/market/sublets/", payload, format="json") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) expected_response = { - "id": 3, + "id": response.json()["id"], "item": { - "id": 7, + "id": response.json()["item"]["id"], "images": [], "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), + "description": "Fully furnished 3-bedroom apartment available for sublet with all" + + " amenities included.", "external_link": "https://example.com/listing", "price": 1350.0, "negotiable": False, + "created_at": "2025-09-05T00:48:08.880144-04:00", "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "seller": self.users[0].id, "category": "Sublet", "buyers": [], "tags": ["New"], @@ -849,123 +894,53 @@ def test_create_sublet_all_fields(self): "beds": 4.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", + "end_date": "3000-05-31T00:00:00-04:00", } self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response) - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=10), + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], ) - response = self.client.get("/market/sublets/3/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) - - def test_create_sublet_invalid_category(self): - payload = { - "id": 3, - "item": { - "id": 7, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment" - " available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, - "category": "Invalid", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - response = self.client.post("/market/sublets/", payload, format="json") - self.assertEqual(response.status_code, 400) - self.assertEqual( + response = self.client.get(f"/market/sublets/{response.json()['id']}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( response.json(), - {"item": {"category": ['Invalid pk "Invalid" - object does not exist.']}}, + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], ) - - def test_create_sublet_non_sublet_category(self): - payload = { - "id": 3, - "item": { - "id": 7, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, - "category": "Book", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - response = self.client.post("/market/sublets/", payload, format="json") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) - expected_response = { - "id": 3, - "item": { - "id": 7, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, - "category": "Book", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) + +class TestSubletPatchDelete(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) + items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) + self.items = items1 + items2 + self.sublets = sublets1 + sublets2 + def test_update_sublet(self): payload = { "id": 3, @@ -981,10 +956,10 @@ def test_update_sublet(self): "price": 1350.0, "negotiable": False, "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "seller": self.users[0].id, "category": "Sublet", "buyers": [], - "tags": ["New"], + "tags": ["Apartment", "New"], "favorites": [], }, "address": "3901 Locust Walk, Philadelphia, PA", @@ -993,14 +968,13 @@ def test_update_sublet(self): "start_date": "2024-01-01T00:00:00-05:00", "end_date": "2025-05-31T00:00:00-04:00", } - response = self.client.patch("/market/sublets/1/", payload, format="json") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.patch( + f"/market/sublets/{self.sublets[0].id}/", payload, format="json" + ) expected_response = { - "id": 1, + "id": int(self.sublets[0].id), "item": { - "id": 5, + "id": int(self.items[0].id), "images": [], "title": "Cira Green Sublet 2", "description": ( @@ -1011,10 +985,10 @@ def test_update_sublet(self): "price": 1350.0, "negotiable": False, "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "seller": self.users[0].id, "category": "Sublet", - "buyers": [1, 2], - "tags": ["New"], + "buyers": [], + "tags": ["New", "Apartment"], "favorites": [], }, "address": "3901 Locust Walk, Philadelphia, PA", @@ -1024,27 +998,38 @@ def test_update_sublet(self): "end_date": "2025-05-31T00:00:00-04:00", } self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) - response = self.client.get("/market/sublets/1/") - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), datetime.timedelta(minutes=10), ) def test_update_sublet_non_sublet_category(self): payload = { - "id": 3, "item": { - "id": 7, "images": [], "title": "Cira Green Sublet 2", "description": ( @@ -1055,7 +1040,7 @@ def test_update_sublet_non_sublet_category(self): "price": 1350.0, "negotiable": False, "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "seller": self.users[0].id, "category": "Book", "buyers": [], "tags": ["New"], @@ -1067,11 +1052,13 @@ def test_update_sublet_non_sublet_category(self): "start_date": "2024-01-01T00:00:00-05:00", "end_date": "2025-05-31T00:00:00-04:00", } - response = self.client.patch("/market/sublets/1/", payload, format="json") + response = self.client.patch( + f"/market/sublets/{self.sublets[0].id}/", payload, format="json" + ) expected_response = { - "id": 1, + "id": self.sublets[0].id, "item": { - "id": 5, + "id": self.items[0].id, "images": [], "title": "Cira Green Sublet 2", "description": ( @@ -1082,9 +1069,9 @@ def test_update_sublet_non_sublet_category(self): "price": 1350.0, "negotiable": False, "expires_at": "2025-12-12T00:00:00-05:00", - "seller": 1, + "seller": self.users[0].id, "category": "Book", - "buyers": [1, 2], + "buyers": [], "tags": ["New"], "favorites": [], }, @@ -1094,313 +1081,248 @@ def test_update_sublet_non_sublet_category(self): "start_date": "2024-01-01T00:00:00-05:00", "end_date": "2025-05-31T00:00:00-04:00", } - response_without_created_at = response.json().copy() - created_at = response_without_created_at["item"].pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) - - def test_delete_sublet(self): - response = self.client.delete("/market/sublets/1/") - self.assertEqual(response.status_code, 204) - self.assertFalse(Sublet.objects.filter(id=1).exists()) - self.assertFalse(Item.objects.filter(id=5).exists()) - - def test_get_all_tags(self): - response = self.client.get("/market/tags/") - self.assertEqual(response.status_code, 200) - self.assertEqual( + self.assert_dict_equal_ignoring_keys( response.json(), - ["New", "Used", "Couch", "Laptop", "Textbook", "Chair", "Apartment", "House"], + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], ) - - def test_get_all_categories(self): - response = self.client.get("/market/categories/") - self.assertEqual(response.status_code, 200) - self.assertEqual( - response.json(), ["Book", "Electronics", "Furniture", "Food", "Sublet", "Other"] + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), ) - def test_get_all_user_favorites(self): - response = self.client.get("/market/favorites/") - expected_response = [ - { - "id": 1, - "seller": 1, - "tags": ["Textbook", "Used"], - "category": "Book", - "title": "Math Textbook", - "price": 20.0, - "expires_at": "2025-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 1, - }, - { - "id": 2, - "seller": 3, - "tags": ["New"], - "category": "Food", - "title": "Bag of Doritos", - "price": 5.0, - "expires_at": "2025-10-12T01:00:00-04:00", - "images": [], - "favorite_count": 1, - }, - { - "id": 3, - "seller": 2, - "tags": ["Laptop", "New"], - "category": "Electronics", - "title": "Macbook Pro", - "price": 2000.0, - "expires_at": "2025-08-12T01:00:00-04:00", - "images": [], - "favorite_count": 1, - }, - { - "id": 6, - "seller": 3, - "tags": ["New"], - "category": "Sublet", - "title": "Rodin Quad", - "price": 1350.0, - "expires_at": "2025-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 1, - }, + +class TestOffer(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = super().load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) + items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) + self.items += items1 + items2 + self.sublets = sublets1 + sublets2 + self.offers = [ + Offer.objects.create( + user=self.users[0], + item=self.items[1], + email="self_user@gmail.com", + ), + Offer.objects.create( + user=self.users[0], + item=self.items[4], + email="self_user@gmail.com", + phone_number="+15555555555", + message="I want this", + ), + Offer.objects.create( + user=self.users[1], + item=self.items[0], + email="user1@gmail.com", + phone_number="+15555555555", + message="", + ), ] - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), expected_response) def test_get_all_user_offers(self): response = self.client.get("/market/offers/made/") - response_without_created_at = [offer.copy() for offer in response.json()] - created_at_list = [ - datetime.datetime.fromisoformat(offer.pop("created_at")) - for offer in response_without_created_at - ] - expected_response = [ { - "id": 1, + "id": self.offers[0].id, "phone_number": None, "email": "self_user@gmail.com", "message": "", - "user": 1, - "item": 1, + "user": self.users[0].id, + "item": self.items[1].id, }, { - "id": 2, - "phone_number": None, + "id": self.offers[1].id, + "phone_number": "+15555555555", "email": "self_user@gmail.com", - "message": "", - "user": 1, - "item": 5, - }, - { - "id": 4, - "phone_number": None, - "email": "self_user@gmail.com", - "message": "", - "user": 1, - "item": 4, + "message": "I want this", + "user": self.users[0].id, + "item": self.items[4].id, }, ] self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) - for created_at in created_at_list: - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=1), - ) - - def test_get_all_user_offers_received(self): - response = self.client.get("/market/offers/received/") - response_without_created_at = [offer.copy() for offer in response.json()] - created_at_list = [ - datetime.datetime.fromisoformat(offer.pop("created_at")) - for offer in response_without_created_at - ] + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, ["created_at"], [""] + ) - expected_response = [ - { - "id": 1, - "phone_number": None, - "email": "self_user@gmail.com", - "message": "", - "user": 1, - "item": 1, - }, - { - "id": 2, - "phone_number": None, - "email": "self_user@gmail.com", - "message": "", - "user": 1, - "item": 5, - }, + def test_list_item_offers(self): + response = self.client.get(f"/market/items/{self.items[0].id}/offers/") + expected = [ { - "id": 3, - "phone_number": None, - "email": "user_1@gmail.com", + "id": self.offers[2].id, + "phone_number": "+15555555555", + "email": "user1@gmail.com", "message": "", - "user": 2, - "item": 5, - }, - ] - self.assertEqual(response.status_code, 200) - self.assertEqual(response_without_created_at, expected_response) - for created_at in created_at_list: - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=1), - ) - - def test_favorite_item(self): - response = self.client.post("/market/items/4/favorites/") - self.assertEqual(response.status_code, 201) - self.assertTrue(Item.objects.get(id=1).favorites.filter(id=1).exists()) - - def test_favorite_favorited_item(self): - response = self.client.post("/market/items/1/favorites/") - self.assertEqual(response.status_code, 406) - self.assertEqual(response.json(), {"detail": "Favorite already exists"}) - - def test_unfavorite_item(self): - response = self.client.delete("/market/items/1/favorites/") - self.assertEqual(response.status_code, 204) - self.assertFalse(Item.objects.get(id=1).favorites.filter(id=1).exists()) - - def test_unfavorite_unfavorited_item(self): - response = self.client.delete("/market/items/4/favorites/") - self.assertEqual(response.status_code, 404) - self.assertEqual(response.json(), {"detail": "No Item matches the given query."}) - - def test_favorite_invalid_item(self): - response = self.client.post("/market/items/100/favorites/") - self.assertEqual(response.status_code, 404) - - def test_favorite_sublet(self): - response = self.client.post("/market/items/5/favorites/") - self.assertEqual(response.status_code, 201) - self.assertTrue(Item.objects.get(id=6).favorites.filter(id=1).exists()) - - def test_favorite_favorited_sublet(self): - response = self.client.post("/market/items/1/favorites/") - self.assertEqual(response.status_code, 406) - self.assertEqual(response.json(), {"detail": "Favorite already exists"}) - - def test_list_item_offers(self): - response = self.client.get("/market/items/1/offers/") - response_without_created_at = response.json().copy() - created_at_list = [ - datetime.datetime.fromisoformat(offer.pop("created_at")) - for offer in response_without_created_at + "user": self.users[1].id, + "item": self.items[0].id, + } ] self.assertEqual(response.status_code, 200) - self.assertEqual( - response_without_created_at, - [ - { - "id": 1, - "phone_number": None, - "email": "self_user@gmail.com", - "message": "", - "user": 1, - "item": 1, - } - ], - ) - for created_at in created_at_list: - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=1), - ) + self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) - def test_list_item_offers_other(self): - response = self.client.get("/market/items/4/offers/") + def test_get_offer_other(self): + response = self.client.get(f"/market/items/{self.items[1].id}/offers/") + expected = {"detail": "You do not have permission to perform this action."} self.assertEqual(response.status_code, 403) - self.assertEqual( - response.json(), {"detail": "You do not have permission to perform this action."} - ) + self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) def test_list_item_offers_invalid_item(self): - response = self.client.get("/market/items/100/offers/") + invalid_id = 1 + while Item.objects.filter(id=invalid_id).exists(): + invalid_id += 1 + response = self.client.get(f"/market/items/{invalid_id}/offers/") self.assertEqual(response.status_code, 404) self.assertEqual(response.json(), {"detail": "No Item matches the given query"}) - def test_list_item_delete_and_offers_nonexistent(self): - self.client.delete("/market/items/1/offers/") - response = self.client.get("/market/items/1/offers/") - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), []) - - def test_create_offer(self): + def test_create_offer_all_fields(self): payload = { - "phone_number": "+1 (425)-269-4412", + "phone_number": "+1 (202) 555 0100", "email": "self_user@gmail.com", - "message": "I am interested in buying this item.", + "message": "I want this", } - response = self.client.post("/market/items/2/offers/", payload) - response_without_created_at = response.json().copy() - created_at = response_without_created_at.pop("created_at") - created_at = datetime.datetime.fromisoformat(created_at) + response = self.client.post( + f"/market/items/{self.items[2].id}/offers/", payload, format="json" + ) expected_response = { - "id": 6, - "phone_number": "+14252694412", + "id": response.json()["id"], + "phone_number": "+12025550100", "email": "self_user@gmail.com", - "message": "I am interested in buying this item.", - "user": 1, - "item": 2, + "message": "I want this", + "user": self.users[0].id, + "item": 3, } self.assertEqual(response.status_code, 201) - self.assertEqual(response_without_created_at, expected_response) - self.assertLessEqual( - abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), - datetime.timedelta(minutes=10), + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, ["created_at"], [""] + ) + + def test_create_offer_required_fields(self): + payload = {"phone_number": "+12025550100"} + response = self.client.post( + f"/market/items/{self.items[2].id}/offers/", payload, format="json" + ) + expected_response = { + "id": response.json()["id"], + "phone_number": "+12025550100", + "email": None, + "message": "", + "user": self.users[0].id, + "item": 3, + } + self.assertEqual(response.status_code, 201) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, ["created_at"], [""] + ) + + def test_create_offer_invalid(self): + payload = {} + response = self.client.post( + f"/market/items/{self.items[2].id}/offers/", payload, format="json" ) + expected_response = {"phone_number": ["This field is required."]} + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), expected_response) + + def test_create_offer_existing(self): + payload = { + "phone_number": "+1 (202) 555 0100", + "email": "self_user@gmail.com", + "message": "I want this", + } + response = self.client.post( + f"/market/items/{self.items[1].id}/offers/", payload, format="json" + ) + expected_response = ["Offer already exists"] + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), expected_response) def test_delete_offer(self): - response = self.client.delete("/market/items/1/offers/") + self.assertTrue(Offer.objects.filter(id=1).exists()) + response = self.client.delete(f"/market/items/{self.items[1].id}/offers/") self.assertEqual(response.status_code, 204) self.assertFalse(Offer.objects.filter(id=1).exists()) def test_delete_offer_nonexistent(self): - response = self.client.delete("/market/items/6/offers/") + invalid_id = 1 + while Offer.objects.filter(id=invalid_id).exists(): + invalid_id += 1 + response = self.client.delete(f"/market/items/{invalid_id}/offers/") self.assertEqual(response.status_code, 404) + +class TestImages(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + + storage_mock = MagicMock(spec=Storage, name="StorageMock") + storage_mock.generate_filename = lambda filename: filename + storage_mock.save = MagicMock(side_effect=lambda name, *args, **kwargs: name) + storage_mock.url = MagicMock(name="url") + storage_mock.url.return_value = "http://penn-mobile.com/mock-image.png" + ItemImage._meta.get_field("image").storage = storage_mock + def test_create_image(self): with open("tests/market/mock_image.jpg", "rb") as image: - response = self.client.post("/market/items/1/images/", {"images": image}) + self.assertEqual(Item.objects.get(id=self.items[0].id).images.count(), 0) + response = self.client.post( + f"/market/items/{self.items[0].id}/images/", {"images": image} + ) self.assertEqual(response.status_code, 201) - images = Item.objects.get(id=1).images.all() - self.assertTrue(images.exists()) - self.assertEqual(1, images.first().item.id) + self.assertEqual(Item.objects.get(id=self.items[0].id).images.count(), 1) + img = Item.objects.get(id=self.items[0].id).images.first() + self.assertIsNotNone(img) def test_create_image_other_users_item(self): with open("tests/market/mock_image.jpg", "rb") as image: - response = self.client.post("/market/items/2/images/", {"images": image}) + response = self.client.post( + f"/market/items/{self.items[1].id}/images/", {"images": image} + ) self.assertEqual(response.status_code, 403) self.assertEqual( - response.json(), {"detail": "You do not have permission to perform this action."} + response.json(), + {"detail": "You do not have permission to perform this action."}, ) def test_create_delete_images(self): with open("tests/market/mock_image.jpg", "rb") as image: with open("tests/market/mock_image.jpg", "rb") as image2: response = self.client.post( - "/market/items/1/images/", + f"/market/items/{self.items[0].id}/images/", {"images": [image, image2]}, "multipart", ) + saved_images = response.json() self.assertEqual(response.status_code, 201) - images = Item.objects.get(id=1).images.all() + images = Item.objects.get(id=self.items[0].id).images.all() self.assertTrue(images.exists()) self.assertEqual(2, images.count()) - self.assertEqual(1, images.first().item.id) - response = self.client.delete("/market/items/images/1/") + self.assertEqual(self.items[0].id, images.first().item.id) + response = self.client.delete(f"/market/items/images/{saved_images[0]['id']}/") self.assertEqual(response.status_code, 204) - self.assertFalse(ItemImage.objects.filter(id=1).exists()) - self.assertTrue(ItemImage.objects.filter(id=2).exists()) + self.assertFalse(ItemImage.objects.filter(id=saved_images[0]["id"]).exists()) + self.assertTrue(ItemImage.objects.filter(id=saved_images[1]["id"]).exists()) self.assertEqual(1, ItemImage.objects.all().count()) -""" diff --git a/backend/tests/market/test_permissions.py b/backend/tests/market/test_permissions.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/tests/market/user_1_items.json b/backend/tests/market/user_1_items.json index 18ce519a..cce6ddc7 100644 --- a/backend/tests/market/user_1_items.json +++ b/backend/tests/market/user_1_items.json @@ -8,7 +8,7 @@ "price": 2000.0, "negotiable": true, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-08-12T00:00:00-05:00" + "expires_at": "3000-08-12T00:00:00-05:00" }, { "tags": ["Couch"], @@ -19,6 +19,6 @@ "price": 400.0, "negotiable": true, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" } ] \ No newline at end of file diff --git a/backend/tests/market/user_2_sublets.json b/backend/tests/market/user_1_sublets.json similarity index 85% rename from backend/tests/market/user_2_sublets.json rename to backend/tests/market/user_1_sublets.json index 79105054..361d0388 100644 --- a/backend/tests/market/user_2_sublets.json +++ b/backend/tests/market/user_1_sublets.json @@ -11,12 +11,12 @@ "price": 1350.0, "negotiable": false, "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-12-12T00:00:00-05:00" + "expires_at": "3000-12-12T00:00:00-05:00" }, "address": "3901 Locust Walk, Philadelphia, PA", "beds": 4.0, "baths": 1.0, "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00" + "end_date": "3000-05-31T00:00:00-04:00" } ] \ No newline at end of file diff --git a/backend/tests/market/user_2_items.json b/backend/tests/market/user_2_items.json deleted file mode 100644 index 82b1adaa..00000000 --- a/backend/tests/market/user_2_items.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { - "tags": ["New"], - "category": "Food", - "title": "Bag of Doritos", - "description": "Cool Ranch", - "external_link": "https://example.com/doritos", - "price": 5.0, - "negotiable": false, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "2025-10-12T00:00:00-05:00" - } -] \ No newline at end of file From 41a9bc3aba1a0889821e5a1abc024e6f784d3bce Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sat, 6 Sep 2025 21:10:00 -0400 Subject: [PATCH 2/7] fixed failing tests --- backend/tests/market/test_market.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index 8564212a..76337b5c 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -806,7 +806,7 @@ def test_get_sublet_other(self): "tags": ["New"], "category": "Sublet", "title": "Rodin Quad", - "description": "Fully furnished 3-bedroom apartment available for sublet with all" + "description": "Fully furnished 4-bedroom apartment available for sublet with all" + " amenities included.", "external_link": "https://example.com/rodin-quad", "price": 1350.0, @@ -1222,7 +1222,7 @@ def test_create_offer_required_fields(self): "email": None, "message": "", "user": self.users[0].id, - "item": 3, + "item": self.items[2].id, } self.assertEqual(response.status_code, 201) self.assert_dict_equal_ignoring_keys( @@ -1252,10 +1252,10 @@ def test_create_offer_existing(self): self.assertEqual(response.json(), expected_response) def test_delete_offer(self): - self.assertTrue(Offer.objects.filter(id=1).exists()) + self.assertTrue(Offer.objects.filter(id=self.offers[0].id).exists()) response = self.client.delete(f"/market/items/{self.items[1].id}/offers/") self.assertEqual(response.status_code, 204) - self.assertFalse(Offer.objects.filter(id=1).exists()) + self.assertFalse(Offer.objects.filter(id=self.offers[0].id).exists()) def test_delete_offer_nonexistent(self): invalid_id = 1 From 96b2b6f3f70147709b3c2aeed748792e9878832d Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sat, 6 Sep 2025 21:22:37 -0400 Subject: [PATCH 3/7] fixed test case --- backend/tests/market/test_market.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index 76337b5c..fc9f15fa 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -1204,7 +1204,7 @@ def test_create_offer_all_fields(self): "email": "self_user@gmail.com", "message": "I want this", "user": self.users[0].id, - "item": 3, + "item": self.items[2].id, } self.assertEqual(response.status_code, 201) self.assert_dict_equal_ignoring_keys( From 2dba2c74a9c8eaf9cfa0a7acd23e5c6852fd528e Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sat, 6 Sep 2025 22:08:50 -0400 Subject: [PATCH 4/7] increased codecov --- backend/tests/market/test_market.py | 2220 ++++++++++++++------------- 1 file changed, 1154 insertions(+), 1066 deletions(-) diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index fc9f15fa..02120596 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -1,15 +1,13 @@ -import datetime import json from unittest.mock import MagicMock -import pytz from django.contrib.auth import get_user_model from django.core.files.storage import Storage from django.test import TestCase from django.utils.timezone import now from rest_framework.test import APIClient -from market.models import Category, Item, ItemImage, Offer, Sublet, Tag +from market.models import Category, Item, ItemImage, Sublet, Tag User = get_user_model() @@ -140,7 +138,1132 @@ def normalize(obj, path=""): self.assertEqual(normalize(actual), normalize(expected)) -class TestItemGet(BaseMarketTest): +# class TestItemGet(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] +# self.items = self.load_items( +# "tests/market/self_user_items.json", self.users[0] +# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + +# def test_get_items(self): +# response = self.client.get("/market/items/") +# expected_response = [ +# { +# "id": self.items[0].id, +# "seller": self.users[0].id, +# "tags": ["Textbook", "Used"], +# "category": "Book", +# "title": "Math Textbook", +# "price": 20.0, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# "favorite_count": 0, +# }, +# { +# "id": self.items[1].id, +# "seller": self.users[1].id, +# "tags": ["Laptop", "New"], +# "category": "Electronics", +# "title": "Macbook Pro", +# "price": 2000.0, +# "expires_at": "3000-08-12T01:00:00-04:00", +# "images": [], +# "favorite_count": 0, +# }, +# { +# "id": self.items[2].id, +# "seller": self.users[1].id, +# "tags": ["Couch"], +# "category": "Furniture", +# "title": "Couch", +# "price": 400.0, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# "favorite_count": 0, +# }, +# ] +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), expected_response, [], ["", "tags", "images"] +# ) + +# def test_get_item_seller(self): +# response = self.client.get("/market/items/?seller=true") +# expected_response = [ +# { +# "id": self.items[0].id, +# "seller": self.users[0].id, +# "tags": ["Textbook", "Used"], +# "category": "Book", +# "title": "Math Textbook", +# "price": 20.0, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# "favorite_count": 0, +# } +# ] +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), expected_response, [], ["", "tags", "images"] +# ) + +# def test_get_single_item_own(self): +# response = self.client.get(f"/market/items/{self.items[0].id}/") +# response_json = response.json() +# expected_response = { +# "id": self.items[0].id, +# "images": [], +# "title": "Math Textbook", +# "description": "2023 version", +# "external_link": "https://example.com/book", +# "price": 20.0, +# "negotiable": True, +# "created_at": "2025-09-04T14:01:34.530659-04:00", +# "expires_at": "3000-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Book", +# "buyers": [], +# "tags": ["Textbook", "Used"], +# "favorites": [], +# } +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response_json, +# expected_response, +# ["created_at"], +# ["", "tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response_json["created_at"]) +# - datetime.datetime.now(pytz.UTC) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_get_single_item_other(self): +# response = self.client.get(f"/market/items/{self.items[1].id}/") +# expected_response = { +# "id": self.items[1].id, +# "seller": self.users[1].id, +# "buyer_count": 0, +# "tags": ["Laptop", "New"], +# "category": "Electronics", +# "title": "Macbook Pro", +# "description": "M1 Chip", +# "external_link": "https://example.com/macbook", +# "price": 2000.0, +# "negotiable": True, +# "expires_at": "3000-08-12T01:00:00-04:00", +# "images": [], +# "favorite_count": 0, +# } +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), expected_response, [], ["", "tags", "images"] +# ) + + +# class TestItemPost(BaseMarketTest): + +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] + +# def test_create_item_all_fields(self): +# payload = { +# "id": 88, +# "seller": self.users[1].id, +# "buyers": [], +# "tags": ["New"], +# "category": "Book", +# "title": "Math Textbook", +# "description": "2023 version", +# "external_link": "https://example.com/listing", +# "price": 20.0, +# "negotiable": True, +# "created_at": "2024-11-26T00:50:03.217587-05:00", +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# } +# response = self.client.post("/market/items/", payload, format="json") +# expected_response = { +# "id": int(f"{response.json()['id']}"), +# "images": [], +# "title": "Math Textbook", +# "description": "2023 version", +# "external_link": "https://example.com/listing", +# "price": 20.0, +# "negotiable": True, +# "created_at": "2025-09-04T15:00:58.895709-04:00", +# "expires_at": "3000-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Book", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# } +# self.assertEqual(response.status_code, 201) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["", "tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.UTC) +# ), +# datetime.timedelta(minutes=10), +# ) +# response = self.client.get(f"/market/items/{response.json()['id']}/") +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["", "tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_create_item_exclude_unrequired(self): +# payload = { +# "tags": ["New"], +# "category": "Book", +# "title": "Math Textbook", +# "description": "2023 version", +# "price": 20.0, +# "negotiable": True, +# "expires_at": "3000-12-12T00:00:00-05:00", +# } +# response = self.client.post("/market/items/", payload, format="json") +# expected_response = { +# "id": int(f"{response.json()['id']}"), +# "images": [], +# "title": "Math Textbook", +# "description": "2023 version", +# "external_link": None, +# "price": 20.0, +# "negotiable": True, +# "created_at": "2025-09-04T15:00:58.895709-04:00", +# "expires_at": "3000-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Book", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# } +# self.assertEqual(response.status_code, 201) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["", "tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.UTC) +# ), +# datetime.timedelta(minutes=10), +# ) +# response = self.client.get(f"/market/items/{response.json()['id']}/") +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["", "tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_create_item_missing_field(self): +# payload = { +# "tags": ["New"], +# "category": "Book", +# "external_link": "https://example.com/listing", +# "price": 20.0, +# "negotiable": True, +# "expires_at": "3000-12-12T00:00:00-05:00", +# } +# response = self.client.post("/market/items/", payload) +# self.assertEqual(response.status_code, 400) +# self.assertEqual(response.json(), {"title": ["This field is required."]}) + +# def test_create_item_invalid_category(self): +# payload = { +# "tags": ["New"], +# "category": "Textbook", +# "title": "Math Textbook", +# "description": "2023 version", +# "external_link": "https://example.com/listing", +# "price": 20.0, +# "negotiable": True, +# "expires_at": "3000-12-12T00:00:00-05:00", +# } +# response = self.client.post("/market/items/", payload) +# res_json = json.loads(response.content) +# self.assertEqual(response.status_code, 400) +# self.assertEqual( +# res_json, +# {"category": ['Invalid pk "Textbook" - object does not exist.']}, +# ) + +# def test_create_item_with_profanity_title(self): +# payload = { +# "tags": ["New"], +# "category": "Book", +# "title": "Fuck Textbook", +# "description": "Fuck 2023 version", +# "external_link": "https://example.com/listing", +# "price": 20.0, +# "negotiable": True, +# "expires_at": "3000-12-12T00:00:00-05:00", +# } +# response = self.client.post("/market/items/", payload) +# self.assertEqual(response.status_code, 400) + +# res_json = response.json() +# self.assertIn("title", res_json) +# self.assertIn("description", res_json) +# self.assertEqual(res_json["title"][0], "The title contains inappropriate language.") +# self.assertEqual( +# res_json["description"][0], +# "The description contains inappropriate language.", +# ) + + +# class TestItemPatch(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] +# self.items = self.load_items( +# "tests/market/self_user_items.json", self.users[0] +# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + +# def test_update_item_minimum_required(self): +# payload = { +# "category": "Book", +# "title": "Physics Textbook", +# "price": 25.0, +# "expires_at": "3000-12-13T00:00:00-05:00", +# } +# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) +# expected_response = { +# "id": self.items[0].id, +# "seller": self.users[0].id, +# "buyers": [], +# "tags": ["Used", "Textbook"], +# "category": "Book", +# "title": "Physics Textbook", +# "description": "2023 version", +# "external_link": "https://example.com/book", +# "price": 25.0, +# "negotiable": True, +# "expires_at": "3000-12-13T00:00:00-05:00", +# "images": [], +# "favorites": [], +# } +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["tags", "images", "favorites", "buyers"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) +# response = self.client.get(f"/market/items/{self.items[0].id}/") +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["tags", "images", "favorites", "buyers"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_update_item_all_fields(self): +# payload = { +# "id": 7, +# "seller": self.users[1].id, +# "buyers": [], +# "tags": ["New"], +# "category": "Food", +# "title": "5 meal swipes", +# "description": "5 meal swipes for sale", +# "external_link": "https://example.com/meal-swipes", +# "price": 25.0, +# "negotiable": False, +# "created_at": "2024-11-26T00:50:03.217587-05:00", +# "expires_at": "3000-12-14T00:00:00-05:00", +# "images": [], +# } +# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) +# expected_response = { +# "id": self.items[0].id, +# "seller": self.users[0].id, +# "buyers": [], +# "tags": ["New"], +# "category": "Food", +# "title": "5 meal swipes", +# "description": "5 meal swipes for sale", +# "external_link": "https://example.com/meal-swipes", +# "price": 25.0, +# "negotiable": False, +# "expires_at": "3000-12-14T00:00:00-05:00", +# "images": [], +# "favorites": [], +# } + +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) +# response = self.client.get(f"/market/items/{self.items[0].id}/") +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_update_item_invalid_category(self): +# payload = { +# "category": "Textbook", +# "title": "Physics Textbook", +# "price": 25.0, +# "expires_at": "3000-12-13T00:00:00-05:00", +# } +# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) +# self.assertEqual(response.status_code, 400) +# self.assertEqual( +# response.json(), +# {"category": ['Invalid pk "Textbook" - object does not exist.']}, +# ) + +# def test_update_item_invalid_tag(self): +# payload = { +# "tags": ["Not a tag"], +# "category": "Book", +# "title": "Physics Textbook", +# "price": 25.0, +# "expires_at": "3000-12-13T00:00:00-05:00", +# } +# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) +# self.assertEqual(response.status_code, 400) +# self.assertEqual( +# response.json(), +# {"tags": ['Invalid pk "Not a tag" - object does not exist.']}, +# ) + +# def test_update_item_to_sublet(self): +# response = self.client.patch(f"/market/items/{self.items[0].id}/", {"category": "Sublet"}) +# expected_response = { +# "id": self.items[0].id, +# "images": [], +# "title": "Math Textbook", +# "description": "2023 version", +# "external_link": "https://example.com/book", +# "price": 20.0, +# "negotiable": True, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Sublet", +# "buyers": [], +# "tags": ["Textbook", "Used"], +# "favorites": [], +# } +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["tags", "images", "buyers", "favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_update_item_not_owned(self): +# payload = {"title": "New Title"} +# response = self.client.patch(f"/market/items/{self.items[1].id}/", payload, format="json") +# self.assertEqual(response.status_code, 403) +# self.assertEqual( +# response.json(), +# {"detail": "You do not have permission to perform this action."}, +# ) + + +# class TestItemDelete(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] +# self.items = self.load_items( +# "tests/market/self_user_items.json", self.users[0] +# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + +# def test_delete_item(self): +# self.assertTrue(Item.objects.filter(id=self.items[0].id).exists()) +# response = self.client.delete(f"/market/items/{self.items[0].id}/") +# self.assertEqual(response.status_code, 204) +# self.assertFalse(Item.objects.filter(id=self.items[0].id).exists()) + +# def test_delete_item_not_owned(self): +# response = self.client.delete(f"/market/items/{self.items[1].id}/") +# self.assertEqual(response.status_code, 403) +# self.assertEqual( +# response.json(), +# {"detail": "You do not have permission to perform this action."}, +# ) + + +# class TestSubletGet(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] +# items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) +# items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) +# self.items = items1 + items2 +# self.sublets = sublets1 + sublets2 + +# def test_get_sublets(self): +# response = self.client.get("/market/sublets/") +# self.assertEqual(response.status_code, 200) +# self.assertEqual(len(response.json()), 2) +# expected_response = [ +# { +# "id": self.sublets[0].id, +# "item": { +# "id": self.items[0].id, +# "seller": self.users[0].id, +# "tags": ["New"], +# "category": "Sublet", +# "title": "Cira Green Sublet", +# "price": 1350.0, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# "favorite_count": 0, +# }, +# "address": "Cira Green, Philadelphia, PA", +# "beds": 3.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "3000-05-31T00:00:00-04:00", +# }, +# { +# "id": self.sublets[1].id, +# "item": { +# "id": self.items[1].id, +# "seller": self.users[1].id, +# "tags": ["New"], +# "category": "Sublet", +# "title": "Rodin Quad", +# "price": 1350.0, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# "favorite_count": 0, +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "3000-05-31T00:00:00-04:00", +# }, +# ] +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["created_at"], +# ["", "tags", "images", "buyers", "favorites"], +# ) + +# def test_get_sublet_own(self): +# response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") +# self.assertEqual(response.status_code, 200) +# expected_response = { +# "id": self.sublets[0].id, +# "item": { +# "id": self.items[0].id, +# "images": [], +# "title": "Cira Green Sublet", +# "description": "Fully furnished 3-bedroom apartment available for sublet with all" +# + " amenities included.", +# "external_link": "https://example.com/cira-green", +# "price": 1350.0, +# "negotiable": False, +# "created_at": "2025-09-05T00:14:15.141807-04:00", +# "expires_at": "3000-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Sublet", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# }, +# "address": "Cira Green, Philadelphia, PA", +# "beds": 3.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "3000-05-31T00:00:00-04:00", +# } +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["", "item.tags", "item.images", "item.buyers", "item.favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_get_sublet_other(self): +# response = self.client.get(f"/market/sublets/{self.sublets[1].id}/") +# self.assertEqual(response.status_code, 200) +# expected_response = { +# "id": self.sublets[1].id, +# "item": { +# "id": self.items[1].id, +# "seller": self.users[1].id, +# "buyer_count": 0, +# "tags": ["New"], +# "category": "Sublet", +# "title": "Rodin Quad", +# "description": "Fully furnished 4-bedroom apartment available for sublet with all" +# + " amenities included.", +# "external_link": "https://example.com/rodin-quad", +# "price": 1350.0, +# "negotiable": False, +# "expires_at": "3000-12-12T00:00:00-05:00", +# "images": [], +# "favorite_count": 0, +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "3000-05-31T00:00:00-04:00", +# } +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["", "item.tags", "item.images", "item.buyers", "item.favorites"], +# ) + +# def test_get_single_sublet_invalid_id(self): +# response = self.client.get(f"/market/sublets/{self.sublets[1].id+1}/") +# self.assertEqual(response.status_code, 404) + + +# class TestSubletPost(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] + +# def test_create_sublet(self): +# payload = { +# "id": 3, +# "item": { +# "id": 7, +# "images": [], +# "title": "Cira Green Sublet 2", +# "description": ( +# "Fully furnished 3-bedroom apartment available" +# " for sublet with all amenities included." +# ), +# "external_link": "https://example.com/listing", +# "price": 1350.0, +# "negotiable": False, +# "expires_at": "2025-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Sublet", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "3000-05-31T00:00:00-04:00", +# } +# response = self.client.post("/market/sublets/", payload, format="json") +# expected_response = { +# "id": response.json()["id"], +# "item": { +# "id": response.json()["item"]["id"], +# "images": [], +# "title": "Cira Green Sublet 2", +# "description": "Fully furnished 3-bedroom apartment available for sublet with all" +# + " amenities included.", +# "external_link": "https://example.com/listing", +# "price": 1350.0, +# "negotiable": False, +# "created_at": "2025-09-05T00:48:08.880144-04:00", +# "expires_at": "2025-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Sublet", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "3000-05-31T00:00:00-04:00", +# } +# self.assertEqual(response.status_code, 201) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["item.tags", "item.images", "item.buyers", "item.favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) +# response = self.client.get(f"/market/sublets/{response.json()['id']}/") +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["item.tags", "item.images", "item.buyers", "item.favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + + +# class TestSubletPatchDelete(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = self.load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] +# items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) +# items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) +# self.items = items1 + items2 +# self.sublets = sublets1 + sublets2 + +# def test_update_sublet(self): +# payload = { +# "id": 3, +# "item": { +# "id": 7, +# "images": [], +# "title": "Cira Green Sublet 2", +# "description": ( +# "Fully furnished 3-bedroom apartment " +# "available for sublet with all amenities included." +# ), +# "external_link": "https://example.com/listing", +# "price": 1350.0, +# "negotiable": False, +# "expires_at": "2025-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Sublet", +# "buyers": [], +# "tags": ["Apartment", "New"], +# "favorites": [], +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "2025-05-31T00:00:00-04:00", +# } +# response = self.client.patch( +# f"/market/sublets/{self.sublets[0].id}/", payload, format="json" +# ) +# expected_response = { +# "id": int(self.sublets[0].id), +# "item": { +# "id": int(self.items[0].id), +# "images": [], +# "title": "Cira Green Sublet 2", +# "description": ( +# "Fully furnished 3-bedroom apartment " +# "available for sublet with all amenities included." +# ), +# "external_link": "https://example.com/listing", +# "price": 1350.0, +# "negotiable": False, +# "expires_at": "2025-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Sublet", +# "buyers": [], +# "tags": ["New", "Apartment"], +# "favorites": [], +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "2025-05-31T00:00:00-04:00", +# } +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["item.tags", "item.images", "item.buyers", "item.favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) +# response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["item.tags", "item.images", "item.buyers", "item.favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + +# def test_update_sublet_non_sublet_category(self): +# payload = { +# "item": { +# "images": [], +# "title": "Cira Green Sublet 2", +# "description": ( +# "Fully furnished 3-bedroom apartment " +# "available for sublet with all amenities included." +# ), +# "external_link": "https://example.com/listing", +# "price": 1350.0, +# "negotiable": False, +# "expires_at": "2025-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Book", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "2025-05-31T00:00:00-04:00", +# } +# response = self.client.patch( +# f"/market/sublets/{self.sublets[0].id}/", payload, format="json" +# ) +# expected_response = { +# "id": self.sublets[0].id, +# "item": { +# "id": self.items[0].id, +# "images": [], +# "title": "Cira Green Sublet 2", +# "description": ( +# "Fully furnished 3-bedroom apartment " +# "available for sublet with all amenities included." +# ), +# "external_link": "https://example.com/listing", +# "price": 1350.0, +# "negotiable": False, +# "expires_at": "2025-12-12T00:00:00-05:00", +# "seller": self.users[0].id, +# "category": "Book", +# "buyers": [], +# "tags": ["New"], +# "favorites": [], +# }, +# "address": "3901 Locust Walk, Philadelphia, PA", +# "beds": 4.0, +# "baths": 1.0, +# "start_date": "2024-01-01T00:00:00-05:00", +# "end_date": "2025-05-31T00:00:00-04:00", +# } +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), +# expected_response, +# ["item.created_at"], +# ["item.tags", "item.images", "item.buyers", "item.favorites"], +# ) +# self.assertLessEqual( +# abs( +# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) +# - datetime.datetime.now(pytz.timezone("UTC")) +# ), +# datetime.timedelta(minutes=10), +# ) + + +# class TestOffer(BaseMarketTest): +# def setUp(self): +# super().setUp() +# self.tags = super().load_tags() +# self.categories = self.load_categories() +# self.users = [ +# self.load_user("user", "user@gmail.com", "user", True, True), +# self.load_user("user1", "user1@gmail.com", "user1", False, False), +# ] +# self.items = self.load_items( +# "tests/market/self_user_items.json", self.users[0] +# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) +# items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) +# items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) +# self.items += items1 + items2 +# self.sublets = sublets1 + sublets2 +# self.offers = [ +# Offer.objects.create( +# user=self.users[0], +# item=self.items[1], +# email="self_user@gmail.com", +# ), +# Offer.objects.create( +# user=self.users[0], +# item=self.items[4], +# email="self_user@gmail.com", +# phone_number="+15555555555", +# message="I want this", +# ), +# Offer.objects.create( +# user=self.users[1], +# item=self.items[0], +# email="user1@gmail.com", +# phone_number="+15555555555", +# message="", +# ), +# ] + +# def test_get_all_user_offers(self): +# response = self.client.get("/market/offers/made/") +# expected_response = [ +# { +# "id": self.offers[0].id, +# "phone_number": None, +# "email": "self_user@gmail.com", +# "message": "", +# "user": self.users[0].id, +# "item": self.items[1].id, +# }, +# { +# "id": self.offers[1].id, +# "phone_number": "+15555555555", +# "email": "self_user@gmail.com", +# "message": "I want this", +# "user": self.users[0].id, +# "item": self.items[4].id, +# }, +# ] +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys( +# response.json(), expected_response, ["created_at"], [""] +# ) + +# def test_list_item_offers(self): +# response = self.client.get(f"/market/items/{self.items[0].id}/offers/") +# expected = [ +# { +# "id": self.offers[2].id, +# "phone_number": "+15555555555", +# "email": "user1@gmail.com", +# "message": "", +# "user": self.users[1].id, +# "item": self.items[0].id, +# } +# ] +# self.assertEqual(response.status_code, 200) +# self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) + +# def test_get_offer_other(self): +# response = self.client.get(f"/market/items/{self.items[1].id}/offers/") +# expected = {"detail": "You do not have permission to perform this action."} +# self.assertEqual(response.status_code, 403) +# self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) + +# def test_list_item_offers_invalid_item(self): +# invalid_id = 1 +# while Item.objects.filter(id=invalid_id).exists(): +# invalid_id += 1 +# response = self.client.get(f"/market/items/{invalid_id}/offers/") +# self.assertEqual(response.status_code, 404) +# self.assertEqual(response.json(), {"detail": "No Item matches the given query"}) + +# def test_create_offer_all_fields(self): +# payload = { +# "phone_number": "+1 (202) 555 0100", +# "email": "self_user@gmail.com", +# "message": "I want this", +# } +# response = self.client.post( +# f"/market/items/{self.items[2].id}/offers/", payload, format="json" +# ) +# expected_response = { +# "id": response.json()["id"], +# "phone_number": "+12025550100", +# "email": "self_user@gmail.com", +# "message": "I want this", +# "user": self.users[0].id, +# "item": self.items[2].id, +# } +# self.assertEqual(response.status_code, 201) +# self.assert_dict_equal_ignoring_keys( +# response.json(), expected_response, ["created_at"], [""] +# ) + +# def test_create_offer_required_fields(self): +# payload = {"phone_number": "+12025550100"} +# response = self.client.post( +# f"/market/items/{self.items[2].id}/offers/", payload, format="json" +# ) +# expected_response = { +# "id": response.json()["id"], +# "phone_number": "+12025550100", +# "email": None, +# "message": "", +# "user": self.users[0].id, +# "item": self.items[2].id, +# } +# self.assertEqual(response.status_code, 201) +# self.assert_dict_equal_ignoring_keys( +# response.json(), expected_response, ["created_at"], [""] +# ) + +# def test_create_offer_invalid(self): +# payload = {} +# response = self.client.post( +# f"/market/items/{self.items[2].id}/offers/", payload, format="json" +# ) +# expected_response = {"phone_number": ["This field is required."]} +# self.assertEqual(response.status_code, 400) +# self.assertEqual(response.json(), expected_response) + +# def test_create_offer_existing(self): +# payload = { +# "phone_number": "+1 (202) 555 0100", +# "email": "self_user@gmail.com", +# "message": "I want this", +# } +# response = self.client.post( +# f"/market/items/{self.items[1].id}/offers/", payload, format="json" +# ) +# expected_response = ["Offer already exists"] +# self.assertEqual(response.status_code, 400) +# self.assertEqual(response.json(), expected_response) + +# def test_delete_offer(self): +# self.assertTrue(Offer.objects.filter(id=self.offers[0].id).exists()) +# response = self.client.delete(f"/market/items/{self.items[1].id}/offers/") +# self.assertEqual(response.status_code, 204) +# self.assertFalse(Offer.objects.filter(id=self.offers[0].id).exists()) + +# def test_delete_offer_nonexistent(self): +# invalid_id = 1 +# while Offer.objects.filter(id=invalid_id).exists(): +# invalid_id += 1 +# response = self.client.delete(f"/market/items/{invalid_id}/offers/") +# self.assertEqual(response.status_code, 404) + + +class TestFavorites(BaseMarketTest): def setUp(self): super().setUp() self.tags = self.load_tags() @@ -152,108 +1275,40 @@ def setUp(self): self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + self.items[0].favorites.add(self.users[1]) + self.items[1].favorites.add(self.users[0]) - def test_get_items(self): - response = self.client.get("/market/items/") - expected_response = [ - { - "id": self.items[0].id, - "seller": self.users[0].id, - "tags": ["Textbook", "Used"], - "category": "Book", - "title": "Math Textbook", - "price": 20.0, - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 0, - }, - { - "id": self.items[1].id, - "seller": self.users[1].id, - "tags": ["Laptop", "New"], - "category": "Electronics", - "title": "Macbook Pro", - "price": 2000.0, - "expires_at": "3000-08-12T01:00:00-04:00", - "images": [], - "favorite_count": 0, - }, - { - "id": self.items[2].id, - "seller": self.users[1].id, - "tags": ["Couch"], - "category": "Furniture", - "title": "Couch", - "price": 400.0, - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 0, - }, - ] - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), expected_response, [], ["", "tags", "images"] - ) - - def test_get_item_seller(self): - response = self.client.get("/market/items/?seller=true") - expected_response = [ - { - "id": self.items[0].id, - "seller": self.users[0].id, - "tags": ["Textbook", "Used"], - "category": "Book", - "title": "Math Textbook", - "price": 20.0, - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 0, - } - ] - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), expected_response, [], ["", "tags", "images"] - ) - - def test_get_single_item_own(self): + def test_get_favorites_for_item_owned(self): response = self.client.get(f"/market/items/{self.items[0].id}/") - response_json = response.json() expected_response = { - "id": self.items[0].id, + "id": 1, "images": [], "title": "Math Textbook", "description": "2023 version", "external_link": "https://example.com/book", "price": 20.0, "negotiable": True, - "created_at": "2025-09-04T14:01:34.530659-04:00", + "created_at": "2025-09-06T21:40:57.667236-04:00", "expires_at": "3000-12-12T00:00:00-05:00", "seller": self.users[0].id, "category": "Book", "buyers": [], "tags": ["Textbook", "Used"], - "favorites": [], + "favorites": [self.users[1].id], } self.assertEqual(response.status_code, 200) self.assert_dict_equal_ignoring_keys( - response_json, + response.json(), expected_response, ["created_at"], - ["", "tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response_json["created_at"]) - - datetime.datetime.now(pytz.UTC) - ), - datetime.timedelta(minutes=10), + ["tags", "images", "buyers", "favorites"], ) - def test_get_single_item_other(self): + def test_get_favorites_for_item_other(self): response = self.client.get(f"/market/items/{self.items[1].id}/") expected_response = { - "id": self.items[1].id, - "seller": self.users[1].id, + "id": 2, + "seller": 2, "buyer_count": 0, "tags": ["Laptop", "New"], "category": "Electronics", @@ -264,300 +1319,8 @@ def test_get_single_item_other(self): "negotiable": True, "expires_at": "3000-08-12T01:00:00-04:00", "images": [], - "favorite_count": 0, - } - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), expected_response, [], ["", "tags", "images"] - ) - - -class TestItemPost(BaseMarketTest): - - def setUp(self): - super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - - def test_create_item_all_fields(self): - payload = { - "id": 88, - "seller": self.users[1].id, - "buyers": [], - "tags": ["New"], - "category": "Book", - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "created_at": "2024-11-26T00:50:03.217587-05:00", - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - } - response = self.client.post("/market/items/", payload, format="json") - expected_response = { - "id": int(f"{response.json()['id']}"), - "images": [], - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "created_at": "2025-09-04T15:00:58.895709-04:00", - "expires_at": "3000-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Book", - "buyers": [], - "tags": ["New"], - "favorites": [], - } - self.assertEqual(response.status_code, 201) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["", "tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.UTC) - ), - datetime.timedelta(minutes=10), - ) - response = self.client.get(f"/market/items/{response.json()['id']}/") - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["", "tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - def test_create_item_exclude_unrequired(self): - payload = { - "tags": ["New"], - "category": "Book", - "title": "Math Textbook", - "description": "2023 version", - "price": 20.0, - "negotiable": True, - "expires_at": "3000-12-12T00:00:00-05:00", - } - response = self.client.post("/market/items/", payload, format="json") - expected_response = { - "id": int(f"{response.json()['id']}"), - "images": [], - "title": "Math Textbook", - "description": "2023 version", - "external_link": None, - "price": 20.0, - "negotiable": True, - "created_at": "2025-09-04T15:00:58.895709-04:00", - "expires_at": "3000-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Book", - "buyers": [], - "tags": ["New"], - "favorites": [], + "favorite_count": 1, } - self.assertEqual(response.status_code, 201) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["", "tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.UTC) - ), - datetime.timedelta(minutes=10), - ) - response = self.client.get(f"/market/items/{response.json()['id']}/") - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["", "tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - def test_create_item_missing_field(self): - payload = { - "tags": ["New"], - "category": "Book", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "expires_at": "3000-12-12T00:00:00-05:00", - } - response = self.client.post("/market/items/", payload) - self.assertEqual(response.status_code, 400) - self.assertEqual(response.json(), {"title": ["This field is required."]}) - - def test_create_item_invalid_category(self): - payload = { - "tags": ["New"], - "category": "Textbook", - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "expires_at": "3000-12-12T00:00:00-05:00", - } - response = self.client.post("/market/items/", payload) - res_json = json.loads(response.content) - self.assertEqual(response.status_code, 400) - self.assertEqual( - res_json, - {"category": ['Invalid pk "Textbook" - object does not exist.']}, - ) - - def test_create_item_with_profanity_title(self): - payload = { - "tags": ["New"], - "category": "Book", - "title": "Fuck Textbook", - "description": "Fuck 2023 version", - "external_link": "https://example.com/listing", - "price": 20.0, - "negotiable": True, - "expires_at": "3000-12-12T00:00:00-05:00", - } - response = self.client.post("/market/items/", payload) - self.assertEqual(response.status_code, 400) - - res_json = response.json() - self.assertIn("title", res_json) - self.assertIn("description", res_json) - self.assertEqual(res_json["title"][0], "The title contains inappropriate language.") - self.assertEqual( - res_json["description"][0], - "The description contains inappropriate language.", - ) - - -class TestItemPatch(BaseMarketTest): - def setUp(self): - super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - self.items = self.load_items( - "tests/market/self_user_items.json", self.users[0] - ) + self.load_items("tests/market/user_1_items.json", self.users[1]) - - def test_update_item_minimum_required(self): - payload = { - "category": "Book", - "title": "Physics Textbook", - "price": 25.0, - "expires_at": "3000-12-13T00:00:00-05:00", - } - response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) - expected_response = { - "id": self.items[0].id, - "seller": self.users[0].id, - "buyers": [], - "tags": ["Used", "Textbook"], - "category": "Book", - "title": "Physics Textbook", - "description": "2023 version", - "external_link": "https://example.com/book", - "price": 25.0, - "negotiable": True, - "expires_at": "3000-12-13T00:00:00-05:00", - "images": [], - "favorites": [], - } - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["tags", "images", "favorites", "buyers"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - response = self.client.get(f"/market/items/{self.items[0].id}/") - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["tags", "images", "favorites", "buyers"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - def test_update_item_all_fields(self): - payload = { - "id": 7, - "seller": self.users[1].id, - "buyers": [], - "tags": ["New"], - "category": "Food", - "title": "5 meal swipes", - "description": "5 meal swipes for sale", - "external_link": "https://example.com/meal-swipes", - "price": 25.0, - "negotiable": False, - "created_at": "2024-11-26T00:50:03.217587-05:00", - "expires_at": "3000-12-14T00:00:00-05:00", - "images": [], - } - response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) - expected_response = { - "id": self.items[0].id, - "seller": self.users[0].id, - "buyers": [], - "tags": ["New"], - "category": "Food", - "title": "5 meal swipes", - "description": "5 meal swipes for sale", - "external_link": "https://example.com/meal-swipes", - "price": 25.0, - "negotiable": False, - "expires_at": "3000-12-14T00:00:00-05:00", - "images": [], - "favorites": [], - } - self.assertEqual(response.status_code, 200) self.assert_dict_equal_ignoring_keys( response.json(), @@ -565,704 +1328,29 @@ def test_update_item_all_fields(self): ["created_at"], ["tags", "images", "buyers", "favorites"], ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - response = self.client.get(f"/market/items/{self.items[0].id}/") - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - def test_update_item_invalid_category(self): - payload = { - "category": "Textbook", - "title": "Physics Textbook", - "price": 25.0, - "expires_at": "3000-12-13T00:00:00-05:00", - } - response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) - self.assertEqual(response.status_code, 400) - self.assertEqual( - response.json(), - {"category": ['Invalid pk "Textbook" - object does not exist.']}, - ) - - def test_update_item_invalid_tag(self): - payload = { - "tags": ["Not a tag"], - "category": "Book", - "title": "Physics Textbook", - "price": 25.0, - "expires_at": "3000-12-13T00:00:00-05:00", - } - response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) - self.assertEqual(response.status_code, 400) - self.assertEqual( - response.json(), - {"tags": ['Invalid pk "Not a tag" - object does not exist.']}, - ) - - def test_update_item_to_sublet(self): - response = self.client.patch(f"/market/items/{self.items[0].id}/", {"category": "Sublet"}) - expected_response = { - "id": self.items[0].id, - "images": [], - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/book", - "price": 20.0, - "negotiable": True, - "expires_at": "3000-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Sublet", - "buyers": [], - "tags": ["Textbook", "Used"], - "favorites": [], - } - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["tags", "images", "buyers", "favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - def test_update_item_not_owned(self): - payload = {"title": "New Title"} - response = self.client.patch(f"/market/items/{self.items[1].id}/", payload, format="json") - self.assertEqual(response.status_code, 403) - self.assertEqual( - response.json(), - {"detail": "You do not have permission to perform this action."}, - ) - - -class TestItemDelete(BaseMarketTest): - def setUp(self): - super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - self.items = self.load_items( - "tests/market/self_user_items.json", self.users[0] - ) + self.load_items("tests/market/user_1_items.json", self.users[1]) - - def test_delete_item(self): - self.assertTrue(Item.objects.filter(id=self.items[0].id).exists()) - response = self.client.delete(f"/market/items/{self.items[0].id}/") - self.assertEqual(response.status_code, 204) - self.assertFalse(Item.objects.filter(id=self.items[0].id).exists()) - - def test_delete_item_not_owned(self): - response = self.client.delete(f"/market/items/{self.items[1].id}/") - self.assertEqual(response.status_code, 403) - self.assertEqual( - response.json(), - {"detail": "You do not have permission to perform this action."}, - ) - - -class TestSubletGet(BaseMarketTest): - def setUp(self): - super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) - items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) - self.items = items1 + items2 - self.sublets = sublets1 + sublets2 - - def test_get_sublets(self): - response = self.client.get("/market/sublets/") - self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.json()), 2) - expected_response = [ - { - "id": self.sublets[0].id, - "item": { - "id": self.items[0].id, - "seller": self.users[0].id, - "tags": ["New"], - "category": "Sublet", - "title": "Cira Green Sublet", - "price": 1350.0, - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 0, - }, - "address": "Cira Green, Philadelphia, PA", - "beds": 3.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00", - }, - { - "id": self.sublets[1].id, - "item": { - "id": self.items[1].id, - "seller": self.users[1].id, - "tags": ["New"], - "category": "Sublet", - "title": "Rodin Quad", - "price": 1350.0, - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 0, - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00", - }, - ] - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["created_at"], - ["", "tags", "images", "buyers", "favorites"], - ) - - def test_get_sublet_own(self): - response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") - self.assertEqual(response.status_code, 200) - expected_response = { - "id": self.sublets[0].id, - "item": { - "id": self.items[0].id, - "images": [], - "title": "Cira Green Sublet", - "description": "Fully furnished 3-bedroom apartment available for sublet with all" - + " amenities included.", - "external_link": "https://example.com/cira-green", - "price": 1350.0, - "negotiable": False, - "created_at": "2025-09-05T00:14:15.141807-04:00", - "expires_at": "3000-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Sublet", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "Cira Green, Philadelphia, PA", - "beds": 3.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00", - } - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["", "item.tags", "item.images", "item.buyers", "item.favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - def test_get_sublet_other(self): - response = self.client.get(f"/market/sublets/{self.sublets[1].id}/") - self.assertEqual(response.status_code, 200) - expected_response = { - "id": self.sublets[1].id, - "item": { - "id": self.items[1].id, - "seller": self.users[1].id, - "buyer_count": 0, - "tags": ["New"], - "category": "Sublet", - "title": "Rodin Quad", - "description": "Fully furnished 4-bedroom apartment available for sublet with all" - + " amenities included.", - "external_link": "https://example.com/rodin-quad", - "price": 1350.0, - "negotiable": False, - "expires_at": "3000-12-12T00:00:00-05:00", - "images": [], - "favorite_count": 0, - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00", - } - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["", "item.tags", "item.images", "item.buyers", "item.favorites"], - ) - - def test_get_single_sublet_invalid_id(self): - response = self.client.get(f"/market/sublets/{self.sublets[1].id+1}/") - self.assertEqual(response.status_code, 404) - - -class TestSubletPost(BaseMarketTest): - def setUp(self): - super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - - def test_create_sublet(self): - payload = { - "id": 3, - "item": { - "id": 7, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment available" - " for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Sublet", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00", - } - response = self.client.post("/market/sublets/", payload, format="json") - expected_response = { - "id": response.json()["id"], - "item": { - "id": response.json()["item"]["id"], - "images": [], - "title": "Cira Green Sublet 2", - "description": "Fully furnished 3-bedroom apartment available for sublet with all" - + " amenities included.", - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "created_at": "2025-09-05T00:48:08.880144-04:00", - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Sublet", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00", - } + def test_post_favorite(self): + response = self.client.post(f"/market/items/{self.items[2].id}/favorites/") self.assertEqual(response.status_code, 201) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["item.tags", "item.images", "item.buyers", "item.favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - response = self.client.get(f"/market/sublets/{response.json()['id']}/") - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["item.tags", "item.images", "item.buyers", "item.favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - + self.assertEqual(Item.objects.get(id=self.items[2].id).favorites.count(), 1) + self.assertEqual(Item.objects.get(id=self.items[2].id).favorites.first(), self.users[0]) -class TestSubletPatchDelete(BaseMarketTest): - def setUp(self): - super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) - items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) - self.items = items1 + items2 - self.sublets = sublets1 + sublets2 - - def test_update_sublet(self): - payload = { - "id": 3, - "item": { - "id": 7, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Sublet", - "buyers": [], - "tags": ["Apartment", "New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - response = self.client.patch( - f"/market/sublets/{self.sublets[0].id}/", payload, format="json" - ) - expected_response = { - "id": int(self.sublets[0].id), - "item": { - "id": int(self.items[0].id), - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Sublet", - "buyers": [], - "tags": ["New", "Apartment"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["item.tags", "item.images", "item.buyers", "item.favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["item.tags", "item.images", "item.buyers", "item.favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - def test_update_sublet_non_sublet_category(self): - payload = { - "item": { - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Book", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - response = self.client.patch( - f"/market/sublets/{self.sublets[0].id}/", payload, format="json" - ) - expected_response = { - "id": self.sublets[0].id, - "item": { - "id": self.items[0].id, - "images": [], - "title": "Cira Green Sublet 2", - "description": ( - "Fully furnished 3-bedroom apartment " - "available for sublet with all amenities included." - ), - "external_link": "https://example.com/listing", - "price": 1350.0, - "negotiable": False, - "expires_at": "2025-12-12T00:00:00-05:00", - "seller": self.users[0].id, - "category": "Book", - "buyers": [], - "tags": ["New"], - "favorites": [], - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "2025-05-31T00:00:00-04:00", - } - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), - expected_response, - ["item.created_at"], - ["item.tags", "item.images", "item.buyers", "item.favorites"], - ) - self.assertLessEqual( - abs( - datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) - - datetime.datetime.now(pytz.timezone("UTC")) - ), - datetime.timedelta(minutes=10), - ) - - -class TestOffer(BaseMarketTest): - def setUp(self): - super().setUp() - self.tags = super().load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] - self.items = self.load_items( - "tests/market/self_user_items.json", self.users[0] - ) + self.load_items("tests/market/user_1_items.json", self.users[1]) - items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) - items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) - self.items += items1 + items2 - self.sublets = sublets1 + sublets2 - self.offers = [ - Offer.objects.create( - user=self.users[0], - item=self.items[1], - email="self_user@gmail.com", - ), - Offer.objects.create( - user=self.users[0], - item=self.items[4], - email="self_user@gmail.com", - phone_number="+15555555555", - message="I want this", - ), - Offer.objects.create( - user=self.users[1], - item=self.items[0], - email="user1@gmail.com", - phone_number="+15555555555", - message="", - ), - ] - - def test_get_all_user_offers(self): - response = self.client.get("/market/offers/made/") - expected_response = [ - { - "id": self.offers[0].id, - "phone_number": None, - "email": "self_user@gmail.com", - "message": "", - "user": self.users[0].id, - "item": self.items[1].id, - }, - { - "id": self.offers[1].id, - "phone_number": "+15555555555", - "email": "self_user@gmail.com", - "message": "I want this", - "user": self.users[0].id, - "item": self.items[4].id, - }, - ] - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys( - response.json(), expected_response, ["created_at"], [""] - ) - - def test_list_item_offers(self): - response = self.client.get(f"/market/items/{self.items[0].id}/offers/") - expected = [ - { - "id": self.offers[2].id, - "phone_number": "+15555555555", - "email": "user1@gmail.com", - "message": "", - "user": self.users[1].id, - "item": self.items[0].id, - } - ] - self.assertEqual(response.status_code, 200) - self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) - - def test_get_offer_other(self): - response = self.client.get(f"/market/items/{self.items[1].id}/offers/") - expected = {"detail": "You do not have permission to perform this action."} - self.assertEqual(response.status_code, 403) - self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) - - def test_list_item_offers_invalid_item(self): - invalid_id = 1 - while Item.objects.filter(id=invalid_id).exists(): - invalid_id += 1 - response = self.client.get(f"/market/items/{invalid_id}/offers/") - self.assertEqual(response.status_code, 404) - self.assertEqual(response.json(), {"detail": "No Item matches the given query"}) - - def test_create_offer_all_fields(self): - payload = { - "phone_number": "+1 (202) 555 0100", - "email": "self_user@gmail.com", - "message": "I want this", - } - response = self.client.post( - f"/market/items/{self.items[2].id}/offers/", payload, format="json" - ) - expected_response = { - "id": response.json()["id"], - "phone_number": "+12025550100", - "email": "self_user@gmail.com", - "message": "I want this", - "user": self.users[0].id, - "item": self.items[2].id, - } - self.assertEqual(response.status_code, 201) - self.assert_dict_equal_ignoring_keys( - response.json(), expected_response, ["created_at"], [""] - ) - - def test_create_offer_required_fields(self): - payload = {"phone_number": "+12025550100"} - response = self.client.post( - f"/market/items/{self.items[2].id}/offers/", payload, format="json" - ) - expected_response = { - "id": response.json()["id"], - "phone_number": "+12025550100", - "email": None, - "message": "", - "user": self.users[0].id, - "item": self.items[2].id, - } - self.assertEqual(response.status_code, 201) - self.assert_dict_equal_ignoring_keys( - response.json(), expected_response, ["created_at"], [""] - ) - - def test_create_offer_invalid(self): - payload = {} - response = self.client.post( - f"/market/items/{self.items[2].id}/offers/", payload, format="json" - ) - expected_response = {"phone_number": ["This field is required."]} - self.assertEqual(response.status_code, 400) - self.assertEqual(response.json(), expected_response) - - def test_create_offer_existing(self): - payload = { - "phone_number": "+1 (202) 555 0100", - "email": "self_user@gmail.com", - "message": "I want this", - } - response = self.client.post( - f"/market/items/{self.items[1].id}/offers/", payload, format="json" - ) - expected_response = ["Offer already exists"] + def test_post_favorite_existing(self): + response = self.client.post(f"/market/items/{self.items[1].id}/favorites/") self.assertEqual(response.status_code, 400) - self.assertEqual(response.json(), expected_response) + self.assertEqual(response.json(), ["Favorite already exists"]) + self.assertEqual(Item.objects.get(id=self.items[1].id).favorites.count(), 1) - def test_delete_offer(self): - self.assertTrue(Offer.objects.filter(id=self.offers[0].id).exists()) - response = self.client.delete(f"/market/items/{self.items[1].id}/offers/") + def test_delete_favorite(self): + response = self.client.delete(f"/market/items/{self.items[1].id}/favorites/") self.assertEqual(response.status_code, 204) - self.assertFalse(Offer.objects.filter(id=self.offers[0].id).exists()) + self.assertEqual(Item.objects.get(id=self.items[1].id).favorites.count(), 0) - def test_delete_offer_nonexistent(self): - invalid_id = 1 - while Offer.objects.filter(id=invalid_id).exists(): - invalid_id += 1 - response = self.client.delete(f"/market/items/{invalid_id}/offers/") + def test_delete_nonexistant_favorite(self): + response = self.client.delete(f"/market/items/{self.items[2].id}/favorites/") self.assertEqual(response.status_code, 404) + self.assertEqual(Item.objects.get(id=self.items[2].id).favorites.count(), 0) + self.assertEqual(response.json(), {"detail": "No Item matches the given query."}) class TestImages(BaseMarketTest): From 4e6bed0863b5a428a216da038dd61d5e3e74ec41 Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sat, 6 Sep 2025 22:10:13 -0400 Subject: [PATCH 5/7] uncommented shit --- backend/tests/market/test_market.py | 2250 ++++++++++++++------------- 1 file changed, 1126 insertions(+), 1124 deletions(-) diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index 02120596..a7cadbf3 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -1,13 +1,15 @@ +import datetime import json from unittest.mock import MagicMock +import pytz from django.contrib.auth import get_user_model from django.core.files.storage import Storage from django.test import TestCase from django.utils.timezone import now from rest_framework.test import APIClient -from market.models import Category, Item, ItemImage, Sublet, Tag +from market.models import Category, Item, ItemImage, Offer, Sublet, Tag User = get_user_model() @@ -138,1129 +140,1129 @@ def normalize(obj, path=""): self.assertEqual(normalize(actual), normalize(expected)) -# class TestItemGet(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] -# self.items = self.load_items( -# "tests/market/self_user_items.json", self.users[0] -# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) - -# def test_get_items(self): -# response = self.client.get("/market/items/") -# expected_response = [ -# { -# "id": self.items[0].id, -# "seller": self.users[0].id, -# "tags": ["Textbook", "Used"], -# "category": "Book", -# "title": "Math Textbook", -# "price": 20.0, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# "favorite_count": 0, -# }, -# { -# "id": self.items[1].id, -# "seller": self.users[1].id, -# "tags": ["Laptop", "New"], -# "category": "Electronics", -# "title": "Macbook Pro", -# "price": 2000.0, -# "expires_at": "3000-08-12T01:00:00-04:00", -# "images": [], -# "favorite_count": 0, -# }, -# { -# "id": self.items[2].id, -# "seller": self.users[1].id, -# "tags": ["Couch"], -# "category": "Furniture", -# "title": "Couch", -# "price": 400.0, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# "favorite_count": 0, -# }, -# ] -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), expected_response, [], ["", "tags", "images"] -# ) - -# def test_get_item_seller(self): -# response = self.client.get("/market/items/?seller=true") -# expected_response = [ -# { -# "id": self.items[0].id, -# "seller": self.users[0].id, -# "tags": ["Textbook", "Used"], -# "category": "Book", -# "title": "Math Textbook", -# "price": 20.0, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# "favorite_count": 0, -# } -# ] -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), expected_response, [], ["", "tags", "images"] -# ) - -# def test_get_single_item_own(self): -# response = self.client.get(f"/market/items/{self.items[0].id}/") -# response_json = response.json() -# expected_response = { -# "id": self.items[0].id, -# "images": [], -# "title": "Math Textbook", -# "description": "2023 version", -# "external_link": "https://example.com/book", -# "price": 20.0, -# "negotiable": True, -# "created_at": "2025-09-04T14:01:34.530659-04:00", -# "expires_at": "3000-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Book", -# "buyers": [], -# "tags": ["Textbook", "Used"], -# "favorites": [], -# } -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response_json, -# expected_response, -# ["created_at"], -# ["", "tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response_json["created_at"]) -# - datetime.datetime.now(pytz.UTC) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_get_single_item_other(self): -# response = self.client.get(f"/market/items/{self.items[1].id}/") -# expected_response = { -# "id": self.items[1].id, -# "seller": self.users[1].id, -# "buyer_count": 0, -# "tags": ["Laptop", "New"], -# "category": "Electronics", -# "title": "Macbook Pro", -# "description": "M1 Chip", -# "external_link": "https://example.com/macbook", -# "price": 2000.0, -# "negotiable": True, -# "expires_at": "3000-08-12T01:00:00-04:00", -# "images": [], -# "favorite_count": 0, -# } -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), expected_response, [], ["", "tags", "images"] -# ) - - -# class TestItemPost(BaseMarketTest): - -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] - -# def test_create_item_all_fields(self): -# payload = { -# "id": 88, -# "seller": self.users[1].id, -# "buyers": [], -# "tags": ["New"], -# "category": "Book", -# "title": "Math Textbook", -# "description": "2023 version", -# "external_link": "https://example.com/listing", -# "price": 20.0, -# "negotiable": True, -# "created_at": "2024-11-26T00:50:03.217587-05:00", -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# } -# response = self.client.post("/market/items/", payload, format="json") -# expected_response = { -# "id": int(f"{response.json()['id']}"), -# "images": [], -# "title": "Math Textbook", -# "description": "2023 version", -# "external_link": "https://example.com/listing", -# "price": 20.0, -# "negotiable": True, -# "created_at": "2025-09-04T15:00:58.895709-04:00", -# "expires_at": "3000-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Book", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# } -# self.assertEqual(response.status_code, 201) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["", "tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.UTC) -# ), -# datetime.timedelta(minutes=10), -# ) -# response = self.client.get(f"/market/items/{response.json()['id']}/") -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["", "tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_create_item_exclude_unrequired(self): -# payload = { -# "tags": ["New"], -# "category": "Book", -# "title": "Math Textbook", -# "description": "2023 version", -# "price": 20.0, -# "negotiable": True, -# "expires_at": "3000-12-12T00:00:00-05:00", -# } -# response = self.client.post("/market/items/", payload, format="json") -# expected_response = { -# "id": int(f"{response.json()['id']}"), -# "images": [], -# "title": "Math Textbook", -# "description": "2023 version", -# "external_link": None, -# "price": 20.0, -# "negotiable": True, -# "created_at": "2025-09-04T15:00:58.895709-04:00", -# "expires_at": "3000-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Book", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# } -# self.assertEqual(response.status_code, 201) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["", "tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.UTC) -# ), -# datetime.timedelta(minutes=10), -# ) -# response = self.client.get(f"/market/items/{response.json()['id']}/") -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["", "tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_create_item_missing_field(self): -# payload = { -# "tags": ["New"], -# "category": "Book", -# "external_link": "https://example.com/listing", -# "price": 20.0, -# "negotiable": True, -# "expires_at": "3000-12-12T00:00:00-05:00", -# } -# response = self.client.post("/market/items/", payload) -# self.assertEqual(response.status_code, 400) -# self.assertEqual(response.json(), {"title": ["This field is required."]}) - -# def test_create_item_invalid_category(self): -# payload = { -# "tags": ["New"], -# "category": "Textbook", -# "title": "Math Textbook", -# "description": "2023 version", -# "external_link": "https://example.com/listing", -# "price": 20.0, -# "negotiable": True, -# "expires_at": "3000-12-12T00:00:00-05:00", -# } -# response = self.client.post("/market/items/", payload) -# res_json = json.loads(response.content) -# self.assertEqual(response.status_code, 400) -# self.assertEqual( -# res_json, -# {"category": ['Invalid pk "Textbook" - object does not exist.']}, -# ) - -# def test_create_item_with_profanity_title(self): -# payload = { -# "tags": ["New"], -# "category": "Book", -# "title": "Fuck Textbook", -# "description": "Fuck 2023 version", -# "external_link": "https://example.com/listing", -# "price": 20.0, -# "negotiable": True, -# "expires_at": "3000-12-12T00:00:00-05:00", -# } -# response = self.client.post("/market/items/", payload) -# self.assertEqual(response.status_code, 400) - -# res_json = response.json() -# self.assertIn("title", res_json) -# self.assertIn("description", res_json) -# self.assertEqual(res_json["title"][0], "The title contains inappropriate language.") -# self.assertEqual( -# res_json["description"][0], -# "The description contains inappropriate language.", -# ) - - -# class TestItemPatch(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] -# self.items = self.load_items( -# "tests/market/self_user_items.json", self.users[0] -# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) - -# def test_update_item_minimum_required(self): -# payload = { -# "category": "Book", -# "title": "Physics Textbook", -# "price": 25.0, -# "expires_at": "3000-12-13T00:00:00-05:00", -# } -# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) -# expected_response = { -# "id": self.items[0].id, -# "seller": self.users[0].id, -# "buyers": [], -# "tags": ["Used", "Textbook"], -# "category": "Book", -# "title": "Physics Textbook", -# "description": "2023 version", -# "external_link": "https://example.com/book", -# "price": 25.0, -# "negotiable": True, -# "expires_at": "3000-12-13T00:00:00-05:00", -# "images": [], -# "favorites": [], -# } -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["tags", "images", "favorites", "buyers"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) -# response = self.client.get(f"/market/items/{self.items[0].id}/") -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["tags", "images", "favorites", "buyers"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_update_item_all_fields(self): -# payload = { -# "id": 7, -# "seller": self.users[1].id, -# "buyers": [], -# "tags": ["New"], -# "category": "Food", -# "title": "5 meal swipes", -# "description": "5 meal swipes for sale", -# "external_link": "https://example.com/meal-swipes", -# "price": 25.0, -# "negotiable": False, -# "created_at": "2024-11-26T00:50:03.217587-05:00", -# "expires_at": "3000-12-14T00:00:00-05:00", -# "images": [], -# } -# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) -# expected_response = { -# "id": self.items[0].id, -# "seller": self.users[0].id, -# "buyers": [], -# "tags": ["New"], -# "category": "Food", -# "title": "5 meal swipes", -# "description": "5 meal swipes for sale", -# "external_link": "https://example.com/meal-swipes", -# "price": 25.0, -# "negotiable": False, -# "expires_at": "3000-12-14T00:00:00-05:00", -# "images": [], -# "favorites": [], -# } - -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) -# response = self.client.get(f"/market/items/{self.items[0].id}/") -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_update_item_invalid_category(self): -# payload = { -# "category": "Textbook", -# "title": "Physics Textbook", -# "price": 25.0, -# "expires_at": "3000-12-13T00:00:00-05:00", -# } -# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) -# self.assertEqual(response.status_code, 400) -# self.assertEqual( -# response.json(), -# {"category": ['Invalid pk "Textbook" - object does not exist.']}, -# ) - -# def test_update_item_invalid_tag(self): -# payload = { -# "tags": ["Not a tag"], -# "category": "Book", -# "title": "Physics Textbook", -# "price": 25.0, -# "expires_at": "3000-12-13T00:00:00-05:00", -# } -# response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) -# self.assertEqual(response.status_code, 400) -# self.assertEqual( -# response.json(), -# {"tags": ['Invalid pk "Not a tag" - object does not exist.']}, -# ) - -# def test_update_item_to_sublet(self): -# response = self.client.patch(f"/market/items/{self.items[0].id}/", {"category": "Sublet"}) -# expected_response = { -# "id": self.items[0].id, -# "images": [], -# "title": "Math Textbook", -# "description": "2023 version", -# "external_link": "https://example.com/book", -# "price": 20.0, -# "negotiable": True, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Sublet", -# "buyers": [], -# "tags": ["Textbook", "Used"], -# "favorites": [], -# } -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["tags", "images", "buyers", "favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_update_item_not_owned(self): -# payload = {"title": "New Title"} -# response = self.client.patch(f"/market/items/{self.items[1].id}/", payload, format="json") -# self.assertEqual(response.status_code, 403) -# self.assertEqual( -# response.json(), -# {"detail": "You do not have permission to perform this action."}, -# ) - - -# class TestItemDelete(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] -# self.items = self.load_items( -# "tests/market/self_user_items.json", self.users[0] -# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) - -# def test_delete_item(self): -# self.assertTrue(Item.objects.filter(id=self.items[0].id).exists()) -# response = self.client.delete(f"/market/items/{self.items[0].id}/") -# self.assertEqual(response.status_code, 204) -# self.assertFalse(Item.objects.filter(id=self.items[0].id).exists()) - -# def test_delete_item_not_owned(self): -# response = self.client.delete(f"/market/items/{self.items[1].id}/") -# self.assertEqual(response.status_code, 403) -# self.assertEqual( -# response.json(), -# {"detail": "You do not have permission to perform this action."}, -# ) - - -# class TestSubletGet(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] -# items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) -# items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) -# self.items = items1 + items2 -# self.sublets = sublets1 + sublets2 - -# def test_get_sublets(self): -# response = self.client.get("/market/sublets/") -# self.assertEqual(response.status_code, 200) -# self.assertEqual(len(response.json()), 2) -# expected_response = [ -# { -# "id": self.sublets[0].id, -# "item": { -# "id": self.items[0].id, -# "seller": self.users[0].id, -# "tags": ["New"], -# "category": "Sublet", -# "title": "Cira Green Sublet", -# "price": 1350.0, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# "favorite_count": 0, -# }, -# "address": "Cira Green, Philadelphia, PA", -# "beds": 3.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "3000-05-31T00:00:00-04:00", -# }, -# { -# "id": self.sublets[1].id, -# "item": { -# "id": self.items[1].id, -# "seller": self.users[1].id, -# "tags": ["New"], -# "category": "Sublet", -# "title": "Rodin Quad", -# "price": 1350.0, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# "favorite_count": 0, -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "3000-05-31T00:00:00-04:00", -# }, -# ] -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["created_at"], -# ["", "tags", "images", "buyers", "favorites"], -# ) - -# def test_get_sublet_own(self): -# response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") -# self.assertEqual(response.status_code, 200) -# expected_response = { -# "id": self.sublets[0].id, -# "item": { -# "id": self.items[0].id, -# "images": [], -# "title": "Cira Green Sublet", -# "description": "Fully furnished 3-bedroom apartment available for sublet with all" -# + " amenities included.", -# "external_link": "https://example.com/cira-green", -# "price": 1350.0, -# "negotiable": False, -# "created_at": "2025-09-05T00:14:15.141807-04:00", -# "expires_at": "3000-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Sublet", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# }, -# "address": "Cira Green, Philadelphia, PA", -# "beds": 3.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "3000-05-31T00:00:00-04:00", -# } -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["", "item.tags", "item.images", "item.buyers", "item.favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_get_sublet_other(self): -# response = self.client.get(f"/market/sublets/{self.sublets[1].id}/") -# self.assertEqual(response.status_code, 200) -# expected_response = { -# "id": self.sublets[1].id, -# "item": { -# "id": self.items[1].id, -# "seller": self.users[1].id, -# "buyer_count": 0, -# "tags": ["New"], -# "category": "Sublet", -# "title": "Rodin Quad", -# "description": "Fully furnished 4-bedroom apartment available for sublet with all" -# + " amenities included.", -# "external_link": "https://example.com/rodin-quad", -# "price": 1350.0, -# "negotiable": False, -# "expires_at": "3000-12-12T00:00:00-05:00", -# "images": [], -# "favorite_count": 0, -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "3000-05-31T00:00:00-04:00", -# } -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["", "item.tags", "item.images", "item.buyers", "item.favorites"], -# ) - -# def test_get_single_sublet_invalid_id(self): -# response = self.client.get(f"/market/sublets/{self.sublets[1].id+1}/") -# self.assertEqual(response.status_code, 404) - - -# class TestSubletPost(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] - -# def test_create_sublet(self): -# payload = { -# "id": 3, -# "item": { -# "id": 7, -# "images": [], -# "title": "Cira Green Sublet 2", -# "description": ( -# "Fully furnished 3-bedroom apartment available" -# " for sublet with all amenities included." -# ), -# "external_link": "https://example.com/listing", -# "price": 1350.0, -# "negotiable": False, -# "expires_at": "2025-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Sublet", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "3000-05-31T00:00:00-04:00", -# } -# response = self.client.post("/market/sublets/", payload, format="json") -# expected_response = { -# "id": response.json()["id"], -# "item": { -# "id": response.json()["item"]["id"], -# "images": [], -# "title": "Cira Green Sublet 2", -# "description": "Fully furnished 3-bedroom apartment available for sublet with all" -# + " amenities included.", -# "external_link": "https://example.com/listing", -# "price": 1350.0, -# "negotiable": False, -# "created_at": "2025-09-05T00:48:08.880144-04:00", -# "expires_at": "2025-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Sublet", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "3000-05-31T00:00:00-04:00", -# } -# self.assertEqual(response.status_code, 201) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["item.tags", "item.images", "item.buyers", "item.favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) -# response = self.client.get(f"/market/sublets/{response.json()['id']}/") -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["item.tags", "item.images", "item.buyers", "item.favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - - -# class TestSubletPatchDelete(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = self.load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] -# items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) -# items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) -# self.items = items1 + items2 -# self.sublets = sublets1 + sublets2 - -# def test_update_sublet(self): -# payload = { -# "id": 3, -# "item": { -# "id": 7, -# "images": [], -# "title": "Cira Green Sublet 2", -# "description": ( -# "Fully furnished 3-bedroom apartment " -# "available for sublet with all amenities included." -# ), -# "external_link": "https://example.com/listing", -# "price": 1350.0, -# "negotiable": False, -# "expires_at": "2025-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Sublet", -# "buyers": [], -# "tags": ["Apartment", "New"], -# "favorites": [], -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "2025-05-31T00:00:00-04:00", -# } -# response = self.client.patch( -# f"/market/sublets/{self.sublets[0].id}/", payload, format="json" -# ) -# expected_response = { -# "id": int(self.sublets[0].id), -# "item": { -# "id": int(self.items[0].id), -# "images": [], -# "title": "Cira Green Sublet 2", -# "description": ( -# "Fully furnished 3-bedroom apartment " -# "available for sublet with all amenities included." -# ), -# "external_link": "https://example.com/listing", -# "price": 1350.0, -# "negotiable": False, -# "expires_at": "2025-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Sublet", -# "buyers": [], -# "tags": ["New", "Apartment"], -# "favorites": [], -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "2025-05-31T00:00:00-04:00", -# } -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["item.tags", "item.images", "item.buyers", "item.favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) -# response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["item.tags", "item.images", "item.buyers", "item.favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - -# def test_update_sublet_non_sublet_category(self): -# payload = { -# "item": { -# "images": [], -# "title": "Cira Green Sublet 2", -# "description": ( -# "Fully furnished 3-bedroom apartment " -# "available for sublet with all amenities included." -# ), -# "external_link": "https://example.com/listing", -# "price": 1350.0, -# "negotiable": False, -# "expires_at": "2025-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Book", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "2025-05-31T00:00:00-04:00", -# } -# response = self.client.patch( -# f"/market/sublets/{self.sublets[0].id}/", payload, format="json" -# ) -# expected_response = { -# "id": self.sublets[0].id, -# "item": { -# "id": self.items[0].id, -# "images": [], -# "title": "Cira Green Sublet 2", -# "description": ( -# "Fully furnished 3-bedroom apartment " -# "available for sublet with all amenities included." -# ), -# "external_link": "https://example.com/listing", -# "price": 1350.0, -# "negotiable": False, -# "expires_at": "2025-12-12T00:00:00-05:00", -# "seller": self.users[0].id, -# "category": "Book", -# "buyers": [], -# "tags": ["New"], -# "favorites": [], -# }, -# "address": "3901 Locust Walk, Philadelphia, PA", -# "beds": 4.0, -# "baths": 1.0, -# "start_date": "2024-01-01T00:00:00-05:00", -# "end_date": "2025-05-31T00:00:00-04:00", -# } -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), -# expected_response, -# ["item.created_at"], -# ["item.tags", "item.images", "item.buyers", "item.favorites"], -# ) -# self.assertLessEqual( -# abs( -# datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) -# - datetime.datetime.now(pytz.timezone("UTC")) -# ), -# datetime.timedelta(minutes=10), -# ) - - -# class TestOffer(BaseMarketTest): -# def setUp(self): -# super().setUp() -# self.tags = super().load_tags() -# self.categories = self.load_categories() -# self.users = [ -# self.load_user("user", "user@gmail.com", "user", True, True), -# self.load_user("user1", "user1@gmail.com", "user1", False, False), -# ] -# self.items = self.load_items( -# "tests/market/self_user_items.json", self.users[0] -# ) + self.load_items("tests/market/user_1_items.json", self.users[1]) -# items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) -# items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) -# self.items += items1 + items2 -# self.sublets = sublets1 + sublets2 -# self.offers = [ -# Offer.objects.create( -# user=self.users[0], -# item=self.items[1], -# email="self_user@gmail.com", -# ), -# Offer.objects.create( -# user=self.users[0], -# item=self.items[4], -# email="self_user@gmail.com", -# phone_number="+15555555555", -# message="I want this", -# ), -# Offer.objects.create( -# user=self.users[1], -# item=self.items[0], -# email="user1@gmail.com", -# phone_number="+15555555555", -# message="", -# ), -# ] - -# def test_get_all_user_offers(self): -# response = self.client.get("/market/offers/made/") -# expected_response = [ -# { -# "id": self.offers[0].id, -# "phone_number": None, -# "email": "self_user@gmail.com", -# "message": "", -# "user": self.users[0].id, -# "item": self.items[1].id, -# }, -# { -# "id": self.offers[1].id, -# "phone_number": "+15555555555", -# "email": "self_user@gmail.com", -# "message": "I want this", -# "user": self.users[0].id, -# "item": self.items[4].id, -# }, -# ] -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys( -# response.json(), expected_response, ["created_at"], [""] -# ) - -# def test_list_item_offers(self): -# response = self.client.get(f"/market/items/{self.items[0].id}/offers/") -# expected = [ -# { -# "id": self.offers[2].id, -# "phone_number": "+15555555555", -# "email": "user1@gmail.com", -# "message": "", -# "user": self.users[1].id, -# "item": self.items[0].id, -# } -# ] -# self.assertEqual(response.status_code, 200) -# self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) - -# def test_get_offer_other(self): -# response = self.client.get(f"/market/items/{self.items[1].id}/offers/") -# expected = {"detail": "You do not have permission to perform this action."} -# self.assertEqual(response.status_code, 403) -# self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) - -# def test_list_item_offers_invalid_item(self): -# invalid_id = 1 -# while Item.objects.filter(id=invalid_id).exists(): -# invalid_id += 1 -# response = self.client.get(f"/market/items/{invalid_id}/offers/") -# self.assertEqual(response.status_code, 404) -# self.assertEqual(response.json(), {"detail": "No Item matches the given query"}) - -# def test_create_offer_all_fields(self): -# payload = { -# "phone_number": "+1 (202) 555 0100", -# "email": "self_user@gmail.com", -# "message": "I want this", -# } -# response = self.client.post( -# f"/market/items/{self.items[2].id}/offers/", payload, format="json" -# ) -# expected_response = { -# "id": response.json()["id"], -# "phone_number": "+12025550100", -# "email": "self_user@gmail.com", -# "message": "I want this", -# "user": self.users[0].id, -# "item": self.items[2].id, -# } -# self.assertEqual(response.status_code, 201) -# self.assert_dict_equal_ignoring_keys( -# response.json(), expected_response, ["created_at"], [""] -# ) - -# def test_create_offer_required_fields(self): -# payload = {"phone_number": "+12025550100"} -# response = self.client.post( -# f"/market/items/{self.items[2].id}/offers/", payload, format="json" -# ) -# expected_response = { -# "id": response.json()["id"], -# "phone_number": "+12025550100", -# "email": None, -# "message": "", -# "user": self.users[0].id, -# "item": self.items[2].id, -# } -# self.assertEqual(response.status_code, 201) -# self.assert_dict_equal_ignoring_keys( -# response.json(), expected_response, ["created_at"], [""] -# ) - -# def test_create_offer_invalid(self): -# payload = {} -# response = self.client.post( -# f"/market/items/{self.items[2].id}/offers/", payload, format="json" -# ) -# expected_response = {"phone_number": ["This field is required."]} -# self.assertEqual(response.status_code, 400) -# self.assertEqual(response.json(), expected_response) - -# def test_create_offer_existing(self): -# payload = { -# "phone_number": "+1 (202) 555 0100", -# "email": "self_user@gmail.com", -# "message": "I want this", -# } -# response = self.client.post( -# f"/market/items/{self.items[1].id}/offers/", payload, format="json" -# ) -# expected_response = ["Offer already exists"] -# self.assertEqual(response.status_code, 400) -# self.assertEqual(response.json(), expected_response) - -# def test_delete_offer(self): -# self.assertTrue(Offer.objects.filter(id=self.offers[0].id).exists()) -# response = self.client.delete(f"/market/items/{self.items[1].id}/offers/") -# self.assertEqual(response.status_code, 204) -# self.assertFalse(Offer.objects.filter(id=self.offers[0].id).exists()) - -# def test_delete_offer_nonexistent(self): -# invalid_id = 1 -# while Offer.objects.filter(id=invalid_id).exists(): -# invalid_id += 1 -# response = self.client.delete(f"/market/items/{invalid_id}/offers/") -# self.assertEqual(response.status_code, 404) +class TestItemGet(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + + def test_get_items(self): + response = self.client.get("/market/items/") + expected_response = [ + { + "id": self.items[0].id, + "seller": self.users[0].id, + "tags": ["Textbook", "Used"], + "category": "Book", + "title": "Math Textbook", + "price": 20.0, + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + "favorite_count": 0, + }, + { + "id": self.items[1].id, + "seller": self.users[1].id, + "tags": ["Laptop", "New"], + "category": "Electronics", + "title": "Macbook Pro", + "price": 2000.0, + "expires_at": "3000-08-12T01:00:00-04:00", + "images": [], + "favorite_count": 0, + }, + { + "id": self.items[2].id, + "seller": self.users[1].id, + "tags": ["Couch"], + "category": "Furniture", + "title": "Couch", + "price": 400.0, + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + "favorite_count": 0, + }, + ] + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, [], ["", "tags", "images"] + ) + + def test_get_item_seller(self): + response = self.client.get("/market/items/?seller=true") + expected_response = [ + { + "id": self.items[0].id, + "seller": self.users[0].id, + "tags": ["Textbook", "Used"], + "category": "Book", + "title": "Math Textbook", + "price": 20.0, + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + "favorite_count": 0, + } + ] + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, [], ["", "tags", "images"] + ) + + def test_get_single_item_own(self): + response = self.client.get(f"/market/items/{self.items[0].id}/") + response_json = response.json() + expected_response = { + "id": self.items[0].id, + "images": [], + "title": "Math Textbook", + "description": "2023 version", + "external_link": "https://example.com/book", + "price": 20.0, + "negotiable": True, + "created_at": "2025-09-04T14:01:34.530659-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["Textbook", "Used"], + "favorites": [], + } + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response_json, + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response_json["created_at"]) + - datetime.datetime.now(pytz.UTC) + ), + datetime.timedelta(minutes=10), + ) + + def test_get_single_item_other(self): + response = self.client.get(f"/market/items/{self.items[1].id}/") + expected_response = { + "id": self.items[1].id, + "seller": self.users[1].id, + "buyer_count": 0, + "tags": ["Laptop", "New"], + "category": "Electronics", + "title": "Macbook Pro", + "description": "M1 Chip", + "external_link": "https://example.com/macbook", + "price": 2000.0, + "negotiable": True, + "expires_at": "3000-08-12T01:00:00-04:00", + "images": [], + "favorite_count": 0, + } + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, [], ["", "tags", "images"] + ) + + +class TestItemPost(BaseMarketTest): + + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + + def test_create_item_all_fields(self): + payload = { + "id": 88, + "seller": self.users[1].id, + "buyers": [], + "tags": ["New"], + "category": "Book", + "title": "Math Textbook", + "description": "2023 version", + "external_link": "https://example.com/listing", + "price": 20.0, + "negotiable": True, + "created_at": "2024-11-26T00:50:03.217587-05:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + } + response = self.client.post("/market/items/", payload, format="json") + expected_response = { + "id": int(f"{response.json()['id']}"), + "images": [], + "title": "Math Textbook", + "description": "2023 version", + "external_link": "https://example.com/listing", + "price": 20.0, + "negotiable": True, + "created_at": "2025-09-04T15:00:58.895709-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["New"], + "favorites": [], + } + self.assertEqual(response.status_code, 201) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.UTC) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/items/{response.json()['id']}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_create_item_exclude_unrequired(self): + payload = { + "tags": ["New"], + "category": "Book", + "title": "Math Textbook", + "description": "2023 version", + "price": 20.0, + "negotiable": True, + "expires_at": "3000-12-12T00:00:00-05:00", + } + response = self.client.post("/market/items/", payload, format="json") + expected_response = { + "id": int(f"{response.json()['id']}"), + "images": [], + "title": "Math Textbook", + "description": "2023 version", + "external_link": None, + "price": 20.0, + "negotiable": True, + "created_at": "2025-09-04T15:00:58.895709-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["New"], + "favorites": [], + } + self.assertEqual(response.status_code, 201) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.UTC) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/items/{response.json()['id']}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_create_item_missing_field(self): + payload = { + "tags": ["New"], + "category": "Book", + "external_link": "https://example.com/listing", + "price": 20.0, + "negotiable": True, + "expires_at": "3000-12-12T00:00:00-05:00", + } + response = self.client.post("/market/items/", payload) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), {"title": ["This field is required."]}) + + def test_create_item_invalid_category(self): + payload = { + "tags": ["New"], + "category": "Textbook", + "title": "Math Textbook", + "description": "2023 version", + "external_link": "https://example.com/listing", + "price": 20.0, + "negotiable": True, + "expires_at": "3000-12-12T00:00:00-05:00", + } + response = self.client.post("/market/items/", payload) + res_json = json.loads(response.content) + self.assertEqual(response.status_code, 400) + self.assertEqual( + res_json, + {"category": ['Invalid pk "Textbook" - object does not exist.']}, + ) + + def test_create_item_with_profanity_title(self): + payload = { + "tags": ["New"], + "category": "Book", + "title": "Fuck Textbook", + "description": "Fuck 2023 version", + "external_link": "https://example.com/listing", + "price": 20.0, + "negotiable": True, + "expires_at": "3000-12-12T00:00:00-05:00", + } + response = self.client.post("/market/items/", payload) + self.assertEqual(response.status_code, 400) + + res_json = response.json() + self.assertIn("title", res_json) + self.assertIn("description", res_json) + self.assertEqual(res_json["title"][0], "The title contains inappropriate language.") + self.assertEqual( + res_json["description"][0], + "The description contains inappropriate language.", + ) + + +class TestItemPatch(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + + def test_update_item_minimum_required(self): + payload = { + "category": "Book", + "title": "Physics Textbook", + "price": 25.0, + "expires_at": "3000-12-13T00:00:00-05:00", + } + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) + expected_response = { + "id": self.items[0].id, + "seller": self.users[0].id, + "buyers": [], + "tags": ["Used", "Textbook"], + "category": "Book", + "title": "Physics Textbook", + "description": "2023 version", + "external_link": "https://example.com/book", + "price": 25.0, + "negotiable": True, + "expires_at": "3000-12-13T00:00:00-05:00", + "images": [], + "favorites": [], + } + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "favorites", "buyers"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/items/{self.items[0].id}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "favorites", "buyers"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_update_item_all_fields(self): + payload = { + "id": 7, + "seller": self.users[1].id, + "buyers": [], + "tags": ["New"], + "category": "Food", + "title": "5 meal swipes", + "description": "5 meal swipes for sale", + "external_link": "https://example.com/meal-swipes", + "price": 25.0, + "negotiable": False, + "created_at": "2024-11-26T00:50:03.217587-05:00", + "expires_at": "3000-12-14T00:00:00-05:00", + "images": [], + } + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) + expected_response = { + "id": self.items[0].id, + "seller": self.users[0].id, + "buyers": [], + "tags": ["New"], + "category": "Food", + "title": "5 meal swipes", + "description": "5 meal swipes for sale", + "external_link": "https://example.com/meal-swipes", + "price": 25.0, + "negotiable": False, + "expires_at": "3000-12-14T00:00:00-05:00", + "images": [], + "favorites": [], + } + + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/items/{self.items[0].id}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_update_item_invalid_category(self): + payload = { + "category": "Textbook", + "title": "Physics Textbook", + "price": 25.0, + "expires_at": "3000-12-13T00:00:00-05:00", + } + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) + self.assertEqual(response.status_code, 400) + self.assertEqual( + response.json(), + {"category": ['Invalid pk "Textbook" - object does not exist.']}, + ) + + def test_update_item_invalid_tag(self): + payload = { + "tags": ["Not a tag"], + "category": "Book", + "title": "Physics Textbook", + "price": 25.0, + "expires_at": "3000-12-13T00:00:00-05:00", + } + response = self.client.patch(f"/market/items/{self.items[0].id}/", payload) + self.assertEqual(response.status_code, 400) + self.assertEqual( + response.json(), + {"tags": ['Invalid pk "Not a tag" - object does not exist.']}, + ) + + def test_update_item_to_sublet(self): + response = self.client.patch(f"/market/items/{self.items[0].id}/", {"category": "Sublet"}) + expected_response = { + "id": self.items[0].id, + "images": [], + "title": "Math Textbook", + "description": "2023 version", + "external_link": "https://example.com/book", + "price": 20.0, + "negotiable": True, + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Sublet", + "buyers": [], + "tags": ["Textbook", "Used"], + "favorites": [], + } + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["tags", "images", "buyers", "favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_update_item_not_owned(self): + payload = {"title": "New Title"} + response = self.client.patch(f"/market/items/{self.items[1].id}/", payload, format="json") + self.assertEqual(response.status_code, 403) + self.assertEqual( + response.json(), + {"detail": "You do not have permission to perform this action."}, + ) + + +class TestItemDelete(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + + def test_delete_item(self): + self.assertTrue(Item.objects.filter(id=self.items[0].id).exists()) + response = self.client.delete(f"/market/items/{self.items[0].id}/") + self.assertEqual(response.status_code, 204) + self.assertFalse(Item.objects.filter(id=self.items[0].id).exists()) + + def test_delete_item_not_owned(self): + response = self.client.delete(f"/market/items/{self.items[1].id}/") + self.assertEqual(response.status_code, 403) + self.assertEqual( + response.json(), + {"detail": "You do not have permission to perform this action."}, + ) + + +class TestSubletGet(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) + items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) + self.items = items1 + items2 + self.sublets = sublets1 + sublets2 + + def test_get_sublets(self): + response = self.client.get("/market/sublets/") + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.json()), 2) + expected_response = [ + { + "id": self.sublets[0].id, + "item": { + "id": self.items[0].id, + "seller": self.users[0].id, + "tags": ["New"], + "category": "Sublet", + "title": "Cira Green Sublet", + "price": 1350.0, + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + "favorite_count": 0, + }, + "address": "Cira Green, Philadelphia, PA", + "beds": 3.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "3000-05-31T00:00:00-04:00", + }, + { + "id": self.sublets[1].id, + "item": { + "id": self.items[1].id, + "seller": self.users[1].id, + "tags": ["New"], + "category": "Sublet", + "title": "Rodin Quad", + "price": 1350.0, + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + "favorite_count": 0, + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "3000-05-31T00:00:00-04:00", + }, + ] + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["created_at"], + ["", "tags", "images", "buyers", "favorites"], + ) + + def test_get_sublet_own(self): + response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") + self.assertEqual(response.status_code, 200) + expected_response = { + "id": self.sublets[0].id, + "item": { + "id": self.items[0].id, + "images": [], + "title": "Cira Green Sublet", + "description": "Fully furnished 3-bedroom apartment available for sublet with all" + + " amenities included.", + "external_link": "https://example.com/cira-green", + "price": 1350.0, + "negotiable": False, + "created_at": "2025-09-05T00:14:15.141807-04:00", + "expires_at": "3000-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Sublet", + "buyers": [], + "tags": ["New"], + "favorites": [], + }, + "address": "Cira Green, Philadelphia, PA", + "beds": 3.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "3000-05-31T00:00:00-04:00", + } + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["", "item.tags", "item.images", "item.buyers", "item.favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_get_sublet_other(self): + response = self.client.get(f"/market/sublets/{self.sublets[1].id}/") + self.assertEqual(response.status_code, 200) + expected_response = { + "id": self.sublets[1].id, + "item": { + "id": self.items[1].id, + "seller": self.users[1].id, + "buyer_count": 0, + "tags": ["New"], + "category": "Sublet", + "title": "Rodin Quad", + "description": "Fully furnished 4-bedroom apartment available for sublet with all" + + " amenities included.", + "external_link": "https://example.com/rodin-quad", + "price": 1350.0, + "negotiable": False, + "expires_at": "3000-12-12T00:00:00-05:00", + "images": [], + "favorite_count": 0, + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "3000-05-31T00:00:00-04:00", + } + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["", "item.tags", "item.images", "item.buyers", "item.favorites"], + ) + + def test_get_single_sublet_invalid_id(self): + response = self.client.get(f"/market/sublets/{self.sublets[1].id+1}/") + self.assertEqual(response.status_code, 404) + + +class TestSubletPost(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + + def test_create_sublet(self): + payload = { + "id": 3, + "item": { + "id": 7, + "images": [], + "title": "Cira Green Sublet 2", + "description": ( + "Fully furnished 3-bedroom apartment available" + " for sublet with all amenities included." + ), + "external_link": "https://example.com/listing", + "price": 1350.0, + "negotiable": False, + "expires_at": "2025-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Sublet", + "buyers": [], + "tags": ["New"], + "favorites": [], + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "3000-05-31T00:00:00-04:00", + } + response = self.client.post("/market/sublets/", payload, format="json") + expected_response = { + "id": response.json()["id"], + "item": { + "id": response.json()["item"]["id"], + "images": [], + "title": "Cira Green Sublet 2", + "description": "Fully furnished 3-bedroom apartment available for sublet with all" + + " amenities included.", + "external_link": "https://example.com/listing", + "price": 1350.0, + "negotiable": False, + "created_at": "2025-09-05T00:48:08.880144-04:00", + "expires_at": "2025-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Sublet", + "buyers": [], + "tags": ["New"], + "favorites": [], + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "3000-05-31T00:00:00-04:00", + } + self.assertEqual(response.status_code, 201) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/sublets/{response.json()['id']}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + +class TestSubletPatchDelete(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) + items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) + self.items = items1 + items2 + self.sublets = sublets1 + sublets2 + + def test_update_sublet(self): + payload = { + "id": 3, + "item": { + "id": 7, + "images": [], + "title": "Cira Green Sublet 2", + "description": ( + "Fully furnished 3-bedroom apartment " + "available for sublet with all amenities included." + ), + "external_link": "https://example.com/listing", + "price": 1350.0, + "negotiable": False, + "expires_at": "2025-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Sublet", + "buyers": [], + "tags": ["Apartment", "New"], + "favorites": [], + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "2025-05-31T00:00:00-04:00", + } + response = self.client.patch( + f"/market/sublets/{self.sublets[0].id}/", payload, format="json" + ) + expected_response = { + "id": int(self.sublets[0].id), + "item": { + "id": int(self.items[0].id), + "images": [], + "title": "Cira Green Sublet 2", + "description": ( + "Fully furnished 3-bedroom apartment " + "available for sublet with all amenities included." + ), + "external_link": "https://example.com/listing", + "price": 1350.0, + "negotiable": False, + "expires_at": "2025-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Sublet", + "buyers": [], + "tags": ["New", "Apartment"], + "favorites": [], + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "2025-05-31T00:00:00-04:00", + } + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + response = self.client.get(f"/market/sublets/{self.sublets[0].id}/") + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + def test_update_sublet_non_sublet_category(self): + payload = { + "item": { + "images": [], + "title": "Cira Green Sublet 2", + "description": ( + "Fully furnished 3-bedroom apartment " + "available for sublet with all amenities included." + ), + "external_link": "https://example.com/listing", + "price": 1350.0, + "negotiable": False, + "expires_at": "2025-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["New"], + "favorites": [], + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "2025-05-31T00:00:00-04:00", + } + response = self.client.patch( + f"/market/sublets/{self.sublets[0].id}/", payload, format="json" + ) + expected_response = { + "id": self.sublets[0].id, + "item": { + "id": self.items[0].id, + "images": [], + "title": "Cira Green Sublet 2", + "description": ( + "Fully furnished 3-bedroom apartment " + "available for sublet with all amenities included." + ), + "external_link": "https://example.com/listing", + "price": 1350.0, + "negotiable": False, + "expires_at": "2025-12-12T00:00:00-05:00", + "seller": self.users[0].id, + "category": "Book", + "buyers": [], + "tags": ["New"], + "favorites": [], + }, + "address": "3901 Locust Walk, Philadelphia, PA", + "beds": 4.0, + "baths": 1.0, + "start_date": "2024-01-01T00:00:00-05:00", + "end_date": "2025-05-31T00:00:00-04:00", + } + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), + expected_response, + ["item.created_at"], + ["item.tags", "item.images", "item.buyers", "item.favorites"], + ) + self.assertLessEqual( + abs( + datetime.datetime.fromisoformat(response.json()["item"]["created_at"]) + - datetime.datetime.now(pytz.timezone("UTC")) + ), + datetime.timedelta(minutes=10), + ) + + +class TestOffer(BaseMarketTest): + def setUp(self): + super().setUp() + self.tags = super().load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] + self.items = self.load_items( + "tests/market/self_user_items.json", self.users[0] + ) + self.load_items("tests/market/user_1_items.json", self.users[1]) + items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) + items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) + self.items += items1 + items2 + self.sublets = sublets1 + sublets2 + self.offers = [ + Offer.objects.create( + user=self.users[0], + item=self.items[1], + email="self_user@gmail.com", + ), + Offer.objects.create( + user=self.users[0], + item=self.items[4], + email="self_user@gmail.com", + phone_number="+15555555555", + message="I want this", + ), + Offer.objects.create( + user=self.users[1], + item=self.items[0], + email="user1@gmail.com", + phone_number="+15555555555", + message="", + ), + ] + + def test_get_all_user_offers(self): + response = self.client.get("/market/offers/made/") + expected_response = [ + { + "id": self.offers[0].id, + "phone_number": None, + "email": "self_user@gmail.com", + "message": "", + "user": self.users[0].id, + "item": self.items[1].id, + }, + { + "id": self.offers[1].id, + "phone_number": "+15555555555", + "email": "self_user@gmail.com", + "message": "I want this", + "user": self.users[0].id, + "item": self.items[4].id, + }, + ] + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, ["created_at"], [""] + ) + + def test_list_item_offers(self): + response = self.client.get(f"/market/items/{self.items[0].id}/offers/") + expected = [ + { + "id": self.offers[2].id, + "phone_number": "+15555555555", + "email": "user1@gmail.com", + "message": "", + "user": self.users[1].id, + "item": self.items[0].id, + } + ] + self.assertEqual(response.status_code, 200) + self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) + + def test_get_offer_other(self): + response = self.client.get(f"/market/items/{self.items[1].id}/offers/") + expected = {"detail": "You do not have permission to perform this action."} + self.assertEqual(response.status_code, 403) + self.assert_dict_equal_ignoring_keys(response.json(), expected, ["created_at"], [""]) + + def test_list_item_offers_invalid_item(self): + invalid_id = 1 + while Item.objects.filter(id=invalid_id).exists(): + invalid_id += 1 + response = self.client.get(f"/market/items/{invalid_id}/offers/") + self.assertEqual(response.status_code, 404) + self.assertEqual(response.json(), {"detail": "No Item matches the given query"}) + + def test_create_offer_all_fields(self): + payload = { + "phone_number": "+1 (202) 555 0100", + "email": "self_user@gmail.com", + "message": "I want this", + } + response = self.client.post( + f"/market/items/{self.items[2].id}/offers/", payload, format="json" + ) + expected_response = { + "id": response.json()["id"], + "phone_number": "+12025550100", + "email": "self_user@gmail.com", + "message": "I want this", + "user": self.users[0].id, + "item": self.items[2].id, + } + self.assertEqual(response.status_code, 201) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, ["created_at"], [""] + ) + + def test_create_offer_required_fields(self): + payload = {"phone_number": "+12025550100"} + response = self.client.post( + f"/market/items/{self.items[2].id}/offers/", payload, format="json" + ) + expected_response = { + "id": response.json()["id"], + "phone_number": "+12025550100", + "email": None, + "message": "", + "user": self.users[0].id, + "item": self.items[2].id, + } + self.assertEqual(response.status_code, 201) + self.assert_dict_equal_ignoring_keys( + response.json(), expected_response, ["created_at"], [""] + ) + + def test_create_offer_invalid(self): + payload = {} + response = self.client.post( + f"/market/items/{self.items[2].id}/offers/", payload, format="json" + ) + expected_response = {"phone_number": ["This field is required."]} + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), expected_response) + + def test_create_offer_existing(self): + payload = { + "phone_number": "+1 (202) 555 0100", + "email": "self_user@gmail.com", + "message": "I want this", + } + response = self.client.post( + f"/market/items/{self.items[1].id}/offers/", payload, format="json" + ) + expected_response = ["Offer already exists"] + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), expected_response) + + def test_delete_offer(self): + self.assertTrue(Offer.objects.filter(id=self.offers[0].id).exists()) + response = self.client.delete(f"/market/items/{self.items[1].id}/offers/") + self.assertEqual(response.status_code, 204) + self.assertFalse(Offer.objects.filter(id=self.offers[0].id).exists()) + + def test_delete_offer_nonexistent(self): + invalid_id = 1 + while Offer.objects.filter(id=invalid_id).exists(): + invalid_id += 1 + response = self.client.delete(f"/market/items/{invalid_id}/offers/") + self.assertEqual(response.status_code, 404) class TestFavorites(BaseMarketTest): From 03ef2f6447b4a31822e26c3359994417dd02222b Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sat, 6 Sep 2025 22:17:50 -0400 Subject: [PATCH 6/7] fix tests --- backend/tests/market/test_market.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index a7cadbf3..bc292ff7 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -1283,7 +1283,7 @@ def setUp(self): def test_get_favorites_for_item_owned(self): response = self.client.get(f"/market/items/{self.items[0].id}/") expected_response = { - "id": 1, + "id": self.items[0].id, "images": [], "title": "Math Textbook", "description": "2023 version", @@ -1309,8 +1309,8 @@ def test_get_favorites_for_item_owned(self): def test_get_favorites_for_item_other(self): response = self.client.get(f"/market/items/{self.items[1].id}/") expected_response = { - "id": 2, - "seller": 2, + "id": self.items[1].id, + "seller": self.users[1].id, "buyer_count": 0, "tags": ["Laptop", "New"], "category": "Electronics", From f070107c34cba2420e2add733f6a1dc6d5f72c2b Mon Sep 17 00:00:00 2001 From: minghansun1 Date: Sun, 7 Sep 2025 13:46:55 -0400 Subject: [PATCH 7/7] removed unused stuff, increased shared setup --- backend/tests/market/mock_items.json | 46 ----------------- backend/tests/market/mock_sublets.json | 42 ---------------- backend/tests/market/test_market.py | 68 +++----------------------- 3 files changed, 7 insertions(+), 149 deletions(-) delete mode 100644 backend/tests/market/mock_items.json delete mode 100644 backend/tests/market/mock_sublets.json diff --git a/backend/tests/market/mock_items.json b/backend/tests/market/mock_items.json deleted file mode 100644 index 35a39c5b..00000000 --- a/backend/tests/market/mock_items.json +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "tags": ["Used","Textbook"], - "category": "Book", - "title": "Math Textbook", - "description": "2023 version", - "external_link": "https://example.com/book", - "price": 20.0, - "negotiable": true, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "3000-12-12T00:00:00-05:00" - }, - { - "tags": ["New"], - "category": "Food", - "title": "Bag of Doritos", - "description": "Cool Ranch", - "external_link": "https://example.com/doritos", - "price": 5.0, - "negotiable": false, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "3000-10-12T00:00:00-05:00" - }, - { - "tags": ["Laptop", "New"], - "category": "Electronics", - "title": "Macbook Pro", - "description": "M1 Chip", - "external_link": "https://example.com/macbook", - "price": 2000.0, - "negotiable": true, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "3000-08-12T00:00:00-05:00" - }, - { - "tags": ["Couch"], - "category": "Furniture", - "title": "Couch", - "description": "5 feet wide, brown", - "external_link": "https://example.com/couch", - "price": 400.0, - "negotiable": true, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "3000-12-12T00:00:00-05:00" - } -] \ No newline at end of file diff --git a/backend/tests/market/mock_sublets.json b/backend/tests/market/mock_sublets.json deleted file mode 100644 index 8f6e522b..00000000 --- a/backend/tests/market/mock_sublets.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "item": { - "tags": [ - "New" - ], - "category": "Sublet", - "title": "Cira Green Sublet", - "description": "Fully furnished 3-bedroom apartment available for sublet with all amenities included.", - "external_link": "https://example.com/cira-green", - "price": 1350.0, - "negotiable": false, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "3000-12-12T00:00:00-05:00" - }, - "address": "Cira Green, Philadelphia, PA", - "beds": 3.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00" - }, - { - "item": { - "tags": [ - "New" - ], - "category": "Sublet", - "title": "Rodin Quad", - "description": "Fully furnished 4-bedroom apartment available for sublet with all amenities included.", - "external_link": "https://example.com/rodin-quad", - "price": 1350.0, - "negotiable": false, - "created_at": "2024-11-13T20:14:34.604238-05:00", - "expires_at": "3000-12-12T00:00:00-05:00" - }, - "address": "3901 Locust Walk, Philadelphia, PA", - "beds": 4.0, - "baths": 1.0, - "start_date": "2024-01-01T00:00:00-05:00", - "end_date": "3000-05-31T00:00:00-04:00" - } -] \ No newline at end of file diff --git a/backend/tests/market/test_market.py b/backend/tests/market/test_market.py index bc292ff7..208b2e1c 100644 --- a/backend/tests/market/test_market.py +++ b/backend/tests/market/test_market.py @@ -18,6 +18,12 @@ class BaseMarketTest(TestCase): def setUp(self): self.client = APIClient() + self.tags = self.load_tags() + self.categories = self.load_categories() + self.users = [ + self.load_user("user", "user@gmail.com", "user", True, True), + self.load_user("user1", "user1@gmail.com", "user1", False, False), + ] def load_tags(self): tags = [ @@ -143,12 +149,6 @@ def normalize(obj, path=""): class TestItemGet(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1]) @@ -276,12 +276,6 @@ class TestItemPost(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] def test_create_item_all_fields(self): payload = { @@ -462,12 +456,6 @@ def test_create_item_with_profanity_title(self): class TestItemPatch(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1]) @@ -662,12 +650,6 @@ def test_update_item_not_owned(self): class TestItemDelete(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1]) @@ -690,12 +672,6 @@ def test_delete_item_not_owned(self): class TestSubletGet(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) self.items = items1 + items2 @@ -836,12 +812,6 @@ def test_get_single_sublet_invalid_id(self): class TestSubletPost(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] def test_create_sublet(self): payload = { @@ -930,12 +900,6 @@ def test_create_sublet(self): class TestSubletPatchDelete(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] items1, sublets1 = self.load_sublets("tests/market/self_user_sublets.json", self.users[0]) items2, sublets2 = self.load_sublets("tests/market/user_1_sublets.json", self.users[1]) self.items = items1 + items2 @@ -1100,12 +1064,6 @@ def test_update_sublet_non_sublet_category(self): class TestOffer(BaseMarketTest): def setUp(self): super().setUp() - self.tags = super().load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1]) @@ -1268,12 +1226,6 @@ def test_delete_offer_nonexistent(self): class TestFavorites(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1]) @@ -1348,7 +1300,7 @@ def test_delete_favorite(self): self.assertEqual(response.status_code, 204) self.assertEqual(Item.objects.get(id=self.items[1].id).favorites.count(), 0) - def test_delete_nonexistant_favorite(self): + def test_delete_nonexistent_favorite(self): response = self.client.delete(f"/market/items/{self.items[2].id}/favorites/") self.assertEqual(response.status_code, 404) self.assertEqual(Item.objects.get(id=self.items[2].id).favorites.count(), 0) @@ -1358,12 +1310,6 @@ def test_delete_nonexistant_favorite(self): class TestImages(BaseMarketTest): def setUp(self): super().setUp() - self.tags = self.load_tags() - self.categories = self.load_categories() - self.users = [ - self.load_user("user", "user@gmail.com", "user", True, True), - self.load_user("user1", "user1@gmail.com", "user1", False, False), - ] self.items = self.load_items( "tests/market/self_user_items.json", self.users[0] ) + self.load_items("tests/market/user_1_items.json", self.users[1])