karakeep-python-api 0.1.2__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {karakeep_python_api-0.1.2/karakeep_python_api.egg-info → karakeep_python_api-0.1.3}/PKG-INFO +41 -38
  2. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/README.md +40 -37
  3. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api/karakeep_api.py +6 -1
  4. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api/openapi_reference.json +1 -1
  5. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3/karakeep_python_api.egg-info}/PKG-INFO +41 -38
  6. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/setup.py +1 -1
  7. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/tests/test_karakeep_api.py +152 -144
  8. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/LICENSE +0 -0
  9. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/MANIFEST.in +0 -0
  10. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api/__init__.py +0 -0
  11. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api/__main__.py +0 -0
  12. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api/datatypes.py +0 -0
  13. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
  14. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
  15. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api.egg-info/entry_points.txt +0 -0
  16. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api.egg-info/requires.txt +0 -0
  17. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/karakeep_python_api.egg-info/top_level.txt +0 -0
  18. {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Community python client for the Karakeep API.
5
5
  Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
6
6
  License: GPLv3
@@ -72,43 +72,46 @@ The development process involved:
72
72
 
73
73
  ## API Method Coverage
74
74
 
75
- The following table lists the public methods available in the `KarakeepAPI` class and indicates whether they are currently covered by the automated test suite (`tests/test_karakeep_api.py`). Methods marked as "No" should be used with caution as their behavior has not been automatically verified.
76
-
77
- | Method Name | Tested | Remarks |
78
- | -------------------------------- | :----: | -------------------------------------------- |
79
- | `get_all_bookmarks` | ✅ | Tested with pagination. |
80
- | `create_a_new_bookmark` | ✅ | Only tested for `type="link"`. |
81
- | `search_bookmarks` | ✅ | Tested as part of create/delete flow. |
82
- | `get_a_single_bookmark` | ✅ | Tested as part of create/delete flow. |
83
- | `delete_a_bookmark` | ✅ | Tested as part of create/delete flow. |
84
- | `update_a_bookmark` | ✅ | Tested for title updates. |
85
- | `summarize_a_bookmark` | | |
86
- | `attach_tags_to_a_bookmark` | | |
87
- | `detach_tags_from_a_bookmark` | | |
88
- | `get_highlights_of_a_bookmark` | ❌ | |
89
- | `attach_asset` | | |
90
- | `replace_asset` | | |
91
- | `detach_asset` | ❌ | |
92
- | `get_all_lists` | | |
93
- | `create_a_new_list` | | Tested as part of create/delete flow. |
94
- | `get_a_single_list` | | Tested as part of create/delete flow. |
95
- | `delete_a_list` | ✅ | Tested as part of create/delete flow. |
96
- | `update_a_list` | | |
97
- | `get_a_bookmarks_in_a_list` | | |
98
- | `add_a_bookmark_to_a_list` | | |
99
- | `remove_a_bookmark_from_a_list` | ❌ | |
100
- | `get_all_tags` | | |
101
- | `get_a_single_tag` | ❌ | |
102
- | `delete_a_tag` | ❌ | |
103
- | `update_a_tag` | | |
104
- | `get_a_bookmarks_with_the_tag` | | |
105
- | `get_all_highlights` | ✅ | Tested with pagination. |
106
- | `create_a_new_highlight` | | |
107
- | `get_a_single_highlight` | ❌ | |
108
- | `delete_a_highlight` | | |
109
- | `update_a_highlight` | ❌ | |
110
- | `get_current_user_info` | | Tested indirectly during client initialization. |
111
- | `get_current_user_stats` | | |
75
+ The following table lists the public methods available in the `KarakeepAPI` class.
76
+ * The "Pytest" column indicates whether the Python library method is covered by the automated test suite (`tests/test_karakeep_api.py`).
77
+ * The "CLI" column indicates whether the corresponding CLI command for that method is tested within the Pytest suite (typically via `subprocess`).
78
+ Methods or CLI commands marked with ❌ should be used with caution as their behavior has not been automatically verified within the test suite.
79
+
80
+ | Method Name | Pytest | CLI | Remarks |
81
+ | -------------------------------- | :----: | :--: | -------------------------------------------- |
82
+ | `get_all_bookmarks` | ✅ | ✅ | Tested with pagination. |
83
+ | `create_a_new_bookmark` | ✅ | ❌ | Pytest for `type="link"` via fixture. CLI not directly tested. |
84
+ | `search_bookmarks` | ✅ | ✅ | Seems to be nondeterministic and fails if using more than 3 words |
85
+ | `get_a_single_bookmark` | || |
86
+ | `delete_a_bookmark` | || |
87
+ | `update_a_bookmark` | || Tested for title updates. |
88
+ | `summarize_a_bookmark` | ❌ | ❌ | |
89
+ | `attach_tags_to_a_bookmark` | || |
90
+ | `detach_tags_from_a_bookmark` | || |
91
+ | `get_highlights_of_a_bookmark` | ❌ || Works from the CLI; not yet added to Pytest. |
92
+ | `attach_asset` | | ❌ | |
93
+ | `replace_asset` | || |
94
+ | `detach_asset` | || |
95
+ | `get_all_lists` | ✅ || |
96
+ | `create_a_new_list` | || |
97
+ | `get_a_single_list` | || |
98
+ | `delete_a_list` | || |
99
+ | `update_a_list` | ❌ | ❌ | |
100
+ | `get_a_bookmarks_in_a_list` | | ❌ | |
101
+ | `add_a_bookmark_to_a_list` | ❌ | ❌ | |
102
+ | `remove_a_bookmark_from_a_list` | ❌ | ❌ | |
103
+ | `get_all_tags` | | ✅ | |
104
+ | `get_a_single_tag` | || |
105
+ | `delete_a_tag` | ✅ || |
106
+ | `update_a_tag` | || No output validation due to [server bug](https://github.com/karakeep-app/karakeep/issues/1365). |
107
+ | `get_a_bookmarks_with_the_tag` | ❌ | ❌ | |
108
+ | `get_all_highlights` | || Tested with pagination. |
109
+ | `create_a_new_highlight` | ❌ | ❌ | |
110
+ | `get_a_single_highlight` | || |
111
+ | `delete_a_highlight` | || Works from the CLI; not yet added to Pytest. |
112
+ | `update_a_highlight` | ❌ | ❌ | |
113
+ | `get_current_user_info` | ✅ | ❌ | Pytest: Tested indirectly during client init. CLI not directly tested. |
114
+ | `get_current_user_stats` | ✅ | ✅ | |
112
115
 
113
116
  ## Installation
114
117
 
@@ -37,43 +37,46 @@ The development process involved:
37
37
 
38
38
  ## API Method Coverage
39
39
 
40
- The following table lists the public methods available in the `KarakeepAPI` class and indicates whether they are currently covered by the automated test suite (`tests/test_karakeep_api.py`). Methods marked as "No" should be used with caution as their behavior has not been automatically verified.
41
-
42
- | Method Name | Tested | Remarks |
43
- | -------------------------------- | :----: | -------------------------------------------- |
44
- | `get_all_bookmarks` | ✅ | Tested with pagination. |
45
- | `create_a_new_bookmark` | ✅ | Only tested for `type="link"`. |
46
- | `search_bookmarks` | ✅ | Tested as part of create/delete flow. |
47
- | `get_a_single_bookmark` | ✅ | Tested as part of create/delete flow. |
48
- | `delete_a_bookmark` | ✅ | Tested as part of create/delete flow. |
49
- | `update_a_bookmark` | ✅ | Tested for title updates. |
50
- | `summarize_a_bookmark` | | |
51
- | `attach_tags_to_a_bookmark` | | |
52
- | `detach_tags_from_a_bookmark` | | |
53
- | `get_highlights_of_a_bookmark` | ❌ | |
54
- | `attach_asset` | | |
55
- | `replace_asset` | | |
56
- | `detach_asset` | ❌ | |
57
- | `get_all_lists` | | |
58
- | `create_a_new_list` | | Tested as part of create/delete flow. |
59
- | `get_a_single_list` | | Tested as part of create/delete flow. |
60
- | `delete_a_list` | ✅ | Tested as part of create/delete flow. |
61
- | `update_a_list` | | |
62
- | `get_a_bookmarks_in_a_list` | | |
63
- | `add_a_bookmark_to_a_list` | | |
64
- | `remove_a_bookmark_from_a_list` | ❌ | |
65
- | `get_all_tags` | | |
66
- | `get_a_single_tag` | ❌ | |
67
- | `delete_a_tag` | ❌ | |
68
- | `update_a_tag` | | |
69
- | `get_a_bookmarks_with_the_tag` | | |
70
- | `get_all_highlights` | ✅ | Tested with pagination. |
71
- | `create_a_new_highlight` | | |
72
- | `get_a_single_highlight` | ❌ | |
73
- | `delete_a_highlight` | | |
74
- | `update_a_highlight` | ❌ | |
75
- | `get_current_user_info` | | Tested indirectly during client initialization. |
76
- | `get_current_user_stats` | | |
40
+ The following table lists the public methods available in the `KarakeepAPI` class.
41
+ * The "Pytest" column indicates whether the Python library method is covered by the automated test suite (`tests/test_karakeep_api.py`).
42
+ * The "CLI" column indicates whether the corresponding CLI command for that method is tested within the Pytest suite (typically via `subprocess`).
43
+ Methods or CLI commands marked with ❌ should be used with caution as their behavior has not been automatically verified within the test suite.
44
+
45
+ | Method Name | Pytest | CLI | Remarks |
46
+ | -------------------------------- | :----: | :--: | -------------------------------------------- |
47
+ | `get_all_bookmarks` | ✅ | ✅ | Tested with pagination. |
48
+ | `create_a_new_bookmark` | ✅ | ❌ | Pytest for `type="link"` via fixture. CLI not directly tested. |
49
+ | `search_bookmarks` | ✅ | ✅ | Seems to be nondeterministic and fails if using more than 3 words |
50
+ | `get_a_single_bookmark` | || |
51
+ | `delete_a_bookmark` | || |
52
+ | `update_a_bookmark` | || Tested for title updates. |
53
+ | `summarize_a_bookmark` | ❌ | ❌ | |
54
+ | `attach_tags_to_a_bookmark` | || |
55
+ | `detach_tags_from_a_bookmark` | || |
56
+ | `get_highlights_of_a_bookmark` | ❌ || Works from the CLI; not yet added to Pytest. |
57
+ | `attach_asset` | | ❌ | |
58
+ | `replace_asset` | || |
59
+ | `detach_asset` | || |
60
+ | `get_all_lists` | ✅ || |
61
+ | `create_a_new_list` | || |
62
+ | `get_a_single_list` | || |
63
+ | `delete_a_list` | || |
64
+ | `update_a_list` | ❌ | ❌ | |
65
+ | `get_a_bookmarks_in_a_list` | | ❌ | |
66
+ | `add_a_bookmark_to_a_list` | ❌ | ❌ | |
67
+ | `remove_a_bookmark_from_a_list` | ❌ | ❌ | |
68
+ | `get_all_tags` | | ✅ | |
69
+ | `get_a_single_tag` | || |
70
+ | `delete_a_tag` | ✅ || |
71
+ | `update_a_tag` | || No output validation due to [server bug](https://github.com/karakeep-app/karakeep/issues/1365). |
72
+ | `get_a_bookmarks_with_the_tag` | ❌ | ❌ | |
73
+ | `get_all_highlights` | || Tested with pagination. |
74
+ | `create_a_new_highlight` | ❌ | ❌ | |
75
+ | `get_a_single_highlight` | || |
76
+ | `delete_a_highlight` | || Works from the CLI; not yet added to Pytest. |
77
+ | `update_a_highlight` | ❌ | ❌ | |
78
+ | `get_current_user_info` | ✅ | ❌ | Pytest: Tested indirectly during client init. CLI not directly tested. |
79
+ | `get_current_user_stats` | ✅ | ✅ | |
77
80
 
78
81
  ## Installation
79
82
 
@@ -85,7 +85,7 @@ class KarakeepAPI:
85
85
  """
86
86
 
87
87
  # Version reflects the client library version, updated by bumpver
88
- VERSION: str = "0.1.2"
88
+ VERSION: str = "0.1.3"
89
89
 
90
90
  def __init__(
91
91
  self,
@@ -1435,6 +1435,11 @@ class KarakeepAPI:
1435
1435
  logger.debug("Skipping response validation as requested.")
1436
1436
  return response_data
1437
1437
  else:
1438
+ # As of version 0.24.1 of karakeep: we do not check the correct
1439
+ # validation type because there is an error on the
1440
+ # server side: https://github.com/karakeep-app/karakeep/issues/1365
1441
+ return response_data
1442
+
1438
1443
  # Response should match Tag1 schema
1439
1444
  return datatypes.Tag1.model_validate(response_data)
1440
1445
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Community python client for the Karakeep API.
5
5
  Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
6
6
  License: GPLv3
@@ -72,43 +72,46 @@ The development process involved:
72
72
 
73
73
  ## API Method Coverage
74
74
 
75
- The following table lists the public methods available in the `KarakeepAPI` class and indicates whether they are currently covered by the automated test suite (`tests/test_karakeep_api.py`). Methods marked as "No" should be used with caution as their behavior has not been automatically verified.
76
-
77
- | Method Name | Tested | Remarks |
78
- | -------------------------------- | :----: | -------------------------------------------- |
79
- | `get_all_bookmarks` | ✅ | Tested with pagination. |
80
- | `create_a_new_bookmark` | ✅ | Only tested for `type="link"`. |
81
- | `search_bookmarks` | ✅ | Tested as part of create/delete flow. |
82
- | `get_a_single_bookmark` | ✅ | Tested as part of create/delete flow. |
83
- | `delete_a_bookmark` | ✅ | Tested as part of create/delete flow. |
84
- | `update_a_bookmark` | ✅ | Tested for title updates. |
85
- | `summarize_a_bookmark` | | |
86
- | `attach_tags_to_a_bookmark` | | |
87
- | `detach_tags_from_a_bookmark` | | |
88
- | `get_highlights_of_a_bookmark` | ❌ | |
89
- | `attach_asset` | | |
90
- | `replace_asset` | | |
91
- | `detach_asset` | ❌ | |
92
- | `get_all_lists` | | |
93
- | `create_a_new_list` | | Tested as part of create/delete flow. |
94
- | `get_a_single_list` | | Tested as part of create/delete flow. |
95
- | `delete_a_list` | ✅ | Tested as part of create/delete flow. |
96
- | `update_a_list` | | |
97
- | `get_a_bookmarks_in_a_list` | | |
98
- | `add_a_bookmark_to_a_list` | | |
99
- | `remove_a_bookmark_from_a_list` | ❌ | |
100
- | `get_all_tags` | | |
101
- | `get_a_single_tag` | ❌ | |
102
- | `delete_a_tag` | ❌ | |
103
- | `update_a_tag` | | |
104
- | `get_a_bookmarks_with_the_tag` | | |
105
- | `get_all_highlights` | ✅ | Tested with pagination. |
106
- | `create_a_new_highlight` | | |
107
- | `get_a_single_highlight` | ❌ | |
108
- | `delete_a_highlight` | | |
109
- | `update_a_highlight` | ❌ | |
110
- | `get_current_user_info` | | Tested indirectly during client initialization. |
111
- | `get_current_user_stats` | | |
75
+ The following table lists the public methods available in the `KarakeepAPI` class.
76
+ * The "Pytest" column indicates whether the Python library method is covered by the automated test suite (`tests/test_karakeep_api.py`).
77
+ * The "CLI" column indicates whether the corresponding CLI command for that method is tested within the Pytest suite (typically via `subprocess`).
78
+ Methods or CLI commands marked with ❌ should be used with caution as their behavior has not been automatically verified within the test suite.
79
+
80
+ | Method Name | Pytest | CLI | Remarks |
81
+ | -------------------------------- | :----: | :--: | -------------------------------------------- |
82
+ | `get_all_bookmarks` | ✅ | ✅ | Tested with pagination. |
83
+ | `create_a_new_bookmark` | ✅ | ❌ | Pytest for `type="link"` via fixture. CLI not directly tested. |
84
+ | `search_bookmarks` | ✅ | ✅ | Seems to be nondeterministic and fails if using more than 3 words |
85
+ | `get_a_single_bookmark` | || |
86
+ | `delete_a_bookmark` | || |
87
+ | `update_a_bookmark` | || Tested for title updates. |
88
+ | `summarize_a_bookmark` | ❌ | ❌ | |
89
+ | `attach_tags_to_a_bookmark` | || |
90
+ | `detach_tags_from_a_bookmark` | || |
91
+ | `get_highlights_of_a_bookmark` | ❌ || Works from the CLI; not yet added to Pytest. |
92
+ | `attach_asset` | | ❌ | |
93
+ | `replace_asset` | || |
94
+ | `detach_asset` | || |
95
+ | `get_all_lists` | ✅ || |
96
+ | `create_a_new_list` | || |
97
+ | `get_a_single_list` | || |
98
+ | `delete_a_list` | || |
99
+ | `update_a_list` | ❌ | ❌ | |
100
+ | `get_a_bookmarks_in_a_list` | | ❌ | |
101
+ | `add_a_bookmark_to_a_list` | ❌ | ❌ | |
102
+ | `remove_a_bookmark_from_a_list` | ❌ | ❌ | |
103
+ | `get_all_tags` | | ✅ | |
104
+ | `get_a_single_tag` | || |
105
+ | `delete_a_tag` | ✅ || |
106
+ | `update_a_tag` | || No output validation due to [server bug](https://github.com/karakeep-app/karakeep/issues/1365). |
107
+ | `get_a_bookmarks_with_the_tag` | ❌ | ❌ | |
108
+ | `get_all_highlights` | || Tested with pagination. |
109
+ | `create_a_new_highlight` | ❌ | ❌ | |
110
+ | `get_a_single_highlight` | || |
111
+ | `delete_a_highlight` | || Works from the CLI; not yet added to Pytest. |
112
+ | `update_a_highlight` | ❌ | ❌ | |
113
+ | `get_current_user_info` | ✅ | ❌ | Pytest: Tested indirectly during client init. CLI not directly tested. |
114
+ | `get_current_user_stats` | ✅ | ✅ | |
112
115
 
113
116
  ## Installation
114
117
 
@@ -7,7 +7,7 @@ with open("README.md", "r") as readme:
7
7
 
8
8
  setup(
9
9
  name="karakeep_python_api",
10
- version="0.1.2",
10
+ version="0.1.3",
11
11
  description="Community python client for the Karakeep API.", # Simplified description
12
12
  long_description=long_description,
13
13
  long_description_content_type="text/markdown",
@@ -340,55 +340,66 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
340
340
  print("\nSkipping deletion because list creation failed or ID was not obtained.")
341
341
 
342
342
 
343
- def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI):
344
- """Test creating a new URL bookmark and then deleting it."""
345
- created_bookmark_id = None
346
- test_url = "https://en.wikipedia.org/wiki/Example"
343
+ def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI, managed_bookmark: datatypes.Bookmark):
344
+ """
345
+ Test verifying a created bookmark (via fixture) and searching for it.
346
+ The fixture handles creation and deletion.
347
+ """
348
+ created_bookmark_id = managed_bookmark.id
349
+ test_url = managed_bookmark.content.url # Get URL from fixture
350
+ original_title = managed_bookmark.title # Get title from fixture
351
+
347
352
  try:
348
- # 1. Define bookmark payload
349
- print(f"\nAttempting to create bookmark for URL: {test_url}")
353
+ # 1. Bookmark is already created by the 'managed_bookmark' fixture.
354
+ print(f"\nUsing managed bookmark ID: {created_bookmark_id}, URL: '{test_url}', Title: '{original_title}'")
350
355
 
351
- # 2. Create the bookmark
352
- # Call the method with keyword arguments matching its signature
353
- created_bookmark = karakeep_client.create_a_new_bookmark(
354
- type="link", url=test_url
355
- )
356
- assert isinstance(
357
- created_bookmark, datatypes.Bookmark
358
- ), "Response should be a Bookmark model"
359
- assert created_bookmark.content.url == test_url, "Created bookmark URL should match"
360
- assert created_bookmark.id, "Created bookmark must have an ID"
361
- created_bookmark_id = created_bookmark.id
362
- print(f"✓ Successfully created bookmark with ID: {created_bookmark_id}")
363
-
364
- # 3. Verify the bookmark exists by getting it directly
356
+ # 2. Verify the bookmark exists by getting it directly
365
357
  retrieved_bookmark = karakeep_client.get_a_single_bookmark(
366
358
  bookmark_id=created_bookmark_id
367
359
  )
368
360
  assert isinstance(retrieved_bookmark, datatypes.Bookmark)
369
361
  assert retrieved_bookmark.id == created_bookmark_id
370
362
  assert retrieved_bookmark.content.url == test_url
371
- print(f"✓ Successfully retrieved the created bookmark by ID.")
363
+ assert retrieved_bookmark.title == original_title
364
+ print(f"✓ Successfully retrieved the managed bookmark by ID.")
372
365
 
373
- # 4. Search for the created bookmark
374
- print(f"\nAttempting to search for bookmark with query: 'wikipedia'")
375
- search_query = "Example - Wikipedia"
376
- search_results = karakeep_client.search_bookmarks(q=search_query, limit=50)
377
- assert isinstance(
378
- search_results, datatypes.PaginatedBookmarks
379
- ), "Search response should be PaginatedBookmarks model"
380
- assert isinstance(
381
- search_results.bookmarks, list
382
- ), "Search results bookmarks attribute should be a list"
383
- assert any(
384
- b.id == created_bookmark_id for b in search_results.bookmarks
385
- ), f"Created bookmark {created_bookmark_id} not found in search results for '{search_query}'"
386
- print(f"✓ Found created bookmark in search results for '{search_query}'.")
387
366
 
388
- # 4a. Test CLI search equivalent
389
- print(f"\n Running CLI equivalent: search-bookmarks --q '{search_query}' --limit 10")
367
+ # 3. Search for the created bookmark
368
+ # Use a search query that is likely to match the fixture's title
369
+ # The fixture title is "Managed Fixture Bookmark {timestamp}-{random_suffix}"
370
+ # A simple search for "Managed Fixture Bookmark" should work.
371
+ # If the title is very dynamic, searching by URL might be more robust if supported,
372
+ # or by a known part of the title.
373
+
374
+ # waiting a bit for the indexation just in case
375
+ time.sleep(30)
376
+
377
+ search_query_component = original_title.split(" ")[0] + " " + original_title.split(" ")[1]
378
+ print(f"\nAttempting to search for bookmark with query based on title: '{search_query_component}'. Retrying multiple times because search is nondeterministic.")
379
+
380
+ for trial in range(5):
381
+ search_results = karakeep_client.search_bookmarks(q=search_query_component, limit=100, include_content=False)
382
+ assert isinstance(
383
+ search_results, datatypes.PaginatedBookmarks
384
+ ), "Search response should be PaginatedBookmarks model"
385
+ assert isinstance(
386
+ search_results.bookmarks, list
387
+ ), "Search results bookmarks attribute should be a list"
388
+
389
+ titles_in_search = [b.title for b in search_results.bookmarks]
390
+ found_in_search = any(b.id == created_bookmark_id for b in search_results.bookmarks)
391
+ if found_in_search:
392
+ break
393
+ else:
394
+ time.sleep(3)
395
+ assert found_in_search, \
396
+ f"Managed bookmark {created_bookmark_id} (Title: '{original_title}') not found in {trial} different search results for '{search_query_component}'. Titles were: '{titles_in_search}'."
397
+ print(f"✓ Found managed bookmark in search results for '{search_query_component}'.")
398
+
399
+ # 4. Test CLI search equivalent
400
+ print(f"\n Running CLI equivalent: search-bookmarks --q '{search_query_component}' --limit 10 --include-content false")
390
401
  try:
391
- cli_search_command = f"python -m karakeep_python_api search-bookmarks --q '{search_query}' --limit 10"
402
+ cli_search_command = f"python -m karakeep_python_api search-bookmarks --q '{search_query_component}' --limit 10 --include-content false"
392
403
  search_cli_output = subprocess.run(
393
404
  cli_search_command,
394
405
  shell=True,
@@ -396,120 +407,41 @@ def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI):
396
407
  capture_output=True,
397
408
  text=True,
398
409
  )
399
- # Basic check: Ensure the created ID is somewhere in the output JSON
400
- # A more robust check would parse the JSON and verify structure/content
401
- assert created_bookmark_id in search_cli_output.stdout, f"Created bookmark ID {created_bookmark_id} not found in CLI search output for '{search_query}'"
410
+ assert created_bookmark_id in search_cli_output.stdout, \
411
+ f"Managed bookmark ID {created_bookmark_id} not found in CLI search output for '{search_query_component}'"
402
412
  print("✓ CLI search command executed successfully and contained the bookmark ID.")
403
413
  except subprocess.CalledProcessError as e:
404
414
  print(f" CLI search command failed with exit code {e.returncode}")
405
415
  print(f" Stdout: {e.stdout}")
406
416
  print(f" Stderr: {e.stderr}")
407
- pytest.fail(f"CLI command 'search-bookmarks --q {search_query}' failed: {e}")
417
+ pytest.fail(f"CLI command 'search-bookmarks --q \"{search_query_component}\"' failed: {e}")
408
418
  except Exception as e:
409
419
  pytest.fail(f"An unexpected error occurred running the CLI search command: {e}")
410
420
 
411
-
412
421
  except (APIError, AuthenticationError) as e:
413
- pytest.fail(f"API error during bookmark creation/verification/search: {e}")
422
+ pytest.fail(f"API error during bookmark verification/search: {e}")
414
423
  except Exception as e:
415
424
  pytest.fail(
416
- f"An unexpected error occurred during bookmark creation/verification/search: {e}"
425
+ f"An unexpected error occurred during bookmark verification/search: {e}"
417
426
  )
418
- finally:
419
- # 5. Delete the bookmark (ensure cleanup)
420
- if created_bookmark_id:
421
- print(f"\nAttempting to delete bookmark with ID: {created_bookmark_id}")
422
- try:
423
- karakeep_client.delete_a_bookmark(bookmark_id=created_bookmark_id)
424
- print(f"✓ Successfully deleted bookmark with ID: {created_bookmark_id}")
425
-
426
- # 6. Verify the bookmark is gone by trying to get it (should fail)
427
- try:
428
- karakeep_client.get_a_single_bookmark(
429
- bookmark_id=created_bookmark_id
430
- )
431
- pytest.fail(
432
- f"Bookmark with ID {created_bookmark_id} should not exist after deletion, but get_a_single_bookmark succeeded."
433
- )
434
- except APIError as e:
435
- assert (
436
- e.status_code == 404
437
- ), f"Expected 404 Not Found when getting deleted bookmark, but got status {e.status_code}"
438
- print(
439
- f"✓ Confirmed bookmark {created_bookmark_id} is deleted via API (received 404)."
440
- )
441
-
442
- # 7. Attempt to delete the same bookmark via CLI (should ideally fail or do nothing)
443
- print(f"\n Attempting CLI deletion for already deleted bookmark ID: {created_bookmark_id}")
444
- try:
445
- cli_delete_command = f"python -m karakeep_python_api delete-a-bookmark --bookmark-id {created_bookmark_id}"
446
- subprocess.run(
447
- cli_delete_command,
448
- shell=True,
449
- check=True, # Set to False if CLI errors on 404, True if it exits 0
450
- capture_output=True,
451
- text=True,
452
- )
453
- # If check=True and it succeeds, it means the CLI might not error on 404.
454
- print(f" ✓ CLI delete command executed (check=True implies it might not error on 404).")
455
- except subprocess.CalledProcessError as e:
456
- # If check=True, this block means the CLI command failed, which *might* be expected if it errors on 404.
457
- print(f" ✓ CLI delete command failed as expected (exit code {e.returncode}), likely because bookmark was already deleted.")
458
- # Optional: Assert specific exit code if known
459
- # assert e.returncode == EXPECTED_EXIT_CODE_FOR_NOT_FOUND
460
- except Exception as cli_e:
461
- pytest.fail(f"An unexpected error occurred running the CLI delete command: {cli_e}")
462
-
463
- # 8. Verify again via API that the bookmark is still gone after CLI attempt
464
- try:
465
- karakeep_client.get_a_single_bookmark(bookmark_id=created_bookmark_id)
466
- pytest.fail(
467
- f"Bookmark {created_bookmark_id} should *still* not exist after CLI deletion attempt, but get_a_single_bookmark succeeded."
468
- )
469
- except APIError as e:
470
- assert (
471
- e.status_code == 404
472
- ), f"Expected 404 Not Found after CLI deletion attempt, but got status {e.status_code}"
473
- print(
474
- f"✓ Confirmed bookmark {created_bookmark_id} is *still* deleted after CLI attempt (received 404)."
475
- )
427
+ # No 'finally' block for deletion needed, as 'managed_bookmark' fixture handles it.
428
+ # The fixture also handles verification of deletion.
476
429
 
477
- except (APIError, AuthenticationError) as e:
478
- # Catch errors from the initial Python API deletion or subsequent checks
479
- pytest.fail(f"API error during bookmark deletion/verification: {e}")
480
- except Exception as e:
481
- pytest.fail(f"An unexpected error occurred during bookmark deletion: {e}")
482
- else:
483
- print(
484
- "\nSkipping deletion because bookmark creation failed or ID was not obtained."
485
- )
486
430
 
487
-
488
- def test_update_bookmark_title(karakeep_client: KarakeepAPI):
489
- """Test updating a bookmark's title via API and CLI."""
490
- created_bookmark_id = None
491
- # Generate a unique suffix for the initial title to avoid collisions
492
- timestamp = int(time.time())
493
- random_suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
494
- original_title = f"Initial Test Title {timestamp}-{random_suffix}"
495
- test_url = "https://en.wikipedia.org/wiki/PageForBookmarkUpdateTest" # A distinct URL for this test
431
+ def test_update_bookmark_title(karakeep_client: KarakeepAPI, managed_bookmark: datatypes.Bookmark):
432
+ """Test updating a bookmark's title via API and CLI, using a managed bookmark."""
433
+ created_bookmark_id = managed_bookmark.id
434
+ original_title = managed_bookmark.title # Get the original title from the fixture
496
435
 
497
436
  target_api_title = "this is a test title"
498
437
  target_cli_title = "this is a test title (CLI)"
499
438
 
500
439
  try:
501
- # 1. Create a bookmark with an initial title
502
- print(f"\nAttempting to create bookmark for URL: {test_url} with original title: '{original_title}'")
503
- created_bookmark = karakeep_client.create_a_new_bookmark(
504
- type="link", url=test_url, title=original_title
505
- )
506
- assert isinstance(created_bookmark, datatypes.Bookmark), "Response should be a Bookmark model"
507
- created_bookmark_id = created_bookmark.id
508
- assert created_bookmark.id, "Created bookmark must have an ID"
509
- assert created_bookmark.title == original_title, f"Initial title '{created_bookmark.title}' does not match expected '{original_title}'"
510
- print(f"✓ Successfully created bookmark ID: {created_bookmark_id}, Title: '{created_bookmark.title}'")
440
+ # The bookmark is already created by the 'managed_bookmark' fixture.
441
+ # We have its ID in created_bookmark_id and its original title.
442
+ print(f"\nUsing managed bookmark ID: {created_bookmark_id}, Original Title: '{original_title}'")
511
443
 
512
- # 2. Update the bookmark's title using the API client
444
+ # 1. Update the bookmark's title using the API client
513
445
  print(f"\nAttempting to update bookmark ID {created_bookmark_id} title to: '{target_api_title}' via API")
514
446
  update_payload_api = {"title": target_api_title}
515
447
  updated_bookmark_partial = karakeep_client.update_a_bookmark(
@@ -520,7 +452,7 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
520
452
  f"Partial response title '{updated_bookmark_partial.get('title')}' does not match target API title '{target_api_title}'"
521
453
  print(f"✓ API call to update_a_bookmark successful. Partial response title: '{updated_bookmark_partial.get('title')}'")
522
454
 
523
- # 3. Verify the API update by fetching the bookmark again
455
+ # 2. Verify the API update by fetching the bookmark again
524
456
  print(f"\nFetching bookmark ID {created_bookmark_id} to verify API title update.")
525
457
  retrieved_bookmark_after_api_update = karakeep_client.get_a_single_bookmark(
526
458
  bookmark_id=created_bookmark_id
@@ -530,7 +462,7 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
530
462
  f"Retrieved bookmark title '{retrieved_bookmark_after_api_update.title}' does not match expected API-updated title '{target_api_title}'"
531
463
  print(f"✓ Successfully verified bookmark title updated by API to: '{retrieved_bookmark_after_api_update.title}'")
532
464
 
533
- # 4. Test CLI equivalent for updating the bookmark's title
465
+ # 3. Test CLI equivalent for updating the bookmark's title
534
466
  print(f"\n Running CLI equivalent to update title to: '{target_cli_title}'")
535
467
  cli_update_payload_json = json.dumps({"title": target_cli_title})
536
468
  # Ensure the JSON string is properly quoted for the shell command
@@ -546,7 +478,7 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
546
478
  )
547
479
  print("✓ CLI update command executed successfully.")
548
480
 
549
- # 5. Verify CLI update by fetching the bookmark again
481
+ # 4. Verify CLI update by fetching the bookmark again
550
482
  print(f"\nFetching bookmark ID {created_bookmark_id} to verify CLI title update.")
551
483
  retrieved_bookmark_after_cli_update = karakeep_client.get_a_single_bookmark(
552
484
  bookmark_id=created_bookmark_id
@@ -569,18 +501,94 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
569
501
  pytest.fail(f"API error during bookmark title update test: {e}")
570
502
  except Exception as e:
571
503
  pytest.fail(f"An unexpected error occurred during bookmark title update test: {e}")
504
+ # No finally block needed for deletion, as 'managed_bookmark' fixture handles it.
505
+
506
+
507
+ def test_tag_lifecycle_on_bookmark(karakeep_client: KarakeepAPI, managed_bookmark: datatypes.Bookmark):
508
+ """
509
+ Test attaching a tag to a bookmark, updating the tag, detaching it, and deleting it.
510
+ Uses the managed_bookmark fixture.
511
+ """
512
+ bookmark_id = managed_bookmark.id
513
+ timestamp = int(time.time())
514
+ random_chars = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
515
+ initial_tag_name = f"test-tag-{timestamp}-{random_chars}"
516
+ updated_tag_name = f"updated-tag-{timestamp}-{random_chars}"
517
+ tag_id_to_manage = None
518
+
519
+ try:
520
+ # 1. Attach a new tag by name to the bookmark
521
+ print(f"\nAttempting to attach tag '{initial_tag_name}' to bookmark {bookmark_id}")
522
+ attach_payload = {"tags": [{"tagName": initial_tag_name}]}
523
+ attach_response = karakeep_client.attach_tags_to_a_bookmark(
524
+ bookmark_id=bookmark_id, tags_data=attach_payload
525
+ )
526
+ assert "attached" in attach_response and len(attach_response["attached"]) == 1, \
527
+ "Failed to attach tag or response format incorrect"
528
+ tag_id_to_manage = attach_response["attached"][0]
529
+ assert isinstance(tag_id_to_manage, str), "Attached tag ID should be a string"
530
+ print(f"✓ Tag '{initial_tag_name}' attached with ID: {tag_id_to_manage}")
531
+
532
+ # 2. Update the tag's name
533
+ print(f"\nAttempting to update tag {tag_id_to_manage} to name '{updated_tag_name}'")
534
+ update_payload = {"name": updated_tag_name}
535
+ updated_tag = karakeep_client.update_a_tag(
536
+ tag_id=tag_id_to_manage, update_data=update_payload
537
+ )
538
+ # Do not check the type because karakeep 0.24.1 has a server side bug
539
+ # assert isinstance(updated_tag, datatypes.Tag1), "Update tag response should be Tag1 model"
540
+ # assert updated_tag.name == updated_tag_name, "Tag name was not updated as expected"
541
+ # print(f"✓ Tag {tag_id_to_manage} updated to name '{updated_tag.name}'")
542
+ assert updated_tag["name"] == updated_tag_name, "Tag name was not updated as expected"
543
+ print(f"✓ Tag {tag_id_to_manage} updated to name '{updated_tag['name']}'")
544
+
545
+ # 3. Verify tag update by getting it directly
546
+ print(f"\nFetching tag {tag_id_to_manage} to verify its name is '{updated_tag_name}'")
547
+ retrieved_tag = karakeep_client.get_a_single_tag(tag_id=tag_id_to_manage)
548
+ assert isinstance(retrieved_tag, datatypes.Tag1), "Get single tag response should be Tag1 model"
549
+ assert retrieved_tag.name == updated_tag_name, "Retrieved tag name does not match updated name"
550
+ assert retrieved_tag.id == tag_id_to_manage, "Retrieved tag ID does not match"
551
+ print(f"✓ Verified tag {tag_id_to_manage} has name '{retrieved_tag.name}'")
552
+
553
+ # 4. Detach the tag from the bookmark
554
+ print(f"\nAttempting to detach tag {tag_id_to_manage} from bookmark {bookmark_id}")
555
+ detach_payload = {"tags": [{"tagId": tag_id_to_manage}]}
556
+ detach_response = karakeep_client.detach_tags_from_a_bookmark(
557
+ bookmark_id=bookmark_id, tags_data=detach_payload
558
+ )
559
+ assert "detached" in detach_response and tag_id_to_manage in detach_response["detached"], \
560
+ "Failed to detach tag or response format incorrect"
561
+ print(f"✓ Tag {tag_id_to_manage} detached from bookmark {bookmark_id}")
562
+
563
+ except (APIError, AuthenticationError) as e:
564
+ pytest.fail(f"API error during tag lifecycle test: {e}")
565
+ except Exception as e:
566
+ pytest.fail(f"An unexpected error occurred during tag lifecycle test: {e}")
572
567
  finally:
573
- # 6. Delete the bookmark (ensure cleanup)
574
- if created_bookmark_id:
575
- print(f"\nAttempting to delete bookmark ID: {created_bookmark_id} during cleanup.")
568
+ # 5. Delete the tag (ensure cleanup even if assertions fail mid-test)
569
+ if tag_id_to_manage:
570
+ print(f"\nAttempting to delete tag {tag_id_to_manage} (cleanup)")
576
571
  try:
577
- karakeep_client.delete_a_bookmark(bookmark_id=created_bookmark_id)
578
- print(f"✓ Successfully deleted bookmark ID: {created_bookmark_id}")
572
+ karakeep_client.delete_a_tag(tag_id=tag_id_to_manage)
573
+ print(f"✓ Successfully deleted tag {tag_id_to_manage}")
574
+
575
+ # 6. Verify the tag is gone by trying to get it (should fail with 404)
576
+ try:
577
+ karakeep_client.get_a_single_tag(tag_id=tag_id_to_manage)
578
+ pytest.fail(
579
+ f"Tag {tag_id_to_manage} should not exist after deletion, but get_a_single_tag succeeded."
580
+ )
581
+ except APIError as e:
582
+ assert e.status_code == 404, \
583
+ f"Expected 404 Not Found when getting deleted tag, but got status {e.status_code}"
584
+ print(f"✓ Confirmed tag {tag_id_to_manage} is deleted (received 404).")
585
+ except (APIError, AuthenticationError) as e:
586
+ # Log error during cleanup but don't let it mask original test failure
587
+ print(f" API error during tag deletion (cleanup) for ID {tag_id_to_manage}: {e}")
579
588
  except Exception as e:
580
- # Log cleanup failure but don't obscure original test failure if any
581
- print(f"Error during cleanup: Failed to delete bookmark {created_bookmark_id}: {e}")
582
- # Depending on test policy, you might re-raise or just log
583
- # pytest.fail(f"Failed to delete bookmark {created_bookmark_id} during cleanup: {e}")
589
+ print(f" Unexpected error during tag deletion (cleanup) for ID {tag_id_to_manage}: {e}")
590
+ else:
591
+ print("\nSkipping tag deletion (cleanup) because tag_id was not obtained or test failed before creation.")
584
592
 
585
593
 
586
594
  # --- Test User Info/Stats Endpoints ---