@@ -91,3 +91,196 @@ async def mock_get(url, **kwargs):
9191
9292 assert report .found is True
9393 assert "well-known" in report .url
94+
95+
96+ # ── llms-full.txt Tests ──────────────────────────────────────────────────────
97+
98+
99+ @pytest .mark .asyncio
100+ async def test_llms_full_txt_found_at_root ():
101+ """llms-full.txt at /llms-full.txt should be detected."""
102+ async def mock_get (url , ** kwargs ):
103+ resp = AsyncMock ()
104+ if "llms-full.txt" in url and "well-known" not in url :
105+ resp .status_code = 200
106+ resp .text = "# Full LLMs content"
107+ elif "/llms.txt" in url and "full" not in url :
108+ resp .status_code = 200
109+ resp .text = "# LLMs.txt"
110+ else :
111+ resp .status_code = 404
112+ resp .text = ""
113+ return resp
114+
115+ mock_client = AsyncMock (spec = httpx .AsyncClient )
116+ mock_client .get = AsyncMock (side_effect = mock_get )
117+
118+ report = await check_llms_txt ("https://example.com" , mock_client )
119+
120+ assert report .found is True
121+ assert report .llms_full_found is True
122+ assert report .llms_full_url == "https://example.com/llms-full.txt"
123+
124+
125+ @pytest .mark .asyncio
126+ async def test_llms_full_txt_found_at_well_known ():
127+ """llms-full.txt at /.well-known/llms-full.txt should be detected."""
128+ async def mock_get (url , ** kwargs ):
129+ resp = AsyncMock ()
130+ if "well-known/llms-full.txt" in url :
131+ resp .status_code = 200
132+ resp .text = "# Full LLMs content"
133+ elif "/llms.txt" in url and "full" not in url :
134+ resp .status_code = 200
135+ resp .text = "# LLMs.txt"
136+ else :
137+ resp .status_code = 404
138+ resp .text = ""
139+ return resp
140+
141+ mock_client = AsyncMock (spec = httpx .AsyncClient )
142+ mock_client .get = AsyncMock (side_effect = mock_get )
143+
144+ report = await check_llms_txt ("https://example.com" , mock_client )
145+
146+ assert report .found is True
147+ assert report .llms_full_found is True
148+ assert report .llms_full_url == "https://example.com/.well-known/llms-full.txt"
149+
150+
151+ @pytest .mark .asyncio
152+ async def test_only_llms_full_txt_found ():
153+ """Only llms-full.txt present (no llms.txt) should still score."""
154+ async def mock_get (url , ** kwargs ):
155+ resp = AsyncMock ()
156+ if "llms-full.txt" in url and "well-known" not in url :
157+ resp .status_code = 200
158+ resp .text = "# Full LLMs content"
159+ else :
160+ resp .status_code = 404
161+ resp .text = ""
162+ return resp
163+
164+ mock_client = AsyncMock (spec = httpx .AsyncClient )
165+ mock_client .get = AsyncMock (side_effect = mock_get )
166+
167+ report = await check_llms_txt ("https://example.com" , mock_client )
168+
169+ assert report .found is False # llms.txt not found
170+ assert report .llms_full_found is True
171+ assert report .llms_full_url == "https://example.com/llms-full.txt"
172+ assert "llms-full.txt" in report .detail
173+
174+
175+ @pytest .mark .asyncio
176+ async def test_both_llms_and_llms_full_found ():
177+ """Both llms.txt and llms-full.txt present should be reported."""
178+ async def mock_get (url , ** kwargs ):
179+ resp = AsyncMock ()
180+ resp .status_code = 200
181+ resp .text = "# Content"
182+ return resp
183+
184+ mock_client = AsyncMock (spec = httpx .AsyncClient )
185+ mock_client .get = AsyncMock (side_effect = mock_get )
186+
187+ report = await check_llms_txt ("https://example.com" , mock_client )
188+
189+ assert report .found is True
190+ assert report .llms_full_found is True
191+ assert "llms.txt" in report .detail
192+ assert "llms-full.txt" in report .detail
193+
194+
195+ @pytest .mark .asyncio
196+ async def test_llms_full_txt_not_found ():
197+ """When only llms.txt exists, llms_full_found should be False."""
198+ async def mock_get (url , ** kwargs ):
199+ resp = AsyncMock ()
200+ if "/llms.txt" in url and "full" not in url and "well-known" not in url :
201+ resp .status_code = 200
202+ resp .text = "# LLMs.txt"
203+ else :
204+ resp .status_code = 404
205+ resp .text = ""
206+ return resp
207+
208+ mock_client = AsyncMock (spec = httpx .AsyncClient )
209+ mock_client .get = AsyncMock (side_effect = mock_get )
210+
211+ report = await check_llms_txt ("https://example.com" , mock_client )
212+
213+ assert report .found is True
214+ assert report .llms_full_found is False
215+ assert report .llms_full_url is None
216+
217+
218+ @pytest .mark .asyncio
219+ async def test_llms_full_txt_empty_not_counted ():
220+ """An empty llms-full.txt should not count as found."""
221+ async def mock_get (url , ** kwargs ):
222+ resp = AsyncMock ()
223+ if "llms-full.txt" in url :
224+ resp .status_code = 200
225+ resp .text = " \n "
226+ elif "/llms.txt" in url :
227+ resp .status_code = 200
228+ resp .text = "# LLMs.txt"
229+ else :
230+ resp .status_code = 404
231+ resp .text = ""
232+ return resp
233+
234+ mock_client = AsyncMock (spec = httpx .AsyncClient )
235+ mock_client .get = AsyncMock (side_effect = mock_get )
236+
237+ report = await check_llms_txt ("https://example.com" , mock_client )
238+
239+ assert report .found is True
240+ assert report .llms_full_found is False
241+
242+
243+ @pytest .mark .asyncio
244+ async def test_llms_full_txt_http_error ():
245+ """HTTP error on llms-full.txt should not crash; llms.txt still found."""
246+ call_count = 0
247+
248+ async def mock_get (url , ** kwargs ):
249+ nonlocal call_count
250+ call_count += 1
251+ if "llms-full.txt" in url :
252+ raise httpx .ConnectError ("Connection refused" )
253+ resp = AsyncMock ()
254+ if "/llms.txt" in url and "full" not in url and "well-known" not in url :
255+ resp .status_code = 200
256+ resp .text = "# LLMs.txt"
257+ else :
258+ resp .status_code = 404
259+ resp .text = ""
260+ return resp
261+
262+ mock_client = AsyncMock (spec = httpx .AsyncClient )
263+ mock_client .get = AsyncMock (side_effect = mock_get )
264+
265+ report = await check_llms_txt ("https://example.com" , mock_client )
266+
267+ assert report .found is True
268+ assert report .llms_full_found is False
269+
270+
271+ @pytest .mark .asyncio
272+ async def test_neither_llms_found ():
273+ """Neither llms.txt nor llms-full.txt found should report not found."""
274+ mock_response = AsyncMock ()
275+ mock_response .status_code = 404
276+ mock_response .text = ""
277+
278+ mock_client = AsyncMock (spec = httpx .AsyncClient )
279+ mock_client .get = AsyncMock (return_value = mock_response )
280+
281+ report = await check_llms_txt ("https://example.com" , mock_client )
282+
283+ assert report .found is False
284+ assert report .llms_full_found is False
285+ assert report .url is None
286+ assert report .llms_full_url is None
0 commit comments