"""Integration tests for the Bookmarks REST API (/api/bookmarks).""" import pytest # a minimal full-export object as produced by x-bookmarks-exporter SAMPLE_EXPORT = { "id": "1234567890", "url": "https://x.com/user/status/1234567890", "author": { "screen_name": "testuser", "name": "Test User", "profile_image_url": "https://example.com/avatar.jpg", }, "text": "Hello world", "media": [{"type": "photo", "media_url": "https://example.com/p.jpg", "media_large": "https://example.com/p.jpg"}], "card": None, "quoted": None, "retweet": None, "favorite_count": 7, "view_count": 21, "bookmark_count": 1, } @pytest.mark.django_db class TestBookmarksAPI: BASE = "/api/bookmarks" def test_list_empty(self, api_client): r = api_client.get(f"{self.BASE}") assert r.status_code == 200 assert r.json()["count"] == 0 def test_create_then_list_and_detail(self, api_client): r = api_client.post(self.BASE, { "tweet_id": "111", "url": "https://x.com/u/status/111", "author_screen_name": "u", "text": "hi", "has_photo": True, }, format="json") assert r.status_code == 201, r.content created = r.json() assert created["tweet_id"] == "111" list_r = api_client.get(self.BASE) assert list_r.json()["count"] == 1 det = api_client.get(f"{self.BASE}/{created['id']}") assert det.status_code == 200 assert det.json()["has_photo"] is True def test_filter_has_video(self, api_client): api_client.post(self.BASE, {"tweet_id": "v1", "has_video": True}, format="json") api_client.post(self.BASE, {"tweet_id": "p1", "has_photo": True}, format="json") r = api_client.get(f"{self.BASE}", {"has_video": "true"}) assert r.json()["count"] == 1 assert r.json()["results"][0]["tweet_id"] == "v1" def test_search_q(self, api_client): api_client.post(self.BASE, {"tweet_id": "a1", "text": "python tips"}, format="json") api_client.post(self.BASE, {"tweet_id": "a2", "text": "rust tricks"}, format="json") r = api_client.get(f"{self.BASE}", {"q": "python"}) assert r.json()["count"] == 1 assert r.json()["results"][0]["tweet_id"] == "a1" def test_update_summary(self, api_client): created = api_client.post(self.BASE, {"tweet_id": "u1"}, format="json").json() r = api_client.patch(f"{self.BASE}/{created['id']}", {"summary": "AI summary"}, format="json") assert r.status_code == 200 assert r.json()["summary"] == "AI summary" def test_delete(self, api_client): created = api_client.post(self.BASE, {"tweet_id": "d1"}, format="json").json() r = api_client.delete(f"{self.BASE}/{created['id']}") assert r.status_code == 204 def test_bulk_import_upserts(self, api_client): r = api_client.post(f"{self.BASE}/import", {"bookmarks": [SAMPLE_EXPORT]}, format="json") assert r.status_code == 200, r.content assert r.json()["created"] == 1 # second import with changed content → update r2 = api_client.post(f"{self.BASE}/import", {"bookmarks": [ {**SAMPLE_EXPORT, "text": "updated"}]}, format="json") assert r2.json()["updated"] == 1 assert r2.json()["created"] == 0 from links.models import Bookmark assert Bookmark.objects.count() == 1 assert Bookmark.objects.first().text == "updated" def test_bulk_import_unchanged_skips_save(self, api_client): """Re-importing identical content should count as 'unchanged', not 'updated'.""" # first import r1 = api_client.post(f"{self.BASE}/import", {"bookmarks": [SAMPLE_EXPORT]}, format="json") assert r1.json()["created"] == 1 # second import with identical content r2 = api_client.post(f"{self.BASE}/import", {"bookmarks": [SAMPLE_EXPORT]}, format="json") assert r2.status_code == 200, r2.content assert r2.json()["unchanged"] == 1 assert r2.json()["updated"] == 0 assert r2.json()["created"] == 0 from links.models import Bookmark bm = Bookmark.objects.first() # last_imported_at should be set even when unchanged assert bm.last_imported_at is not None # content_hash should be populated assert bm.content_hash != '' def test_bulk_upsert_array_unchanged(self, api_client): """Bulk array endpoint should also detect unchanged content.""" r1 = api_client.post(f"{self.BASE}/bulk", [{"tweet_id": "ub1", "text": "same"}], format="json") assert r1.json()["created"] == 1 # re-import identical r2 = api_client.post(f"{self.BASE}/bulk", [{"tweet_id": "ub1", "text": "same"}], format="json") assert r2.json()["unchanged"] == 1 assert r2.json()["updated"] == 0 def test_bulk_upsert_array(self, api_client): r = api_client.post(f"{self.BASE}/bulk", [{"tweet_id": "b1", "text": "one"}, {"tweet_id": "b2", "text": "two"}], format="json") assert r.status_code == 200 assert r.json()["created"] == 2 r2 = api_client.post(f"{self.BASE}/bulk", [{"tweet_id": "b1", "text": "one-edit"}], format="json") assert r2.json()["updated"] == 1 from links.models import Bookmark assert Bookmark.objects.get(tweet_id="b1").text == "one-edit" def test_stats(self, api_client): api_client.post(self.BASE, {"tweet_id": "s1", "has_video": True}, format="json") api_client.post(self.BASE, {"tweet_id": "s2", "has_photo": True}, format="json") r = api_client.get(f"{self.BASE}/stats") assert r.status_code == 200 s = r.json() assert s["total"] == 2 assert s["has_video"] == 1 assert s["has_photo"] == 1