11import pytest
22
3+ from app .services .media_ref import extract_media_filenames
4+
5+
36@pytest .mark .asyncio
47async def test_media_upload_and_get (auth_client ):
58 # Mock a file upload
69 file_content = b"fake image content"
710 files = {"file" : ("test.png" , file_content , "image/png" )}
8-
11+
912 response = await auth_client .post ("/api/media/upload" , files = files )
1013 assert response .status_code == 201
1114 data = response .json ()
@@ -16,3 +19,105 @@ async def test_media_upload_and_get(auth_client):
1619 response = await auth_client .get (f"/api/media/{ filename } " )
1720 assert response .status_code == 200
1821 assert response .content == file_content
22+
23+
24+ def test_extract_media_filenames_markdown_image ():
25+ md = ""
26+ assert extract_media_filenames (md ) == {"abc123.png" }
27+
28+
29+ def test_extract_media_filenames_multiple_and_html ():
30+ md = """
31+ 
32+ <img src="/api/media/two.jpg" />
33+ Plain link: /api/media/three.pdf
34+ """
35+ assert extract_media_filenames (md ) == {"one.png" , "two.jpg" , "three.pdf" }
36+
37+
38+ def test_extract_media_filenames_empty ():
39+ assert extract_media_filenames ("" ) == set ()
40+ assert extract_media_filenames ("no media here" ) == set ()
41+
42+
43+ @pytest .mark .asyncio
44+ async def test_media_list_available_to_editors (auth_client ):
45+ # Any authenticated user can browse the library to reuse assets.
46+ response = await auth_client .get ("/api/media" )
47+ assert response .status_code == 200
48+ assert isinstance (response .json (), list )
49+
50+
51+ @pytest .mark .asyncio
52+ async def test_media_list_shows_upload_with_zero_refs (admin_client ):
53+ files = {"file" : ("zero.png" , b"zero" , "image/png" )}
54+ up = await admin_client .post ("/api/media/upload" , files = files )
55+ assert up .status_code == 201
56+ uploaded = up .json ()
57+
58+ response = await admin_client .get ("/api/media" )
59+ assert response .status_code == 200
60+ items = response .json ()
61+ match = next ((m for m in items if m ["id" ] == uploaded ["id" ]), None )
62+ assert match is not None
63+ assert match ["reference_count" ] == 0
64+ assert match ["referenced_pages" ] == []
65+ assert match ["url" ] == f"/api/media/{ uploaded ['filename' ]} "
66+
67+
68+ @pytest .mark .asyncio
69+ async def test_media_reference_tracking_and_delete (admin_client ):
70+ files = {"file" : ("ref.png" , b"ref" , "image/png" )}
71+ up = await admin_client .post ("/api/media/upload" , files = files )
72+ media = up .json ()
73+
74+ # Create a page that references the uploaded media
75+ page_body = {
76+ "title" : "References media" ,
77+ "content_md" : f"Here is an image: " ,
78+ }
79+ page_resp = await admin_client .post ("/api/pages" , json = page_body )
80+ assert page_resp .status_code == 201
81+ page = page_resp .json ()
82+
83+ # The list endpoint should now report 1 reference
84+ listing = await admin_client .get ("/api/media" )
85+ match = next (m for m in listing .json () if m ["id" ] == media ["id" ])
86+ assert match ["reference_count" ] == 1
87+ assert len (match ["referenced_pages" ]) == 1
88+ assert match ["referenced_pages" ][0 ]["slug" ] == page ["slug" ]
89+
90+ # Delete should be blocked while referenced
91+ blocked = await admin_client .delete (f"/api/media/{ media ['id' ]} " )
92+ assert blocked .status_code == 409
93+
94+ # Remove the reference by updating the page
95+ upd = await admin_client .put (
96+ f"/api/pages/{ page ['slug' ]} " ,
97+ json = {"content_md" : "No media anymore" },
98+ )
99+ assert upd .status_code == 200
100+
101+ listing2 = await admin_client .get ("/api/media" )
102+ match2 = next (m for m in listing2 .json () if m ["id" ] == media ["id" ])
103+ assert match2 ["reference_count" ] == 0
104+
105+ # Delete should now succeed
106+ ok = await admin_client .delete (f"/api/media/{ media ['id' ]} " )
107+ assert ok .status_code == 204
108+
109+ # The media entry is gone
110+ listing3 = await admin_client .get ("/api/media" )
111+ assert all (m ["id" ] != media ["id" ] for m in listing3 .json ())
112+
113+
114+ @pytest .mark .asyncio
115+ async def test_media_delete_non_admin_forbidden (auth_client ):
116+ response = await auth_client .delete ("/api/media/1" )
117+ assert response .status_code == 403
118+
119+
120+ @pytest .mark .asyncio
121+ async def test_media_delete_not_found (admin_client ):
122+ response = await admin_client .delete ("/api/media/99999999" )
123+ assert response .status_code == 404
0 commit comments