karakeep-python-api 0.1.2__tar.gz → 0.1.4__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.
- {karakeep_python_api-0.1.2/karakeep_python_api.egg-info → karakeep_python_api-0.1.4}/PKG-INFO +41 -38
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/README.md +40 -37
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api/__main__.py +0 -3
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api/karakeep_api.py +6 -6
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api/openapi_reference.json +1 -1
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4/karakeep_python_api.egg-info}/PKG-INFO +41 -38
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/setup.py +1 -1
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/tests/test_karakeep_api.py +241 -226
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/LICENSE +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/MANIFEST.in +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api/__init__.py +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api/datatypes.py +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/entry_points.txt +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/requires.txt +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/top_level.txt +0 -0
- {karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/setup.cfg +0 -0
{karakeep_python_api-0.1.2/karakeep_python_api.egg-info → karakeep_python_api-0.1.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karakeep_python_api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
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
|
|
|
@@ -35,7 +35,6 @@ def serialize_output(data: Any) -> Any:
|
|
|
35
35
|
) # Use Pydantic's built-in JSON serialization
|
|
36
36
|
elif isinstance(data, list):
|
|
37
37
|
return [serialize_output(item) for item in data]
|
|
38
|
-
# Removed dataclass handling as API uses Pydantic models primarily
|
|
39
38
|
elif isinstance(data, dict):
|
|
40
39
|
# Serialize dictionary values
|
|
41
40
|
return {k: serialize_output(v) for k, v in data.items()}
|
|
@@ -345,7 +344,6 @@ def create_click_command(
|
|
|
345
344
|
full_help = docstring
|
|
346
345
|
|
|
347
346
|
# tweak the whitespaces in the full help:
|
|
348
|
-
# full_help = full_help.replace("\n", "\n\n").replace("\n ", " ")
|
|
349
347
|
full_help = full_help.replace("\n ", " ")
|
|
350
348
|
full_help = full_help.replace("\n", "\n\n")
|
|
351
349
|
|
|
@@ -452,7 +450,6 @@ def create_click_command(
|
|
|
452
450
|
elif isinstance(click_type, click.Choice):
|
|
453
451
|
param_help += f" (Choices: {', '.join(click_type.choices)})"
|
|
454
452
|
|
|
455
|
-
# Standard parameter handling (no special '--data' mapping anymore)
|
|
456
453
|
click_required = is_required_in_sig and default_value is None and not is_flag
|
|
457
454
|
|
|
458
455
|
# Add the Click Option
|
|
@@ -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.
|
|
88
|
+
VERSION: str = "0.1.4"
|
|
89
89
|
|
|
90
90
|
def __init__(
|
|
91
91
|
self,
|
|
@@ -94,7 +94,6 @@ class KarakeepAPI:
|
|
|
94
94
|
openapi_spec_path: Optional[str] = None, # Allow None, default handled below
|
|
95
95
|
verify_ssl: bool = True,
|
|
96
96
|
verbose: bool = False,
|
|
97
|
-
strict_response_parsing: bool = False, # Kept for potential future use
|
|
98
97
|
disable_response_validation: Optional[bool] = None,
|
|
99
98
|
):
|
|
100
99
|
"""
|
|
@@ -112,7 +111,6 @@ class KarakeepAPI:
|
|
|
112
111
|
Can be overridden with KARAKEEP_PYTHON_API_VERIFY_SSL environment variable (true/false).
|
|
113
112
|
verbose: Enable verbose logging (default: False).
|
|
114
113
|
Can be overridden with KARAKEEP_PYTHON_API_VERBOSE environment variable (true/false).
|
|
115
|
-
strict_response_parsing: (Currently unused) If True, raise an APIError when response parsing fails.
|
|
116
114
|
disable_response_validation: If True, skip Pydantic validation of API responses and return raw data.
|
|
117
115
|
Defaults to False. Can be overridden by setting the
|
|
118
116
|
KARAKEEP_PYTHON_API_DISABLE_RESPONSE_VALIDATION environment variable to "true".
|
|
@@ -206,9 +204,6 @@ class KarakeepAPI:
|
|
|
206
204
|
|
|
207
205
|
self.verify_ssl = verify_ssl
|
|
208
206
|
self.verbose = verbose
|
|
209
|
-
self.strict_response_parsing = (
|
|
210
|
-
strict_response_parsing # Currently unused but kept
|
|
211
|
-
)
|
|
212
207
|
self.last_request_time: float = time.monotonic() # Initialize timestamp for rate limiting
|
|
213
208
|
|
|
214
209
|
# --- Response Validation Setting ---
|
|
@@ -1435,6 +1430,11 @@ class KarakeepAPI:
|
|
|
1435
1430
|
logger.debug("Skipping response validation as requested.")
|
|
1436
1431
|
return response_data
|
|
1437
1432
|
else:
|
|
1433
|
+
# As of version 0.24.1 of karakeep: we do not check the correct
|
|
1434
|
+
# validation type because there is an error on the
|
|
1435
|
+
# server side: https://github.com/karakeep-app/karakeep/issues/1365
|
|
1436
|
+
return response_data
|
|
1437
|
+
|
|
1438
1438
|
# Response should match Tag1 schema
|
|
1439
1439
|
return datatypes.Tag1.model_validate(response_data)
|
|
1440
1440
|
|
{karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4/karakeep_python_api.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karakeep_python_api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
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.
|
|
10
|
+
version="0.1.4",
|
|
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",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import pytest
|
|
2
|
+
from loguru import logger
|
|
2
3
|
import os
|
|
3
4
|
import subprocess
|
|
4
5
|
import random
|
|
@@ -24,23 +25,23 @@ def test_get_all_bookmarks_paginated(karakeep_client: KarakeepAPI):
|
|
|
24
25
|
), "Response should be PaginatedBookmarks model"
|
|
25
26
|
assert isinstance(page1.bookmarks, list), "Bookmarks attribute should be a list"
|
|
26
27
|
assert len(page1.bookmarks) <= 2, "Should return at most 'limit' bookmarks"
|
|
27
|
-
|
|
28
|
+
logger.info(f"✓ Retrieved first page with {len(page1.bookmarks)} bookmarks.")
|
|
28
29
|
|
|
29
30
|
# If there's a next cursor, get the next page
|
|
30
31
|
if page1.nextCursor:
|
|
31
|
-
|
|
32
|
+
logger.info(f" Attempting to fetch next page with cursor: {page1.nextCursor}")
|
|
32
33
|
page2 = karakeep_client.get_all_bookmarks(limit=2, cursor=page1.nextCursor)
|
|
33
34
|
assert isinstance(page2, datatypes.PaginatedBookmarks)
|
|
34
35
|
assert isinstance(page2.bookmarks, list)
|
|
35
36
|
assert len(page2.bookmarks) <= 2
|
|
36
|
-
|
|
37
|
+
logger.info(f"✓ Retrieved second page with {len(page2.bookmarks)} bookmarks.")
|
|
37
38
|
# Ensure bookmarks are different from page 1 (simple check)
|
|
38
39
|
if page1.bookmarks and page2.bookmarks:
|
|
39
40
|
assert (
|
|
40
41
|
page1.bookmarks[0].id != page2.bookmarks[0].id
|
|
41
42
|
), "Bookmarks on page 1 and 2 should differ"
|
|
42
43
|
else:
|
|
43
|
-
|
|
44
|
+
logger.info(" No next cursor found, pagination test ends.")
|
|
44
45
|
|
|
45
46
|
except (APIError, AuthenticationError) as e:
|
|
46
47
|
pytest.fail(f"API error during paginated bookmark retrieval: {e}")
|
|
@@ -51,21 +52,21 @@ def test_get_all_bookmarks_paginated(karakeep_client: KarakeepAPI):
|
|
|
51
52
|
|
|
52
53
|
# --- Add CLI call ---
|
|
53
54
|
try:
|
|
54
|
-
|
|
55
|
+
logger.info("\n Running CLI equivalent: get-all-bookmarks --limit 2")
|
|
55
56
|
# Assumes KARAKEEP_PYTHON_API_BASE_URL and KARAKEEP_PYTHON_API_KEY are set in env
|
|
56
57
|
subprocess.run(
|
|
57
58
|
"python -m karakeep_python_api get-all-bookmarks --limit 2",
|
|
58
59
|
shell=True,
|
|
59
60
|
check=True,
|
|
60
|
-
capture_output=True, # Capture output to avoid
|
|
61
|
+
capture_output=True, # Capture output to avoid logger.infoing it during tests unless verbose
|
|
61
62
|
text=True,
|
|
62
63
|
)
|
|
63
|
-
|
|
64
|
+
logger.info("✓ CLI command executed successfully.")
|
|
64
65
|
except subprocess.CalledProcessError as e:
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
logger.info(f" CLI command failed with exit code {e.returncode}")
|
|
67
|
+
# logger.info stdout/stderr only if the command failed to aid debugging
|
|
68
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
69
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
69
70
|
pytest.fail(f"CLI command 'get-all-bookmarks --limit 2' failed: {e}")
|
|
70
71
|
except Exception as e:
|
|
71
72
|
pytest.fail(f"An unexpected error occurred running the CLI command: {e}")
|
|
@@ -80,7 +81,7 @@ def test_get_all_lists(karakeep_client: KarakeepAPI):
|
|
|
80
81
|
assert all(
|
|
81
82
|
isinstance(item, datatypes.ListModel) for item in lists
|
|
82
83
|
), "All items should be ListModel instances"
|
|
83
|
-
|
|
84
|
+
logger.info(f"✓ Successfully retrieved {len(lists)} lists.")
|
|
84
85
|
except (APIError, AuthenticationError) as e:
|
|
85
86
|
pytest.fail(f"API error during list retrieval: {e}")
|
|
86
87
|
except Exception as e:
|
|
@@ -88,21 +89,21 @@ def test_get_all_lists(karakeep_client: KarakeepAPI):
|
|
|
88
89
|
|
|
89
90
|
# --- Add CLI call ---
|
|
90
91
|
try:
|
|
91
|
-
|
|
92
|
+
logger.info("\n Running CLI equivalent: get-all-lists")
|
|
92
93
|
# Assumes KARAKEEP_PYTHON_API_BASE_URL and KARAKEEP_PYTHON_API_KEY are set in env
|
|
93
94
|
subprocess.run(
|
|
94
95
|
"python -m karakeep_python_api get-all-lists",
|
|
95
96
|
shell=True,
|
|
96
97
|
check=True,
|
|
97
|
-
capture_output=True, # Capture output to avoid
|
|
98
|
+
capture_output=True, # Capture output to avoid logger.infoing it during tests unless verbose
|
|
98
99
|
text=True,
|
|
99
100
|
)
|
|
100
|
-
|
|
101
|
+
logger.info("✓ CLI command executed successfully.")
|
|
101
102
|
except subprocess.CalledProcessError as e:
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
logger.info(f" CLI command failed with exit code {e.returncode}")
|
|
104
|
+
# logger.info stdout/stderr only if the command failed to aid debugging
|
|
105
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
106
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
106
107
|
pytest.fail(f"CLI command 'get-all-lists' failed: {e}")
|
|
107
108
|
except Exception as e:
|
|
108
109
|
pytest.fail(f"An unexpected error occurred running the CLI command: {e}")
|
|
@@ -117,7 +118,7 @@ def test_get_all_tags(karakeep_client: KarakeepAPI):
|
|
|
117
118
|
assert all(
|
|
118
119
|
isinstance(item, datatypes.Tag1) for item in tags
|
|
119
120
|
), "All items should be Tag1 instances"
|
|
120
|
-
|
|
121
|
+
logger.info(f"✓ Successfully retrieved {len(tags)} tags.")
|
|
121
122
|
except (APIError, AuthenticationError) as e:
|
|
122
123
|
pytest.fail(f"API error during tag retrieval: {e}")
|
|
123
124
|
except Exception as e:
|
|
@@ -125,21 +126,21 @@ def test_get_all_tags(karakeep_client: KarakeepAPI):
|
|
|
125
126
|
|
|
126
127
|
# --- Add CLI call ---
|
|
127
128
|
try:
|
|
128
|
-
|
|
129
|
+
logger.info("\n Running CLI equivalent: get-all-tags")
|
|
129
130
|
# Assumes KARAKEEP_PYTHON_API_BASE_URL and KARAKEEP_PYTHON_API_KEY are set in env
|
|
130
131
|
subprocess.run(
|
|
131
132
|
"python -m karakeep_python_api get-all-tags",
|
|
132
133
|
shell=True,
|
|
133
134
|
check=True,
|
|
134
|
-
capture_output=True, # Capture output to avoid
|
|
135
|
+
capture_output=True, # Capture output to avoid logger.infoing it during tests unless verbose
|
|
135
136
|
text=True,
|
|
136
137
|
)
|
|
137
|
-
|
|
138
|
+
logger.info("✓ CLI command executed successfully.")
|
|
138
139
|
except subprocess.CalledProcessError as e:
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
logger.info(f" CLI command failed with exit code {e.returncode}")
|
|
141
|
+
# logger.info stdout/stderr only if the command failed to aid debugging
|
|
142
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
143
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
143
144
|
pytest.fail(f"CLI command 'get-all-tags' failed: {e}")
|
|
144
145
|
except Exception as e:
|
|
145
146
|
pytest.fail(f"An unexpected error occurred running the CLI command: {e}")
|
|
@@ -157,23 +158,23 @@ def test_get_all_highlights_paginated(karakeep_client: KarakeepAPI):
|
|
|
157
158
|
page1.highlights, list
|
|
158
159
|
), "Highlights attribute should be a list"
|
|
159
160
|
assert len(page1.highlights) <= 3, "Should return at most 'limit' highlights"
|
|
160
|
-
|
|
161
|
+
logger.info(f"✓ Retrieved first page with {len(page1.highlights)} highlights.")
|
|
161
162
|
|
|
162
163
|
# If there's a next cursor, get the next page
|
|
163
164
|
if page1.nextCursor:
|
|
164
|
-
|
|
165
|
+
logger.info(f" Attempting to fetch next page with cursor: {page1.nextCursor}")
|
|
165
166
|
page2 = karakeep_client.get_all_highlights(limit=3, cursor=page1.nextCursor)
|
|
166
167
|
assert isinstance(page2, datatypes.PaginatedHighlights)
|
|
167
168
|
assert isinstance(page2.highlights, list)
|
|
168
169
|
assert len(page2.highlights) <= 3
|
|
169
|
-
|
|
170
|
+
logger.info(f"✓ Retrieved second page with {len(page2.highlights)} highlights.")
|
|
170
171
|
# Ensure highlights are different from page 1 (simple check)
|
|
171
172
|
if page1.highlights and page2.highlights:
|
|
172
173
|
assert (
|
|
173
174
|
page1.highlights[0].id != page2.highlights[0].id
|
|
174
175
|
), "Highlights on page 1 and 2 should differ"
|
|
175
176
|
else:
|
|
176
|
-
|
|
177
|
+
logger.info(" No next cursor found, pagination test ends.")
|
|
177
178
|
|
|
178
179
|
except (APIError, AuthenticationError) as e:
|
|
179
180
|
pytest.fail(f"API error during paginated highlight retrieval: {e}")
|
|
@@ -184,21 +185,21 @@ def test_get_all_highlights_paginated(karakeep_client: KarakeepAPI):
|
|
|
184
185
|
|
|
185
186
|
# --- Add CLI call ---
|
|
186
187
|
try:
|
|
187
|
-
|
|
188
|
+
logger.info("\n Running CLI equivalent: get-all-highlights --limit 3")
|
|
188
189
|
# Assumes KARAKEEP_PYTHON_API_BASE_URL and KARAKEEP_PYTHON_API_KEY are set in env
|
|
189
190
|
subprocess.run(
|
|
190
191
|
"python -m karakeep_python_api get-all-highlights --limit 3",
|
|
191
192
|
shell=True,
|
|
192
193
|
check=True,
|
|
193
|
-
capture_output=True, # Capture output to avoid
|
|
194
|
+
capture_output=True, # Capture output to avoid logger.infoing it during tests unless verbose
|
|
194
195
|
text=True,
|
|
195
196
|
)
|
|
196
|
-
|
|
197
|
+
logger.info("✓ CLI command executed successfully.")
|
|
197
198
|
except subprocess.CalledProcessError as e:
|
|
198
|
-
|
|
199
|
-
#
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
logger.info(f" CLI command failed with exit code {e.returncode}")
|
|
200
|
+
# logger.info stdout/stderr only if the command failed to aid debugging
|
|
201
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
202
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
202
203
|
pytest.fail(f"CLI command 'get-all-highlights --limit 3' failed: {e}")
|
|
203
204
|
except Exception as e:
|
|
204
205
|
pytest.fail(f"An unexpected error occurred running the CLI command: {e}")
|
|
@@ -217,7 +218,7 @@ def test_openapi_spec_accessible(karakeep_client: KarakeepAPI):
|
|
|
217
218
|
assert (
|
|
218
219
|
"openapi" in spec
|
|
219
220
|
), "openapi_spec should contain the 'openapi' version key"
|
|
220
|
-
|
|
221
|
+
logger.info(
|
|
221
222
|
f"✓ Successfully accessed openapi_spec attribute. Version: {spec.get('openapi', 'N/A')}"
|
|
222
223
|
)
|
|
223
224
|
except Exception as e:
|
|
@@ -226,21 +227,21 @@ def test_openapi_spec_accessible(karakeep_client: KarakeepAPI):
|
|
|
226
227
|
# --- Add CLI call ---
|
|
227
228
|
# The closest CLI equivalent is dumping the spec file content
|
|
228
229
|
try:
|
|
229
|
-
|
|
230
|
+
logger.info("\n Running CLI equivalent: --dump-openapi-specification")
|
|
230
231
|
# This command doesn't require API key or base URL
|
|
231
232
|
subprocess.run(
|
|
232
233
|
"python -m karakeep_python_api --dump-openapi-specification",
|
|
233
234
|
shell=True,
|
|
234
235
|
check=True,
|
|
235
|
-
capture_output=True, # Capture output to avoid
|
|
236
|
+
capture_output=True, # Capture output to avoid logger.infoing it during tests unless verbose
|
|
236
237
|
text=True,
|
|
237
238
|
)
|
|
238
|
-
|
|
239
|
+
logger.info("✓ CLI command executed successfully.")
|
|
239
240
|
except subprocess.CalledProcessError as e:
|
|
240
|
-
|
|
241
|
-
#
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
logger.info(f" CLI command failed with exit code {e.returncode}")
|
|
242
|
+
# logger.info stdout/stderr only if the command failed to aid debugging
|
|
243
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
244
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
244
245
|
pytest.fail(f"CLI command '--dump-openapi-specification' failed: {e}")
|
|
245
246
|
except Exception as e:
|
|
246
247
|
pytest.fail(f"An unexpected error occurred running the CLI command: {e}")
|
|
@@ -259,12 +260,12 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
|
|
|
259
260
|
list_name = f"Test List {timestamp}-{random_suffix}"
|
|
260
261
|
list_icon = "🧪" # Test tube icon
|
|
261
262
|
|
|
262
|
-
|
|
263
|
+
logger.info(f"\nAttempting to create list: Name='{list_name}', Icon='{list_icon}'")
|
|
263
264
|
|
|
264
265
|
# 2. Get initial list count (optional, for comparison)
|
|
265
266
|
initial_lists = karakeep_client.get_all_lists()
|
|
266
267
|
initial_list_count = len(initial_lists)
|
|
267
|
-
|
|
268
|
+
logger.info(f" Initial list count: {initial_list_count}")
|
|
268
269
|
|
|
269
270
|
# 3. Create the new list
|
|
270
271
|
create_payload = {"name": list_name, "icon": list_icon, "type": "manual"}
|
|
@@ -276,7 +277,7 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
|
|
|
276
277
|
assert created_list.icon == list_icon, "Created list icon should match"
|
|
277
278
|
assert created_list.id, "Created list must have an ID"
|
|
278
279
|
created_list_id = created_list.id # Store the ID for deletion
|
|
279
|
-
|
|
280
|
+
logger.info(f"✓ Successfully created list with ID: {created_list_id}")
|
|
280
281
|
|
|
281
282
|
# 4. Verify the list appears in get_all_lists
|
|
282
283
|
current_lists_after_create = karakeep_client.get_all_lists()
|
|
@@ -286,15 +287,15 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
|
|
|
286
287
|
assert any(
|
|
287
288
|
lst.id == created_list_id for lst in current_lists_after_create
|
|
288
289
|
), "Created list should be present in the list of all lists"
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
logger.info(f" List count after creation: {len(current_lists_after_create)}")
|
|
291
|
+
logger.info(f"✓ Verified list {created_list_id} is present in get_all_lists.")
|
|
291
292
|
|
|
292
293
|
|
|
293
294
|
# 5. Verify the list exists by getting it directly (redundant but good check)
|
|
294
295
|
retrieved_list = karakeep_client.get_a_single_list(list_id=created_list_id)
|
|
295
296
|
assert isinstance(retrieved_list, datatypes.ListModel)
|
|
296
297
|
assert retrieved_list.id == created_list_id
|
|
297
|
-
|
|
298
|
+
logger.info(f"✓ Successfully retrieved the created list by ID.")
|
|
298
299
|
|
|
299
300
|
except (APIError, AuthenticationError) as e:
|
|
300
301
|
pytest.fail(f"API error during list creation/verification: {e}")
|
|
@@ -303,10 +304,10 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
|
|
|
303
304
|
finally:
|
|
304
305
|
# 6. Delete the list (ensure cleanup even if assertions fail)
|
|
305
306
|
if created_list_id:
|
|
306
|
-
|
|
307
|
+
logger.info(f"\nAttempting to delete list with ID: {created_list_id}")
|
|
307
308
|
try:
|
|
308
309
|
karakeep_client.delete_a_list(list_id=created_list_id)
|
|
309
|
-
|
|
310
|
+
logger.info(f"✓ Successfully deleted list with ID: {created_list_id}")
|
|
310
311
|
|
|
311
312
|
# 7. Verify the list is gone by trying to get it (should fail)
|
|
312
313
|
try:
|
|
@@ -318,7 +319,7 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
|
|
|
318
319
|
assert (
|
|
319
320
|
e.status_code == 404
|
|
320
321
|
), f"Expected 404 Not Found when getting deleted list, but got status {e.status_code}"
|
|
321
|
-
|
|
322
|
+
logger.info(
|
|
322
323
|
f"✓ Confirmed list {created_list_id} is deleted (received 404)."
|
|
323
324
|
)
|
|
324
325
|
|
|
@@ -330,65 +331,82 @@ def test_create_and_delete_list(karakeep_client: KarakeepAPI):
|
|
|
330
331
|
assert not any(
|
|
331
332
|
lst.id == created_list_id for lst in final_lists
|
|
332
333
|
), "Deleted list should not be present in the final list of all lists"
|
|
333
|
-
|
|
334
|
+
logger.info(f" Final list count: {len(final_lists)}")
|
|
334
335
|
|
|
335
336
|
except (APIError, AuthenticationError) as e:
|
|
336
337
|
pytest.fail(f"API error during list deletion: {e}")
|
|
337
338
|
except Exception as e:
|
|
338
339
|
pytest.fail(f"An unexpected error occurred during list deletion: {e}")
|
|
339
340
|
else:
|
|
340
|
-
|
|
341
|
+
logger.info("\nSkipping deletion because list creation failed or ID was not obtained.")
|
|
341
342
|
|
|
342
343
|
|
|
343
|
-
def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI):
|
|
344
|
-
"""
|
|
345
|
-
|
|
346
|
-
|
|
344
|
+
def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI, managed_bookmark: datatypes.Bookmark):
|
|
345
|
+
"""
|
|
346
|
+
Test verifying a created bookmark (via fixture) and searching for it.
|
|
347
|
+
The fixture handles creation and deletion.
|
|
348
|
+
"""
|
|
349
|
+
created_bookmark_id = managed_bookmark.id
|
|
350
|
+
test_url = managed_bookmark.content.url # Get URL from fixture
|
|
351
|
+
original_title = managed_bookmark.title # Get title from fixture
|
|
352
|
+
|
|
347
353
|
try:
|
|
348
|
-
# 1.
|
|
349
|
-
|
|
354
|
+
# 1. Bookmark is already created by the 'managed_bookmark' fixture.
|
|
355
|
+
logger.info(f"\nUsing managed bookmark ID: {created_bookmark_id}, URL: '{test_url}', Title: '{original_title}'")
|
|
350
356
|
|
|
351
|
-
# 2.
|
|
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
|
|
357
|
+
# 2. Verify the bookmark exists by getting it directly
|
|
365
358
|
retrieved_bookmark = karakeep_client.get_a_single_bookmark(
|
|
366
359
|
bookmark_id=created_bookmark_id
|
|
367
360
|
)
|
|
368
361
|
assert isinstance(retrieved_bookmark, datatypes.Bookmark)
|
|
369
362
|
assert retrieved_bookmark.id == created_bookmark_id
|
|
370
363
|
assert retrieved_bookmark.content.url == test_url
|
|
371
|
-
|
|
364
|
+
assert retrieved_bookmark.title == original_title
|
|
365
|
+
logger.info(f"✓ Successfully retrieved the managed bookmark by ID.")
|
|
372
366
|
|
|
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
367
|
|
|
388
|
-
#
|
|
389
|
-
|
|
368
|
+
# 3. Search for the created bookmark
|
|
369
|
+
# Use a search query that is likely to match the fixture's title
|
|
370
|
+
# The fixture title is "Managed Fixture Bookmark {timestamp}-{random_suffix}"
|
|
371
|
+
# A simple search for "Managed Fixture Bookmark" should work.
|
|
372
|
+
# If the title is very dynamic, searching by URL might be more robust if supported,
|
|
373
|
+
# or by a known part of the title.
|
|
374
|
+
|
|
375
|
+
# waiting a bit for the indexation just in case
|
|
376
|
+
time.sleep(30)
|
|
377
|
+
|
|
378
|
+
search_queries = [
|
|
379
|
+
"Managed Fixture Bookmark",
|
|
380
|
+
"managed fixture bookmark",
|
|
381
|
+
"managed fixture",
|
|
382
|
+
"fixture managed",
|
|
383
|
+
"fixture",
|
|
384
|
+
'"fixture"',
|
|
385
|
+
]
|
|
386
|
+
for trial, search_query_component in enumerate(search_queries):
|
|
387
|
+
logger.info(f"\nAttempting to search for bookmark with query based on title: '{search_query_component}'. Retrying multiple times because search is nondeterministic.")
|
|
388
|
+
search_results = karakeep_client.search_bookmarks(q=search_query_component, limit=100, include_content=False)
|
|
389
|
+
assert isinstance(
|
|
390
|
+
search_results, datatypes.PaginatedBookmarks
|
|
391
|
+
), "Search response should be PaginatedBookmarks model"
|
|
392
|
+
assert isinstance(
|
|
393
|
+
search_results.bookmarks, list
|
|
394
|
+
), "Search results bookmarks attribute should be a list"
|
|
395
|
+
|
|
396
|
+
titles_in_search = [b.title for b in search_results.bookmarks]
|
|
397
|
+
found_in_search = any(b.id == created_bookmark_id for b in search_results.bookmarks)
|
|
398
|
+
if found_in_search:
|
|
399
|
+
break
|
|
400
|
+
else:
|
|
401
|
+
time.sleep(3)
|
|
402
|
+
assert found_in_search, \
|
|
403
|
+
f"Managed bookmark {created_bookmark_id} (Title: '{original_title}') not found in {trial + 1} different search results for '{search_query_component}'. Titles were: '{titles_in_search}'."
|
|
404
|
+
logger.info(f"✓ Found managed bookmark in search results for '{search_query_component}'.")
|
|
405
|
+
|
|
406
|
+
# 4. Test CLI search equivalent
|
|
407
|
+
logger.info(f"\n Running CLI equivalent: search-bookmarks --q '{search_query_component}' --limit 10 --include-content false")
|
|
390
408
|
try:
|
|
391
|
-
cli_search_command = f"python -m karakeep_python_api search-bookmarks --q '{
|
|
409
|
+
cli_search_command = f"python -m karakeep_python_api search-bookmarks --q '{search_query_component}' --limit 10 --include-content false"
|
|
392
410
|
search_cli_output = subprocess.run(
|
|
393
411
|
cli_search_command,
|
|
394
412
|
shell=True,
|
|
@@ -396,121 +414,42 @@ def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI):
|
|
|
396
414
|
capture_output=True,
|
|
397
415
|
text=True,
|
|
398
416
|
)
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
print("✓ CLI search command executed successfully and contained the bookmark ID.")
|
|
417
|
+
assert created_bookmark_id in search_cli_output.stdout, \
|
|
418
|
+
f"Managed bookmark ID {created_bookmark_id} not found in CLI search output for '{search_query_component}'"
|
|
419
|
+
logger.info("✓ CLI search command executed successfully and contained the bookmark ID.")
|
|
403
420
|
except subprocess.CalledProcessError as e:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
pytest.fail(f"CLI command 'search-bookmarks --q {
|
|
421
|
+
logger.info(f" CLI search command failed with exit code {e.returncode}")
|
|
422
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
423
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
424
|
+
pytest.fail(f"CLI command 'search-bookmarks --q \"{search_query_component}\"' failed: {e}")
|
|
408
425
|
except Exception as e:
|
|
409
426
|
pytest.fail(f"An unexpected error occurred running the CLI search command: {e}")
|
|
410
427
|
|
|
411
|
-
|
|
412
428
|
except (APIError, AuthenticationError) as e:
|
|
413
|
-
pytest.fail(f"API error during bookmark
|
|
429
|
+
pytest.fail(f"API error during bookmark verification/search: {e}")
|
|
414
430
|
except Exception as e:
|
|
415
431
|
pytest.fail(
|
|
416
|
-
f"An unexpected error occurred during bookmark
|
|
432
|
+
f"An unexpected error occurred during bookmark verification/search: {e}"
|
|
417
433
|
)
|
|
418
|
-
finally
|
|
419
|
-
|
|
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
|
-
)
|
|
434
|
+
# No 'finally' block for deletion needed, as 'managed_bookmark' fixture handles it.
|
|
435
|
+
# The fixture also handles verification of deletion.
|
|
441
436
|
|
|
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
|
-
)
|
|
476
437
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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
|
|
438
|
+
def test_update_bookmark_title(karakeep_client: KarakeepAPI, managed_bookmark: datatypes.Bookmark):
|
|
439
|
+
"""Test updating a bookmark's title via API and CLI, using a managed bookmark."""
|
|
440
|
+
created_bookmark_id = managed_bookmark.id
|
|
441
|
+
original_title = managed_bookmark.title # Get the original title from the fixture
|
|
496
442
|
|
|
497
443
|
target_api_title = "this is a test title"
|
|
498
444
|
target_cli_title = "this is a test title (CLI)"
|
|
499
445
|
|
|
500
446
|
try:
|
|
501
|
-
#
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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}'")
|
|
511
|
-
|
|
512
|
-
# 2. Update the bookmark's title using the API client
|
|
513
|
-
print(f"\nAttempting to update bookmark ID {created_bookmark_id} title to: '{target_api_title}' via API")
|
|
447
|
+
# The bookmark is already created by the 'managed_bookmark' fixture.
|
|
448
|
+
# We have its ID in created_bookmark_id and its original title.
|
|
449
|
+
logger.info(f"\nUsing managed bookmark ID: {created_bookmark_id}, Original Title: '{original_title}'")
|
|
450
|
+
|
|
451
|
+
# 1. Update the bookmark's title using the API client
|
|
452
|
+
logger.info(f"\nAttempting to update bookmark ID {created_bookmark_id} title to: '{target_api_title}' via API")
|
|
514
453
|
update_payload_api = {"title": target_api_title}
|
|
515
454
|
updated_bookmark_partial = karakeep_client.update_a_bookmark(
|
|
516
455
|
bookmark_id=created_bookmark_id, update_data=update_payload_api
|
|
@@ -518,20 +457,20 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
|
|
|
518
457
|
assert isinstance(updated_bookmark_partial, dict), "Update response should be a dict"
|
|
519
458
|
assert updated_bookmark_partial.get("title") == target_api_title, \
|
|
520
459
|
f"Partial response title '{updated_bookmark_partial.get('title')}' does not match target API title '{target_api_title}'"
|
|
521
|
-
|
|
460
|
+
logger.info(f"✓ API call to update_a_bookmark successful. Partial response title: '{updated_bookmark_partial.get('title')}'")
|
|
522
461
|
|
|
523
|
-
#
|
|
524
|
-
|
|
462
|
+
# 2. Verify the API update by fetching the bookmark again
|
|
463
|
+
logger.info(f"\nFetching bookmark ID {created_bookmark_id} to verify API title update.")
|
|
525
464
|
retrieved_bookmark_after_api_update = karakeep_client.get_a_single_bookmark(
|
|
526
465
|
bookmark_id=created_bookmark_id
|
|
527
466
|
)
|
|
528
467
|
assert isinstance(retrieved_bookmark_after_api_update, datatypes.Bookmark)
|
|
529
468
|
assert retrieved_bookmark_after_api_update.title == target_api_title, \
|
|
530
469
|
f"Retrieved bookmark title '{retrieved_bookmark_after_api_update.title}' does not match expected API-updated title '{target_api_title}'"
|
|
531
|
-
|
|
470
|
+
logger.info(f"✓ Successfully verified bookmark title updated by API to: '{retrieved_bookmark_after_api_update.title}'")
|
|
532
471
|
|
|
533
|
-
#
|
|
534
|
-
|
|
472
|
+
# 3. Test CLI equivalent for updating the bookmark's title
|
|
473
|
+
logger.info(f"\n Running CLI equivalent to update title to: '{target_cli_title}'")
|
|
535
474
|
cli_update_payload_json = json.dumps({"title": target_cli_title})
|
|
536
475
|
# Ensure the JSON string is properly quoted for the shell command
|
|
537
476
|
cli_update_command = f"python -m karakeep_python_api update-a-bookmark --bookmark-id {created_bookmark_id} --update-data '{cli_update_payload_json}'"
|
|
@@ -544,23 +483,23 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
|
|
|
544
483
|
capture_output=True,
|
|
545
484
|
text=True,
|
|
546
485
|
)
|
|
547
|
-
|
|
486
|
+
logger.info("✓ CLI update command executed successfully.")
|
|
548
487
|
|
|
549
|
-
#
|
|
550
|
-
|
|
488
|
+
# 4. Verify CLI update by fetching the bookmark again
|
|
489
|
+
logger.info(f"\nFetching bookmark ID {created_bookmark_id} to verify CLI title update.")
|
|
551
490
|
retrieved_bookmark_after_cli_update = karakeep_client.get_a_single_bookmark(
|
|
552
491
|
bookmark_id=created_bookmark_id
|
|
553
492
|
)
|
|
554
493
|
assert isinstance(retrieved_bookmark_after_cli_update, datatypes.Bookmark)
|
|
555
494
|
assert retrieved_bookmark_after_cli_update.title == target_cli_title, \
|
|
556
495
|
f"Retrieved bookmark title '{retrieved_bookmark_after_cli_update.title}' after CLI update does not match expected '{target_cli_title}'"
|
|
557
|
-
|
|
496
|
+
logger.info(f"✓ Successfully verified bookmark title updated by CLI to: '{retrieved_bookmark_after_cli_update.title}'")
|
|
558
497
|
|
|
559
498
|
except subprocess.CalledProcessError as e:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
499
|
+
logger.info(f" CLI update command failed with exit code {e.returncode}")
|
|
500
|
+
logger.info(f" Command: {cli_update_command}")
|
|
501
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
502
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
564
503
|
pytest.fail(f"CLI command for update-a-bookmark failed: {e}")
|
|
565
504
|
except Exception as e:
|
|
566
505
|
pytest.fail(f"An unexpected error occurred running the CLI update command: {e}")
|
|
@@ -569,18 +508,94 @@ def test_update_bookmark_title(karakeep_client: KarakeepAPI):
|
|
|
569
508
|
pytest.fail(f"API error during bookmark title update test: {e}")
|
|
570
509
|
except Exception as e:
|
|
571
510
|
pytest.fail(f"An unexpected error occurred during bookmark title update test: {e}")
|
|
511
|
+
# No finally block needed for deletion, as 'managed_bookmark' fixture handles it.
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def test_tag_lifecycle_on_bookmark(karakeep_client: KarakeepAPI, managed_bookmark: datatypes.Bookmark):
|
|
515
|
+
"""
|
|
516
|
+
Test attaching a tag to a bookmark, updating the tag, detaching it, and deleting it.
|
|
517
|
+
Uses the managed_bookmark fixture.
|
|
518
|
+
"""
|
|
519
|
+
bookmark_id = managed_bookmark.id
|
|
520
|
+
timestamp = int(time.time())
|
|
521
|
+
random_chars = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
|
522
|
+
initial_tag_name = f"test-tag-{timestamp}-{random_chars}"
|
|
523
|
+
updated_tag_name = f"updated-tag-{timestamp}-{random_chars}"
|
|
524
|
+
tag_id_to_manage = None
|
|
525
|
+
|
|
526
|
+
try:
|
|
527
|
+
# 1. Attach a new tag by name to the bookmark
|
|
528
|
+
logger.info(f"\nAttempting to attach tag '{initial_tag_name}' to bookmark {bookmark_id}")
|
|
529
|
+
attach_payload = {"tags": [{"tagName": initial_tag_name}]}
|
|
530
|
+
attach_response = karakeep_client.attach_tags_to_a_bookmark(
|
|
531
|
+
bookmark_id=bookmark_id, tags_data=attach_payload
|
|
532
|
+
)
|
|
533
|
+
assert "attached" in attach_response and len(attach_response["attached"]) == 1, \
|
|
534
|
+
"Failed to attach tag or response format incorrect"
|
|
535
|
+
tag_id_to_manage = attach_response["attached"][0]
|
|
536
|
+
assert isinstance(tag_id_to_manage, str), "Attached tag ID should be a string"
|
|
537
|
+
logger.info(f"✓ Tag '{initial_tag_name}' attached with ID: {tag_id_to_manage}")
|
|
538
|
+
|
|
539
|
+
# 2. Update the tag's name
|
|
540
|
+
logger.info(f"\nAttempting to update tag {tag_id_to_manage} to name '{updated_tag_name}'")
|
|
541
|
+
update_payload = {"name": updated_tag_name}
|
|
542
|
+
updated_tag = karakeep_client.update_a_tag(
|
|
543
|
+
tag_id=tag_id_to_manage, update_data=update_payload
|
|
544
|
+
)
|
|
545
|
+
# Do not check the type because karakeep 0.24.1 has a server side bug
|
|
546
|
+
# assert isinstance(updated_tag, datatypes.Tag1), "Update tag response should be Tag1 model"
|
|
547
|
+
# assert updated_tag.name == updated_tag_name, "Tag name was not updated as expected"
|
|
548
|
+
# logger.info(f"✓ Tag {tag_id_to_manage} updated to name '{updated_tag.name}'")
|
|
549
|
+
assert updated_tag["name"] == updated_tag_name, "Tag name was not updated as expected"
|
|
550
|
+
logger.info(f"✓ Tag {tag_id_to_manage} updated to name '{updated_tag['name']}'")
|
|
551
|
+
|
|
552
|
+
# 3. Verify tag update by getting it directly
|
|
553
|
+
logger.info(f"\nFetching tag {tag_id_to_manage} to verify its name is '{updated_tag_name}'")
|
|
554
|
+
retrieved_tag = karakeep_client.get_a_single_tag(tag_id=tag_id_to_manage)
|
|
555
|
+
assert isinstance(retrieved_tag, datatypes.Tag1), "Get single tag response should be Tag1 model"
|
|
556
|
+
assert retrieved_tag.name == updated_tag_name, "Retrieved tag name does not match updated name"
|
|
557
|
+
assert retrieved_tag.id == tag_id_to_manage, "Retrieved tag ID does not match"
|
|
558
|
+
logger.info(f"✓ Verified tag {tag_id_to_manage} has name '{retrieved_tag.name}'")
|
|
559
|
+
|
|
560
|
+
# 4. Detach the tag from the bookmark
|
|
561
|
+
logger.info(f"\nAttempting to detach tag {tag_id_to_manage} from bookmark {bookmark_id}")
|
|
562
|
+
detach_payload = {"tags": [{"tagId": tag_id_to_manage}]}
|
|
563
|
+
detach_response = karakeep_client.detach_tags_from_a_bookmark(
|
|
564
|
+
bookmark_id=bookmark_id, tags_data=detach_payload
|
|
565
|
+
)
|
|
566
|
+
assert "detached" in detach_response and tag_id_to_manage in detach_response["detached"], \
|
|
567
|
+
"Failed to detach tag or response format incorrect"
|
|
568
|
+
logger.info(f"✓ Tag {tag_id_to_manage} detached from bookmark {bookmark_id}")
|
|
569
|
+
|
|
570
|
+
except (APIError, AuthenticationError) as e:
|
|
571
|
+
pytest.fail(f"API error during tag lifecycle test: {e}")
|
|
572
|
+
except Exception as e:
|
|
573
|
+
pytest.fail(f"An unexpected error occurred during tag lifecycle test: {e}")
|
|
572
574
|
finally:
|
|
573
|
-
#
|
|
574
|
-
if
|
|
575
|
-
|
|
575
|
+
# 5. Delete the tag (ensure cleanup even if assertions fail mid-test)
|
|
576
|
+
if tag_id_to_manage:
|
|
577
|
+
logger.info(f"\nAttempting to delete tag {tag_id_to_manage} (cleanup)")
|
|
576
578
|
try:
|
|
577
|
-
karakeep_client.
|
|
578
|
-
|
|
579
|
+
karakeep_client.delete_a_tag(tag_id=tag_id_to_manage)
|
|
580
|
+
logger.info(f"✓ Successfully deleted tag {tag_id_to_manage}")
|
|
581
|
+
|
|
582
|
+
# 6. Verify the tag is gone by trying to get it (should fail with 404)
|
|
583
|
+
try:
|
|
584
|
+
karakeep_client.get_a_single_tag(tag_id=tag_id_to_manage)
|
|
585
|
+
pytest.fail(
|
|
586
|
+
f"Tag {tag_id_to_manage} should not exist after deletion, but get_a_single_tag succeeded."
|
|
587
|
+
)
|
|
588
|
+
except APIError as e:
|
|
589
|
+
assert e.status_code == 404, \
|
|
590
|
+
f"Expected 404 Not Found when getting deleted tag, but got status {e.status_code}"
|
|
591
|
+
logger.info(f"✓ Confirmed tag {tag_id_to_manage} is deleted (received 404).")
|
|
592
|
+
except (APIError, AuthenticationError) as e:
|
|
593
|
+
# Log error during cleanup but don't let it mask original test failure
|
|
594
|
+
logger.info(f" API error during tag deletion (cleanup) for ID {tag_id_to_manage}: {e}")
|
|
579
595
|
except Exception as e:
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
# pytest.fail(f"Failed to delete bookmark {created_bookmark_id} during cleanup: {e}")
|
|
596
|
+
logger.info(f" Unexpected error during tag deletion (cleanup) for ID {tag_id_to_manage}: {e}")
|
|
597
|
+
else:
|
|
598
|
+
logger.info("\nSkipping tag deletion (cleanup) because tag_id was not obtained or test failed before creation.")
|
|
584
599
|
|
|
585
600
|
|
|
586
601
|
# --- Test User Info/Stats Endpoints ---
|
|
@@ -601,7 +616,7 @@ def test_get_current_user_stats(karakeep_client: KarakeepAPI):
|
|
|
601
616
|
assert isinstance(stats["numLists"], int) and stats["numLists"] >= 0
|
|
602
617
|
assert isinstance(stats["numTags"], int) and stats["numTags"] >= 0
|
|
603
618
|
|
|
604
|
-
|
|
619
|
+
logger.info(f"✓ Successfully retrieved user stats: {stats}")
|
|
605
620
|
|
|
606
621
|
except (APIError, AuthenticationError) as e:
|
|
607
622
|
pytest.fail(f"API error during user stats retrieval: {e}")
|
|
@@ -610,21 +625,21 @@ def test_get_current_user_stats(karakeep_client: KarakeepAPI):
|
|
|
610
625
|
|
|
611
626
|
# --- Add CLI call ---
|
|
612
627
|
try:
|
|
613
|
-
|
|
628
|
+
logger.info("\n Running CLI equivalent: get-current-user-stats")
|
|
614
629
|
# Assumes KARAKEEP_PYTHON_API_BASE_URL and KARAKEEP_PYTHON_API_KEY are set in env
|
|
615
630
|
subprocess.run(
|
|
616
631
|
"python -m karakeep_python_api get-current-user-stats",
|
|
617
632
|
shell=True,
|
|
618
633
|
check=True,
|
|
619
|
-
capture_output=True, # Capture output to avoid
|
|
634
|
+
capture_output=True, # Capture output to avoid logger.infoing it during tests unless verbose
|
|
620
635
|
text=True,
|
|
621
636
|
)
|
|
622
|
-
|
|
637
|
+
logger.info("✓ CLI command executed successfully.")
|
|
623
638
|
except subprocess.CalledProcessError as e:
|
|
624
|
-
|
|
625
|
-
#
|
|
626
|
-
|
|
627
|
-
|
|
639
|
+
logger.info(f" CLI command failed with exit code {e.returncode}")
|
|
640
|
+
# logger.info stdout/stderr only if the command failed to aid debugging
|
|
641
|
+
logger.info(f" Stdout: {e.stdout}")
|
|
642
|
+
logger.info(f" Stderr: {e.stderr}")
|
|
628
643
|
pytest.fail(f"CLI command 'get-current-user-stats' failed: {e}")
|
|
629
644
|
except Exception as e:
|
|
630
645
|
pytest.fail(f"An unexpected error occurred running the CLI command: {e}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/requires.txt
RENAMED
|
File without changes
|
{karakeep_python_api-0.1.2 → karakeep_python_api-0.1.4}/karakeep_python_api.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|