wp_python 0.1.1__tar.gz → 0.1.2__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 (55) hide show
  1. {wp_python-0.1.1 → wp_python-0.1.2}/.gitignore +1 -0
  2. wp_python-0.1.2/LICENSE +19 -0
  3. {wp_python-0.1.1 → wp_python-0.1.2}/PKG-INFO +6 -1
  4. {wp_python-0.1.1 → wp_python-0.1.2}/pyproject.toml +17 -2
  5. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/__init__.py +4 -4
  6. wp_python-0.1.1/README.md +0 -81
  7. wp_python-0.1.1/data.db +0 -0
  8. wp_python-0.1.1/pytest.ini +0 -6
  9. wp_python-0.1.1/tests/__init__.py +0 -1
  10. wp_python-0.1.1/tests/conftest.py +0 -243
  11. wp_python-0.1.1/tests/test_application_passwords.py +0 -167
  12. wp_python-0.1.1/tests/test_auth.py +0 -146
  13. wp_python-0.1.1/tests/test_client.py +0 -120
  14. wp_python-0.1.1/tests/test_endpoints.py +0 -294
  15. wp_python-0.1.1/tests/test_exceptions.py +0 -142
  16. wp_python-0.1.1/tests/test_models.py +0 -279
  17. wp_python-0.1.1/uv.lock +0 -788
  18. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/auth.py +0 -0
  19. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/client.py +0 -0
  20. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/__init__.py +0 -0
  21. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/application_passwords.py +0 -0
  22. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/autosaves.py +0 -0
  23. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/base.py +0 -0
  24. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/blocks.py +0 -0
  25. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/categories.py +0 -0
  26. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/comments.py +0 -0
  27. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/media.py +0 -0
  28. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/menus.py +0 -0
  29. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/pages.py +0 -0
  30. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/plugins.py +0 -0
  31. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/post_types.py +0 -0
  32. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/posts.py +0 -0
  33. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/revisions.py +0 -0
  34. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/search.py +0 -0
  35. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/settings.py +0 -0
  36. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/statuses.py +0 -0
  37. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/tags.py +0 -0
  38. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/taxonomies.py +0 -0
  39. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/themes.py +0 -0
  40. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/endpoints/users.py +0 -0
  41. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/exceptions.py +0 -0
  42. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/__init__.py +0 -0
  43. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/base.py +0 -0
  44. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/category.py +0 -0
  45. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/comment.py +0 -0
  46. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/media.py +0 -0
  47. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/menu.py +0 -0
  48. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/page.py +0 -0
  49. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/plugin.py +0 -0
  50. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/post.py +0 -0
  51. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/settings.py +0 -0
  52. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/tag.py +0 -0
  53. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/taxonomy.py +0 -0
  54. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/theme.py +0 -0
  55. {wp_python-0.1.1/src/wordpress_api → wp_python-0.1.2/wp_python}/models/user.py +0 -0
@@ -23,3 +23,4 @@ wheels/
23
23
  env/
24
24
  venv/
25
25
  .cache/
26
+ *.db
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -1,7 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wp_python
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A Python client for interacting with the WordPress REST API.
5
+ Author-email: Andrew <andrew.neher1@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
5
10
  Requires-Python: >=3.12
6
11
  Requires-Dist: hatch>=1.16.3
7
12
  Requires-Dist: httpx>=0.28.1
@@ -1,8 +1,17 @@
1
1
  [project]
2
2
  name = "wp_python"
3
- version = "0.1.1"
3
+ dynamic = ["version"]
4
4
  description = "A Python client for interacting with the WordPress REST API."
5
+ authors = [
6
+ { name = "Andrew", email = "andrew.neher1@gmail.com"}
7
+ ]
5
8
  requires-python = ">=3.12"
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Operating System :: OS Independent",
12
+ ]
13
+ license = "MIT"
14
+ license-files = ["LICEN[CS]E*"]
6
15
  dependencies = [
7
16
  "hatch>=1.16.3",
8
17
  "httpx>=0.28.1",
@@ -17,7 +26,13 @@ requires = ["hatchling"]
17
26
  build-backend = "hatchling.build"
18
27
 
19
28
  [tool.hatch.build.targets.wheel]
20
- packages = ["src"]
29
+ packages = ["src/wp_python"]
30
+
31
+ [tool.hatch.build.targets.sdist]
32
+ packages = ["src/wp_python"]
33
+
34
+ [tool.hatch.version]
35
+ path = "src/wp_python/__init__.py"
21
36
 
22
37
  [tool.pytest.ini_options]
23
38
  testpaths = ["tests"]
@@ -4,18 +4,18 @@ WordPress REST API Python Client
4
4
  A full-featured Python 3.12 interface to the WordPress REST API.
5
5
  """
6
6
 
7
- from .client import WordPressClient
8
7
  from .auth import ApplicationPasswordAuth, BasicAuth, JWTAuth, OAuth2Auth
8
+ from .client import WordPressClient
9
9
  from .exceptions import (
10
- WordPressError,
11
10
  AuthenticationError,
12
11
  NotFoundError,
13
- ValidationError,
14
12
  RateLimitError,
15
13
  ServerError,
14
+ ValidationError,
15
+ WordPressError,
16
16
  )
17
17
 
18
- __version__ = "1.0.0"
18
+ __version__ = "0.1.2"
19
19
  __all__ = [
20
20
  "WordPressClient",
21
21
  "ApplicationPasswordAuth",
wp_python-0.1.1/README.md DELETED
@@ -1,81 +0,0 @@
1
- # WordPress REST API Python Client
2
-
3
- A full-featured Python 3.12 interface to the WordPress REST API.
4
-
5
- ## Overview
6
-
7
- This library provides a comprehensive, type-safe Python client for interacting with WordPress sites via the REST API. It supports all major WordPress endpoints including posts, pages, media, users, comments, categories, tags, settings, plugins, themes, menus, and more.
8
-
9
- ## Project Structure
10
-
11
- ```
12
- src/wordpress_api/
13
- ├── __init__.py # Main exports
14
- ├── client.py # WordPressClient main class
15
- ├── auth.py # Authentication handlers
16
- ├── exceptions.py # Custom exceptions
17
- ├── models/ # Pydantic data models
18
- │ ├── post.py
19
- │ ├── page.py
20
- │ ├── media.py
21
- │ ├── user.py
22
- │ ├── comment.py
23
- │ ├── category.py
24
- │ ├── tag.py
25
- │ └── ...
26
- └── endpoints/ # API endpoint classes
27
- ├── posts.py
28
- ├── pages.py
29
- ├── media.py
30
- ├── users.py
31
- ├── comments.py
32
- └── ...
33
- ```
34
-
35
- ## Quick Start
36
-
37
- ```python
38
- from wordpress_api import WordPressClient, ApplicationPasswordAuth
39
-
40
- # Authenticate with WordPress Application Passwords
41
- auth = ApplicationPasswordAuth("username", "xxxx xxxx xxxx xxxx")
42
- client = WordPressClient("https://your-site.com", auth=auth)
43
-
44
- # List posts
45
- posts = client.posts.list()
46
-
47
- # Create a post
48
- from wordpress_api.models import PostCreate, PostStatus
49
- post = client.posts.create(PostCreate(
50
- title="Hello World",
51
- content="<p>My first post!</p>",
52
- status=PostStatus.PUBLISH
53
- ))
54
-
55
- # Get current user
56
- me = client.users.me()
57
- ```
58
-
59
- ## Features
60
-
61
- - **Full CRUD Support**: Posts, Pages, Media, Users, Comments, Categories, Tags
62
- - **Authentication**: Application Passwords, Basic Auth, JWT
63
- - **Type Safety**: Pydantic models for all WordPress objects
64
- - **Pagination**: Automatic iteration through all results
65
- - **Media Upload**: Upload files from path or file objects
66
- - **Custom Post Types**: Access any registered custom post type
67
- - **Block Editor**: Access block types and patterns
68
- - **Error Handling**: Typed exceptions for all API errors
69
-
70
- ## Dependencies
71
-
72
- - httpx: Modern HTTP client
73
- - pydantic: Data validation using Python type annotations
74
-
75
- ## Authentication Setup
76
-
77
- 1. Go to your WordPress site
78
- 2. Navigate to Users → Your Profile
79
- 3. Scroll to "Application Passwords"
80
- 4. Create a new application password
81
- 5. Use it with `ApplicationPasswordAuth`
wp_python-0.1.1/data.db DELETED
Binary file
@@ -1,6 +0,0 @@
1
- [pytest]
2
- testpaths = tests
3
- python_files = test_*.py
4
- python_classes = Test*
5
- python_functions = test_*
6
- addopts = -v --tb=short
@@ -1 +0,0 @@
1
- """WordPress API Test Suite."""
@@ -1,243 +0,0 @@
1
- """Pytest fixtures for WordPress API tests."""
2
-
3
- import sys
4
- from pathlib import Path
5
-
6
- sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
7
-
8
- import pytest
9
- import respx
10
- import httpx
11
-
12
- from wordpress_api import WordPressClient, ApplicationPasswordAuth, BasicAuth, JWTAuth
13
-
14
-
15
- @pytest.fixture
16
- def base_url():
17
- """Return the test WordPress site URL."""
18
- return "https://example.com"
19
-
20
-
21
- @pytest.fixture
22
- def api_url(base_url):
23
- """Return the full API URL."""
24
- return f"{base_url}/wp-json"
25
-
26
-
27
- @pytest.fixture
28
- def mock_auth():
29
- """Return a mock authentication handler."""
30
- return ApplicationPasswordAuth("testuser", "xxxx xxxx xxxx xxxx")
31
-
32
-
33
- @pytest.fixture
34
- def client(base_url, mock_auth):
35
- """Return a WordPress client for testing."""
36
- return WordPressClient(base_url, auth=mock_auth)
37
-
38
-
39
- @pytest.fixture
40
- def unauthenticated_client(base_url):
41
- """Return a WordPress client without authentication."""
42
- return WordPressClient(base_url)
43
-
44
-
45
- @pytest.fixture
46
- def mock_post():
47
- """Return mock post data."""
48
- return {
49
- "id": 1,
50
- "date": "2024-01-15T10:30:00",
51
- "date_gmt": "2024-01-15T10:30:00",
52
- "guid": {"rendered": "https://example.com/?p=1"},
53
- "modified": "2024-01-15T10:30:00",
54
- "modified_gmt": "2024-01-15T10:30:00",
55
- "slug": "hello-world",
56
- "status": "publish",
57
- "type": "post",
58
- "link": "https://example.com/hello-world/",
59
- "title": {"rendered": "Hello World"},
60
- "content": {"rendered": "<p>Hello World content</p>"},
61
- "excerpt": {"rendered": "<p>Hello World excerpt</p>"},
62
- "author": 1,
63
- "featured_media": 0,
64
- "comment_status": "open",
65
- "ping_status": "open",
66
- "sticky": False,
67
- "template": "",
68
- "format": "standard",
69
- "meta": {},
70
- "categories": [1],
71
- "tags": [],
72
- }
73
-
74
-
75
- @pytest.fixture
76
- def mock_page():
77
- """Return mock page data."""
78
- return {
79
- "id": 10,
80
- "date": "2024-01-15T10:30:00",
81
- "date_gmt": "2024-01-15T10:30:00",
82
- "guid": {"rendered": "https://example.com/?page_id=10"},
83
- "modified": "2024-01-15T10:30:00",
84
- "modified_gmt": "2024-01-15T10:30:00",
85
- "slug": "about",
86
- "status": "publish",
87
- "type": "page",
88
- "link": "https://example.com/about/",
89
- "title": {"rendered": "About Us"},
90
- "content": {"rendered": "<p>About us content</p>"},
91
- "excerpt": {"rendered": ""},
92
- "author": 1,
93
- "featured_media": 0,
94
- "parent": 0,
95
- "menu_order": 0,
96
- "comment_status": "closed",
97
- "ping_status": "closed",
98
- "template": "",
99
- "meta": {},
100
- }
101
-
102
-
103
- @pytest.fixture
104
- def mock_user():
105
- """Return mock user data."""
106
- return {
107
- "id": 1,
108
- "username": "admin",
109
- "name": "Admin User",
110
- "first_name": "Admin",
111
- "last_name": "User",
112
- "email": "admin@example.com",
113
- "url": "https://example.com",
114
- "description": "Site administrator",
115
- "link": "https://example.com/author/admin/",
116
- "locale": "en_US",
117
- "nickname": "admin",
118
- "slug": "admin",
119
- "registered_date": "2024-01-01T00:00:00",
120
- "roles": ["administrator"],
121
- "capabilities": {"administrator": True},
122
- "extra_capabilities": {},
123
- "avatar_urls": {"24": "https://example.com/avatar.jpg"},
124
- "meta": {},
125
- }
126
-
127
-
128
- @pytest.fixture
129
- def mock_category():
130
- """Return mock category data."""
131
- return {
132
- "id": 1,
133
- "count": 5,
134
- "description": "Default category",
135
- "link": "https://example.com/category/uncategorized/",
136
- "name": "Uncategorized",
137
- "slug": "uncategorized",
138
- "taxonomy": "category",
139
- "parent": 0,
140
- "meta": {},
141
- }
142
-
143
-
144
- @pytest.fixture
145
- def mock_tag():
146
- """Return mock tag data."""
147
- return {
148
- "id": 1,
149
- "count": 3,
150
- "description": "",
151
- "link": "https://example.com/tag/python/",
152
- "name": "Python",
153
- "slug": "python",
154
- "taxonomy": "post_tag",
155
- "meta": {},
156
- }
157
-
158
-
159
- @pytest.fixture
160
- def mock_comment():
161
- """Return mock comment data."""
162
- return {
163
- "id": 1,
164
- "post": 1,
165
- "parent": 0,
166
- "author": 0,
167
- "author_name": "John Doe",
168
- "author_email": "john@example.com",
169
- "author_url": "",
170
- "author_ip": "127.0.0.1",
171
- "author_user_agent": "Mozilla/5.0",
172
- "date": "2024-01-15T10:30:00",
173
- "date_gmt": "2024-01-15T10:30:00",
174
- "content": {"rendered": "<p>Great post!</p>"},
175
- "link": "https://example.com/hello-world/#comment-1",
176
- "status": "approved",
177
- "type": "comment",
178
- "author_avatar_urls": {},
179
- "meta": {},
180
- }
181
-
182
-
183
- @pytest.fixture
184
- def mock_media():
185
- """Return mock media data."""
186
- return {
187
- "id": 100,
188
- "date": "2024-01-15T10:30:00",
189
- "date_gmt": "2024-01-15T10:30:00",
190
- "guid": {"rendered": "https://example.com/wp-content/uploads/image.jpg"},
191
- "modified": "2024-01-15T10:30:00",
192
- "modified_gmt": "2024-01-15T10:30:00",
193
- "slug": "image",
194
- "status": "inherit",
195
- "type": "attachment",
196
- "link": "https://example.com/image/",
197
- "title": {"rendered": "Test Image"},
198
- "author": 1,
199
- "comment_status": "open",
200
- "ping_status": "closed",
201
- "template": "",
202
- "meta": {},
203
- "description": {"rendered": ""},
204
- "caption": {"rendered": ""},
205
- "alt_text": "Test image alt",
206
- "media_type": "image",
207
- "mime_type": "image/jpeg",
208
- "media_details": {
209
- "width": 1920,
210
- "height": 1080,
211
- "file": "2024/01/image.jpg",
212
- "filesize": 102400,
213
- "sizes": {},
214
- "image_meta": {},
215
- },
216
- "post": None,
217
- "source_url": "https://example.com/wp-content/uploads/image.jpg",
218
- }
219
-
220
-
221
- @pytest.fixture
222
- def mock_application_password():
223
- """Return mock application password data."""
224
- return {
225
- "uuid": "12345678-1234-1234-1234-123456789012",
226
- "app_id": "",
227
- "name": "Test App",
228
- "created": "2024-01-15T10:30:00",
229
- "last_used": None,
230
- "last_ip": None,
231
- }
232
-
233
-
234
- @pytest.fixture
235
- def mock_application_password_created():
236
- """Return mock created application password data (includes password)."""
237
- return {
238
- "uuid": "12345678-1234-1234-1234-123456789012",
239
- "app_id": "",
240
- "name": "Test App",
241
- "created": "2024-01-15T10:30:00",
242
- "password": "abcd efgh ijkl mnop",
243
- }
@@ -1,167 +0,0 @@
1
- """Tests for Application Passwords endpoint."""
2
-
3
- import pytest
4
- import respx
5
- import httpx
6
-
7
- from wordpress_api import WordPressClient
8
-
9
-
10
- class TestApplicationPasswordsEndpoint:
11
- """Tests for ApplicationPasswordsEndpoint."""
12
-
13
- @respx.mock
14
- def test_list_passwords(self, client, api_url, mock_application_password):
15
- """Test listing application passwords."""
16
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
17
- return_value=httpx.Response(200, json=[mock_application_password])
18
- )
19
-
20
- passwords = client.application_passwords.list()
21
- assert len(passwords) == 1
22
- assert passwords[0].name == "Test App"
23
-
24
- @respx.mock
25
- def test_list_passwords_for_user(self, client, api_url, mock_application_password):
26
- """Test listing application passwords for specific user."""
27
- respx.get(f"{api_url}/wp/v2/users/1/application-passwords").mock(
28
- return_value=httpx.Response(200, json=[mock_application_password])
29
- )
30
-
31
- passwords = client.application_passwords.list(user_id=1)
32
- assert len(passwords) == 1
33
-
34
- @respx.mock
35
- def test_get_password(self, client, api_url, mock_application_password):
36
- """Test getting a specific application password."""
37
- uuid = mock_application_password["uuid"]
38
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords/{uuid}").mock(
39
- return_value=httpx.Response(200, json=mock_application_password)
40
- )
41
-
42
- password = client.application_passwords.get(uuid)
43
- assert password.uuid == uuid
44
- assert password.name == "Test App"
45
-
46
- @respx.mock
47
- def test_create_password(self, client, api_url, mock_application_password_created):
48
- """Test creating an application password."""
49
- respx.post(f"{api_url}/wp/v2/users/me/application-passwords").mock(
50
- return_value=httpx.Response(201, json=mock_application_password_created)
51
- )
52
-
53
- result = client.application_passwords.create("Test App")
54
- assert result.name == "Test App"
55
- assert result.password == "abcd efgh ijkl mnop"
56
-
57
- @respx.mock
58
- def test_update_password(self, client, api_url, mock_application_password):
59
- """Test updating an application password."""
60
- uuid = mock_application_password["uuid"]
61
- updated = {**mock_application_password, "name": "Renamed App"}
62
- respx.post(f"{api_url}/wp/v2/users/me/application-passwords/{uuid}").mock(
63
- return_value=httpx.Response(200, json=updated)
64
- )
65
-
66
- result = client.application_passwords.update(uuid, "Renamed App")
67
- assert result.name == "Renamed App"
68
-
69
- @respx.mock
70
- def test_delete_password(self, client, api_url, mock_application_password):
71
- """Test deleting an application password."""
72
- uuid = mock_application_password["uuid"]
73
- respx.delete(f"{api_url}/wp/v2/users/me/application-passwords/{uuid}").mock(
74
- return_value=httpx.Response(200, json={"deleted": True})
75
- )
76
-
77
- result = client.application_passwords.delete(uuid)
78
- assert result["deleted"] is True
79
-
80
- @respx.mock
81
- def test_delete_all_passwords(self, client, api_url):
82
- """Test deleting all application passwords."""
83
- respx.delete(f"{api_url}/wp/v2/users/me/application-passwords").mock(
84
- return_value=httpx.Response(200, json={"deleted": 3})
85
- )
86
-
87
- result = client.application_passwords.delete_all()
88
- assert result["deleted"] == 3
89
-
90
- @respx.mock
91
- def test_get_or_create_existing(self, client, api_url, mock_application_password):
92
- """Test get_or_create returns existing password."""
93
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
94
- return_value=httpx.Response(200, json=[mock_application_password])
95
- )
96
-
97
- result = client.application_passwords.get_or_create("Test App")
98
- assert result.name == "Test App"
99
-
100
- @respx.mock
101
- def test_get_or_create_new(self, client, api_url, mock_application_password_created):
102
- """Test get_or_create creates new password when none exists."""
103
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
104
- return_value=httpx.Response(200, json=[])
105
- )
106
- respx.post(f"{api_url}/wp/v2/users/me/application-passwords").mock(
107
- return_value=httpx.Response(201, json=mock_application_password_created)
108
- )
109
-
110
- result = client.application_passwords.get_or_create("Test App")
111
- assert hasattr(result, "password")
112
- assert result.password == "abcd efgh ijkl mnop"
113
-
114
- @respx.mock
115
- def test_ensure_exists_returns_existing(self, client, api_url, mock_application_password):
116
- """Test ensure_exists returns existing with was_created=False."""
117
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
118
- return_value=httpx.Response(200, json=[mock_application_password])
119
- )
120
-
121
- result, was_created = client.application_passwords.ensure_exists("Test App")
122
- assert result.name == "Test App"
123
- assert was_created is False
124
-
125
- @respx.mock
126
- def test_ensure_exists_creates_new(self, client, api_url, mock_application_password_created):
127
- """Test ensure_exists creates new with was_created=True."""
128
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
129
- return_value=httpx.Response(200, json=[])
130
- )
131
- respx.post(f"{api_url}/wp/v2/users/me/application-passwords").mock(
132
- return_value=httpx.Response(201, json=mock_application_password_created)
133
- )
134
-
135
- result, was_created = client.application_passwords.ensure_exists("Test App")
136
- assert was_created is True
137
- assert result.password == "abcd efgh ijkl mnop"
138
-
139
- @respx.mock
140
- def test_has_any_true(self, client, api_url, mock_application_password):
141
- """Test has_any returns True when passwords exist."""
142
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
143
- return_value=httpx.Response(200, json=[mock_application_password])
144
- )
145
-
146
- assert client.application_passwords.has_any() is True
147
-
148
- @respx.mock
149
- def test_has_any_false(self, client, api_url):
150
- """Test has_any returns False when no passwords exist."""
151
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
152
- return_value=httpx.Response(200, json=[])
153
- )
154
-
155
- assert client.application_passwords.has_any() is False
156
-
157
- @respx.mock
158
- def test_count(self, client, api_url, mock_application_password):
159
- """Test count returns correct number."""
160
- respx.get(f"{api_url}/wp/v2/users/me/application-passwords").mock(
161
- return_value=httpx.Response(200, json=[
162
- mock_application_password,
163
- {**mock_application_password, "uuid": "different-uuid", "name": "Another App"},
164
- ])
165
- )
166
-
167
- assert client.application_passwords.count() == 2