Pyzotero: a Python client for the Zotero API pyzotero.readthedocs.io
zotero

More tests

+214 -1
+214 -1
tests/test_zotero.py
··· 9 9 import os 10 10 import time 11 11 import unittest 12 - from unittest.mock import patch 12 + from unittest.mock import MagicMock, patch 13 13 14 14 import httpretty 15 15 from dateutil import parser ··· 1196 1196 def tearDown(self): 1197 1197 """Tear stuff down""" 1198 1198 HTTPretty.disable() 1199 + 1200 + @httpretty.activate 1201 + def test_updated_template_comparison(self): 1202 + """Test that ONE_HOUR is properly used for template freshness check""" 1203 + zot = z.Zotero("myuserID", "user", "myuserkey") 1204 + 1205 + # Test that ONE_HOUR constant matches code expectation 1206 + self.assertEqual(z.ONE_HOUR, 3600) 1207 + 1208 + # Use a simplified approach to test checking template freshness 1209 + zot.templates = {} 1210 + 1211 + # Create a template 1212 + template_name = "test_template" 1213 + HTTPretty.register_uri( 1214 + HTTPretty.GET, 1215 + "https://api.zotero.org/users/myuserID/items/new", 1216 + body=json.dumps({"success": True}), 1217 + content_type="application/json", 1218 + ) 1219 + 1220 + # The _cache method should create a template with a timestamp 1221 + zot._cache(MagicMock(json=lambda: {"data": "test"}), template_name) 1222 + 1223 + # Verify template was created with a timestamp 1224 + self.assertIn(template_name, zot.templates) 1225 + self.assertIn("updated", zot.templates[template_name]) 1226 + 1227 + @httpretty.activate 1228 + def test_template_cache_creation(self): 1229 + """Test template caching in the _cache method""" 1230 + zot = z.Zotero("myuserID", "user", "myuserkey") 1231 + 1232 + # Create a mock response to cache 1233 + mock_response = MagicMock() 1234 + mock_response.json.return_value = {"test": "data"} 1235 + 1236 + # Call the _cache method 1237 + template_name = "test_key" 1238 + result = zot._cache(mock_response, template_name) 1239 + 1240 + # Verify the template was stored correctly 1241 + self.assertIn(template_name, zot.templates) 1242 + self.assertEqual(zot.templates[template_name]["tmplt"], {"test": "data"}) 1243 + self.assertIn("updated", zot.templates[template_name]) 1244 + 1245 + # Verify the return value is a deep copy 1246 + self.assertEqual(result, {"test": "data"}) 1247 + 1248 + # Modify the returned data and check it doesn't affect the cached template 1249 + result["modified"] = True 1250 + self.assertNotIn("modified", zot.templates[template_name]["tmplt"]) 1251 + 1252 + @httpretty.activate 1253 + def test_striplocal_local_mode(self): 1254 + """Test _striplocal method in local mode""" 1255 + zot = z.Zotero("myuserID", "user", "myuserkey", local=True) 1256 + 1257 + # Test stripping local API path 1258 + url = "http://localhost:23119/api/users/myuserID/items" 1259 + result = zot._striplocal(url) 1260 + self.assertEqual(result, "http://localhost:23119/users/myuserID/items") 1261 + 1262 + # Test with more complex path 1263 + url = "http://localhost:23119/api/users/myuserID/collections/ABC123/items" 1264 + result = zot._striplocal(url) 1265 + self.assertEqual( 1266 + result, "http://localhost:23119/users/myuserID/collections/ABC123/items" 1267 + ) 1268 + 1269 + @httpretty.activate 1270 + def test_striplocal_remote_mode(self): 1271 + """Test _striplocal method in remote mode (shouldn't change URL)""" 1272 + zot = z.Zotero("myuserID", "user", "myuserkey", local=False) 1273 + 1274 + # Test without changing URL in remote mode 1275 + url = "https://api.zotero.org/users/myuserID/items" 1276 + result = zot._striplocal(url) 1277 + self.assertEqual(result, url) 1278 + 1279 + @httpretty.activate 1280 + def test_set_fulltext(self): 1281 + """Test set_fulltext method for setting full-text data""" 1282 + zot = z.Zotero("myuserID", "user", "myuserkey") 1283 + 1284 + # Mock response from Zotero API 1285 + HTTPretty.register_uri( 1286 + HTTPretty.PUT, 1287 + "https://api.zotero.org/users/myuserID/items/ABCD1234/fulltext", 1288 + status=204, 1289 + ) 1290 + 1291 + # Test with PDF data 1292 + pdf_payload = { 1293 + "content": "This is the full text content", 1294 + "indexedPages": 5, 1295 + "totalPages": 10, 1296 + } 1297 + 1298 + _ = zot.set_fulltext("ABCD1234", pdf_payload) 1299 + 1300 + # Verify the request 1301 + request = httpretty.last_request() 1302 + self.assertEqual(request.method, "PUT") 1303 + self.assertEqual(json.loads(request.body.decode()), pdf_payload) 1304 + self.assertEqual(request.headers["Content-Type"], "application/json") 1305 + 1306 + @httpretty.activate 1307 + def test_new_fulltext(self): 1308 + """Test new_fulltext method for retrieving newer full-text content""" 1309 + zot = z.Zotero("myuserID", "user", "myuserkey") 1310 + 1311 + # Mock response from Zotero API 1312 + mock_response = { 1313 + "ITEM1": {"version": 123, "indexedPages": 10, "totalPages": 10}, 1314 + "ITEM2": {"version": 456, "indexedChars": 5000, "totalChars": 5000}, 1315 + } 1316 + 1317 + HTTPretty.register_uri( 1318 + HTTPretty.GET, 1319 + "https://api.zotero.org/users/myuserID/fulltext", 1320 + body=json.dumps(mock_response), 1321 + content_type="application/json", 1322 + ) 1323 + 1324 + # Set up a mock for the request attribute 1325 + zot.request = MagicMock() 1326 + zot.request.headers = {} 1327 + 1328 + # Test the new_fulltext method 1329 + result = zot.new_fulltext(since=5) 1330 + 1331 + # Verify the result 1332 + self.assertEqual(result, mock_response) 1333 + 1334 + # Check that the correct parameters were sent 1335 + request = httpretty.last_request() 1336 + self.assertEqual(request.querystring.get("since"), ["5"]) 1337 + 1338 + @httpretty.activate 1339 + def test_last_modified_version(self): 1340 + """Test the last_modified_version method""" 1341 + zot = z.Zotero("myuserID", "user", "myuserkey") 1342 + 1343 + # Mock the response with a last-modified-version header 1344 + HTTPretty.register_uri( 1345 + HTTPretty.GET, 1346 + "https://api.zotero.org/users/myuserID/items", 1347 + body=self.items_doc, 1348 + content_type="application/json", 1349 + adding_headers={"last-modified-version": "1234"}, 1350 + ) 1351 + 1352 + # Test retrieving the last modified version 1353 + version = zot.last_modified_version() 1354 + 1355 + # Verify the result 1356 + self.assertEqual(version, 1234) 1357 + 1358 + def test_makeiter(self): 1359 + """Test the makeiter method that wraps iterfollow""" 1360 + zot = z.Zotero("myuserID", "user", "myuserkey") 1361 + 1362 + # Create a mock method that returns items 1363 + # This is a better approach than trying to test the generator directly 1364 + mock_items = [{"key": "ITEM1"}, {"key": "ITEM2"}] 1365 + 1366 + with patch.object(zot, "iterfollow") as mock_iterfollow: 1367 + # Set up the mock to return our test items 1368 + mock_iterfollow.return_value = iter([mock_items]) 1369 + 1370 + # Test makeiter which wraps the iterfollow generator 1371 + # Set links manually since we're not actually calling the API 1372 + zot.links = {"self": "/test", "next": "/test?start=5"} 1373 + 1374 + # This should call iterfollow internally 1375 + result = zot.makeiter(lambda: None) 1376 + 1377 + # Verify makeiter sets the 'next' link to the 'self' link 1378 + self.assertEqual(zot.links["next"], zot.links["self"]) 1379 + 1380 + # Verify makeiter returns an iterable 1381 + self.assertTrue(hasattr(result, "__iter__")) 1382 + 1383 + # Verify the mock was called 1384 + mock_iterfollow.assert_called_once() 1385 + 1386 + @httpretty.activate 1387 + def test_publications_user(self): 1388 + """Test the publications method for user libraries""" 1389 + zot = z.Zotero("myuserID", "user", "myuserkey") 1390 + 1391 + # Mock the API response 1392 + HTTPretty.register_uri( 1393 + HTTPretty.GET, 1394 + "https://api.zotero.org/users/myuserID/publications/items", 1395 + body=self.items_doc, 1396 + content_type="application/json", 1397 + ) 1398 + 1399 + # Get publications 1400 + items = zot.publications() 1401 + 1402 + # Verify the result 1403 + self.assertEqual(items[0]["key"], "NM66T6EF") 1404 + 1405 + def test_publications_group(self): 1406 + """Test that publications method raises error for group libraries""" 1407 + zot = z.Zotero("myGroupID", "group", "myuserkey") 1408 + 1409 + # Publications API endpoint doesn't exist for groups 1410 + with self.assertRaises(z.ze.CallDoesNotExistError): 1411 + zot.publications() 1199 1412 1200 1413 1201 1414 if __name__ == "__main__":