diaspora-event-sdk 0.4.1__py3-none-any.whl → 0.4.3__py3-none-any.whl
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.
- diaspora_event_sdk/__init__.py +5 -2
- diaspora_event_sdk/sdk/_environments.py +0 -2
- diaspora_event_sdk/sdk/client.py +44 -175
- diaspora_event_sdk/sdk/kafka_client.py +111 -67
- diaspora_event_sdk/sdk/login_manager/manager.py +60 -1
- diaspora_event_sdk/sdk/web_client.py +29 -114
- diaspora_event_sdk/version.py +1 -1
- {diaspora_event_sdk-0.4.1.dist-info → diaspora_event_sdk-0.4.3.dist-info}/METADATA +12 -3
- {diaspora_event_sdk-0.4.1.dist-info → diaspora_event_sdk-0.4.3.dist-info}/RECORD +13 -14
- {diaspora_event_sdk-0.4.1.dist-info → diaspora_event_sdk-0.4.3.dist-info}/WHEEL +1 -1
- tests/unit/apis_test.py +267 -114
- tests/unit/client_test.py +0 -100
- {diaspora_event_sdk-0.4.1.dist-info → diaspora_event_sdk-0.4.3.dist-info/licenses}/LICENSE +0 -0
- {diaspora_event_sdk-0.4.1.dist-info → diaspora_event_sdk-0.4.3.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import json
|
|
2
1
|
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import globus_sdk
|
|
@@ -25,131 +24,47 @@ class WebClient(globus_sdk.BaseClient):
|
|
|
25
24
|
self._user_app_name = None
|
|
26
25
|
self.user_app_name = app_name
|
|
27
26
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def list_topics(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
32
|
-
return self.get("/api/v2/topics", headers={"Subject": str(subject)})
|
|
33
|
-
|
|
34
|
-
def register_topic(
|
|
35
|
-
self, subject: UUID_LIKE_T, topic: str, action: str
|
|
36
|
-
) -> globus_sdk.GlobusHTTPResponse:
|
|
37
|
-
return self.put(
|
|
38
|
-
f"/api/v2/topic/{topic}",
|
|
39
|
-
headers={"Subject": str(subject), "Action": action},
|
|
40
|
-
)
|
|
27
|
+
def create_user(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
28
|
+
"""Call the v3 create_user endpoint (POST /api/v3/user)."""
|
|
29
|
+
return self.post("/api/v3/user", headers={"Subject": str(subject)})
|
|
41
30
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return self.get(
|
|
46
|
-
f"/api/v2/topic/{topic}", headers={"Subject": str(subject), "Topic": topic}
|
|
47
|
-
)
|
|
31
|
+
def delete_user(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
32
|
+
"""Call the v3 delete_user endpoint (DELETE /api/v3/user)."""
|
|
33
|
+
return self.delete("/api/v3/user", headers={"Subject": str(subject)})
|
|
48
34
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return self.post(
|
|
53
|
-
f"/api/v2/topic/{topic}",
|
|
54
|
-
headers={
|
|
55
|
-
"Subject": str(subject),
|
|
56
|
-
"Topic": topic,
|
|
57
|
-
"Content-Type": "text/plain",
|
|
58
|
-
},
|
|
59
|
-
data=json.dumps(configs).encode("utf-8"),
|
|
60
|
-
)
|
|
35
|
+
def create_key(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
36
|
+
"""Call the v3 create_key endpoint (POST /api/v3/key)."""
|
|
37
|
+
return self.post("/api/v3/key", headers={"Subject": str(subject)})
|
|
61
38
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return self.post(
|
|
66
|
-
f"/api/v2/topic/{topic}/partitions",
|
|
67
|
-
headers={
|
|
68
|
-
"Subject": str(subject),
|
|
69
|
-
"Topic": topic,
|
|
70
|
-
"NewPartitions": str(new_partitions),
|
|
71
|
-
},
|
|
72
|
-
)
|
|
39
|
+
def delete_key(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
40
|
+
"""Call the v3 delete_key endpoint (DELETE /api/v3/key)."""
|
|
41
|
+
return self.delete("/api/v3/key", headers={"Subject": str(subject)})
|
|
73
42
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return self.post(
|
|
78
|
-
f"/api/v2/topic/{topic}/reset",
|
|
79
|
-
headers={"Subject": str(subject), "Topic": topic},
|
|
80
|
-
)
|
|
43
|
+
def list_namespaces(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
44
|
+
"""Call the v3 list_namespaces endpoint (GET /api/v3/namespace)."""
|
|
45
|
+
return self.get("/api/v3/namespace", headers={"Subject": str(subject)})
|
|
81
46
|
|
|
82
|
-
def
|
|
83
|
-
self, subject: UUID_LIKE_T,
|
|
47
|
+
def create_topic(
|
|
48
|
+
self, subject: UUID_LIKE_T, namespace: str, topic: str
|
|
84
49
|
) -> globus_sdk.GlobusHTTPResponse:
|
|
50
|
+
"""Call the v3 create_topic endpoint (POST /api/v3/{namespace}/{topic})."""
|
|
85
51
|
return self.post(
|
|
86
|
-
f"/api/
|
|
87
|
-
headers={
|
|
88
|
-
"Subject": str(subject),
|
|
89
|
-
"Action": action,
|
|
90
|
-
"Topic": topic,
|
|
91
|
-
"User": str(user),
|
|
92
|
-
},
|
|
52
|
+
f"/api/v3/{namespace}/{topic}", headers={"Subject": str(subject)}
|
|
93
53
|
)
|
|
94
54
|
|
|
95
|
-
def
|
|
96
|
-
self, subject: UUID_LIKE_T, topic: str
|
|
55
|
+
def delete_topic(
|
|
56
|
+
self, subject: UUID_LIKE_T, namespace: str, topic: str
|
|
97
57
|
) -> globus_sdk.GlobusHTTPResponse:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
headers={"Subject": str(subject)
|
|
58
|
+
"""Call the v3 delete_topic endpoint (DELETE /api/v3/{namespace}/{topic})."""
|
|
59
|
+
return self.delete(
|
|
60
|
+
f"/api/v3/{namespace}/{topic}", headers={"Subject": str(subject)}
|
|
101
61
|
)
|
|
102
62
|
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def create_trigger(
|
|
107
|
-
self,
|
|
108
|
-
subject: UUID_LIKE_T,
|
|
109
|
-
topic: str,
|
|
110
|
-
function: str,
|
|
111
|
-
action: str,
|
|
112
|
-
function_configs: dict,
|
|
113
|
-
trigger_configs: dict,
|
|
63
|
+
def recreate_topic(
|
|
64
|
+
self, subject: UUID_LIKE_T, namespace: str, topic: str
|
|
114
65
|
) -> globus_sdk.GlobusHTTPResponse:
|
|
66
|
+
"""Call the v3 recreate_topic endpoint (PUT /api/v3/{namespace}/{topic}/recreate)."""
|
|
115
67
|
return self.put(
|
|
116
|
-
"/api/
|
|
117
|
-
headers={
|
|
118
|
-
"Subject": str(subject),
|
|
119
|
-
"Topic": topic,
|
|
120
|
-
"Trigger": function,
|
|
121
|
-
"Action": action,
|
|
122
|
-
"Content-Type": "text/plain",
|
|
123
|
-
},
|
|
124
|
-
data=json.dumps(
|
|
125
|
-
{"function": function_configs, "trigger": trigger_configs}
|
|
126
|
-
).encode("utf-8"),
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
def update_trigger(
|
|
130
|
-
self, subject: UUID_LIKE_T, trigger_uuid: UUID_LIKE_T, trigger_configs: dict
|
|
131
|
-
) -> globus_sdk.GlobusHTTPResponse:
|
|
132
|
-
return self.post(
|
|
133
|
-
f"/api/v2/triggers/{trigger_uuid}",
|
|
134
|
-
headers={
|
|
135
|
-
"Subject": str(subject),
|
|
136
|
-
"Trigger_id": str(trigger_uuid),
|
|
137
|
-
"Content-Type": "text/plain",
|
|
138
|
-
},
|
|
139
|
-
data=json.dumps(trigger_configs).encode("utf-8"),
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
def list_log_streams(
|
|
143
|
-
self, subject: UUID_LIKE_T, trigger: str
|
|
144
|
-
) -> globus_sdk.GlobusHTTPResponse:
|
|
145
|
-
return self.get(
|
|
146
|
-
"/api/v2/logs", headers={"Subject": str(subject), "Trigger": trigger}
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
def get_log_events(
|
|
150
|
-
self, subject: UUID_LIKE_T, trigger: str, stream: str
|
|
151
|
-
) -> globus_sdk.GlobusHTTPResponse:
|
|
152
|
-
return self.get(
|
|
153
|
-
"/api/v2/log",
|
|
154
|
-
headers={"Subject": str(subject), "Trigger": trigger, "Stream": stream},
|
|
68
|
+
f"/api/v3/{namespace}/{topic}/recreate",
|
|
69
|
+
headers={"Subject": str(subject)},
|
|
155
70
|
)
|
diaspora_event_sdk/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.4.
|
|
1
|
+
__version__ = "0.4.3"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: diaspora-event-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Diaspora Event Fabric SDK
|
|
5
5
|
Home-page: https://github.com/globus-labs/diaspora-event-sdk
|
|
6
6
|
License: Apache 2.0
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: globus-sdk<4,>=3.
|
|
19
|
+
Requires-Dist: globus-sdk<4,>=3.59.0
|
|
20
20
|
Provides-Extra: kafka-python
|
|
21
21
|
Requires-Dist: kafka-python; extra == "kafka-python"
|
|
22
22
|
Provides-Extra: test
|
|
@@ -27,6 +27,15 @@ Requires-Dist: mypy; extra == "test"
|
|
|
27
27
|
Requires-Dist: tox; extra == "test"
|
|
28
28
|
Requires-Dist: check-manifest; extra == "test"
|
|
29
29
|
Requires-Dist: pre-commit; extra == "test"
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: description-content-type
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: provides-extra
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: summary
|
|
30
39
|
|
|
31
40
|
# Diaspora Event Fabric SDK
|
|
32
41
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
diaspora_event_sdk/__init__.py,sha256=
|
|
2
|
-
diaspora_event_sdk/version.py,sha256=
|
|
1
|
+
diaspora_event_sdk/__init__.py,sha256=Tayc-lBVFUjkbs-Qvxg_J6vBM0Mhl4yhef53cdkUoPI,426
|
|
2
|
+
diaspora_event_sdk/version.py,sha256=Nyg0pmk5ea9-SLCAFEIF96ByFx4-TJFtrqYPN-Zn6g4,22
|
|
3
3
|
diaspora_event_sdk/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
diaspora_event_sdk/sdk/_environments.py,sha256=
|
|
4
|
+
diaspora_event_sdk/sdk/_environments.py,sha256=zuvyoMtGsUA3J0GI92NBn-z_1J4GbiE7WaUgKe8-dpI,501
|
|
5
5
|
diaspora_event_sdk/sdk/aws_iam_msk.py,sha256=9fPH7lgdfZhxKohdZE7shGPYIA4-X-XRbA5KdY-Agjo,3933
|
|
6
|
-
diaspora_event_sdk/sdk/client.py,sha256=
|
|
6
|
+
diaspora_event_sdk/sdk/client.py,sha256=RMSWVTsW6JQr9L2rp5LktMw5oztpRJkrIQk5GFo8Ses,4277
|
|
7
7
|
diaspora_event_sdk/sdk/decorators.py,sha256=Gel8AyhIjbf4-FNintTNcOqvC9hHH_YwbOH257Nfmf0,884
|
|
8
|
-
diaspora_event_sdk/sdk/kafka_client.py,sha256=
|
|
9
|
-
diaspora_event_sdk/sdk/web_client.py,sha256=
|
|
8
|
+
diaspora_event_sdk/sdk/kafka_client.py,sha256=WSYkyGRmTtPtLQiPe-Slc3Zq2QqohVVX1d_qJu7h6ZE,6124
|
|
9
|
+
diaspora_event_sdk/sdk/web_client.py,sha256=VtEk4luVw7afc0vhtGhG-qd4vWvuBGLA6vkU_qriuoA,2791
|
|
10
10
|
diaspora_event_sdk/sdk/botocore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
diaspora_event_sdk/sdk/botocore/auth.py,sha256=QXwCNMzM0wAqD64RlFd-K27atRpixti73VKConpb1kk,18699
|
|
12
12
|
diaspora_event_sdk/sdk/botocore/awsrequest.py,sha256=fcR28YxbsCk2ZT1rGtgWxhje-sYt-SxIqixjOWAFLcM,9344
|
|
@@ -19,16 +19,15 @@ diaspora_event_sdk/sdk/login_manager/client_login.py,sha256=e8o6Tf40AW61auHNhQ_5
|
|
|
19
19
|
diaspora_event_sdk/sdk/login_manager/decorators.py,sha256=JlCEwoceSMmFy6PLQ5kz5mpkKfitJgCVAA6I4I_k1nw,1013
|
|
20
20
|
diaspora_event_sdk/sdk/login_manager/globus_auth.py,sha256=pbDy67Mdf4SKFf0IlXD3ZFGj4lG-xpzFTD1ygW-0H1k,430
|
|
21
21
|
diaspora_event_sdk/sdk/login_manager/login_flow.py,sha256=NjmDtr9QWCG1SIPt9TL3pxcUgLfz2qzzWVHBBMJWvE0,978
|
|
22
|
-
diaspora_event_sdk/sdk/login_manager/manager.py,sha256=
|
|
22
|
+
diaspora_event_sdk/sdk/login_manager/manager.py,sha256=NU5uzYE8UTiQjMLhpO7Mrr3vbUZy5DYQ3u60wUEHZVc,9335
|
|
23
23
|
diaspora_event_sdk/sdk/login_manager/protocol.py,sha256=ipAOUi7GYF5YfU-az1LWUbU_rNXHarDwUkKn6TUyjVY,678
|
|
24
24
|
diaspora_event_sdk/sdk/login_manager/tokenstore.py,sha256=ImncC8EIxoAuGtDiZIwdtUgOD2fWo8oBP22G-fiZ5L4,2036
|
|
25
25
|
diaspora_event_sdk/sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
diaspora_event_sdk/sdk/utils/uuid_like.py,sha256=xbxf0YXpDhdii16lwPLWRN21qFekHrNrqODSToMPtCg,470
|
|
27
|
+
diaspora_event_sdk-0.4.3.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
27
28
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
tests/unit/apis_test.py,sha256=
|
|
29
|
-
|
|
30
|
-
diaspora_event_sdk-0.4.
|
|
31
|
-
diaspora_event_sdk-0.4.
|
|
32
|
-
diaspora_event_sdk-0.4.
|
|
33
|
-
diaspora_event_sdk-0.4.1.dist-info/top_level.txt,sha256=OVun-67t3fkLFEIwvJuNINgFFvAc--bClYhXjLhMmvs,25
|
|
34
|
-
diaspora_event_sdk-0.4.1.dist-info/RECORD,,
|
|
29
|
+
tests/unit/apis_test.py,sha256=x9Pd8Ss2COtXk7nKez4wHw0-Xp7GDeJT9bIWXJ1K5GQ,9140
|
|
30
|
+
diaspora_event_sdk-0.4.3.dist-info/METADATA,sha256=xbh2IbvzvA9GzImKof6slxSVw2E0OGe524Tcf1hEbI8,3828
|
|
31
|
+
diaspora_event_sdk-0.4.3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
32
|
+
diaspora_event_sdk-0.4.3.dist-info/top_level.txt,sha256=OVun-67t3fkLFEIwvJuNINgFFvAc--bClYhXjLhMmvs,25
|
|
33
|
+
diaspora_event_sdk-0.4.3.dist-info/RECORD,,
|
tests/unit/apis_test.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
import os
|
|
3
3
|
import logging
|
|
4
|
-
|
|
4
|
+
import uuid
|
|
5
5
|
from diaspora_event_sdk import Client
|
|
6
|
-
from diaspora_event_sdk.sdk.login_manager import tokenstore
|
|
7
6
|
|
|
8
7
|
# Configure module-level logger
|
|
9
8
|
logging.basicConfig(
|
|
@@ -14,123 +13,277 @@ logger = logging.getLogger(__name__)
|
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
@pytest.fixture(scope="module")
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
def client():
|
|
17
|
+
"""Create a Client instance for integration tests.
|
|
18
|
+
|
|
19
|
+
Requires environment variables:
|
|
20
|
+
- DIASPORA_SDK_CLIENT_ID
|
|
21
|
+
- DIASPORA_SDK_CLIENT_SECRET
|
|
22
|
+
- DIASPORA_SCOPE (optional, defaults to action_all scope)
|
|
23
|
+
|
|
24
|
+
Note: CLIENT_SCOPE is used for backward compatibility but should only
|
|
25
|
+
contain scopes for a single resource server.
|
|
26
|
+
"""
|
|
27
|
+
# Ensure required environment variables are set
|
|
28
|
+
assert os.environ.get("DIASPORA_SDK_CLIENT_ID"), (
|
|
29
|
+
"DIASPORA_SDK_CLIENT_ID must be set"
|
|
28
30
|
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
assert os.environ.get("DIASPORA_SDK_CLIENT_SECRET"), (
|
|
32
|
+
"DIASPORA_SDK_CLIENT_SECRET must be set"
|
|
31
33
|
)
|
|
32
|
-
token = token_response.by_resource_server[client_id]
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
# Set DIASPORA_SCOPE if CLIENT_SCOPE is set (for backward compatibility)
|
|
36
|
+
# This ensures the LoginManager uses the correct scope for client credentials
|
|
37
|
+
if "CLIENT_SCOPE" in os.environ and "DIASPORA_SCOPE" not in os.environ:
|
|
38
|
+
os.environ["DIASPORA_SCOPE"] = os.environ["CLIENT_SCOPE"]
|
|
37
39
|
|
|
38
|
-
return
|
|
39
|
-
"client_id": client_id,
|
|
40
|
-
"client_secret": client_secret,
|
|
41
|
-
"requested_scopes": requested_scopes,
|
|
42
|
-
"token_response": token_response,
|
|
43
|
-
"token": token,
|
|
44
|
-
}
|
|
40
|
+
return Client()
|
|
45
41
|
|
|
46
42
|
|
|
47
|
-
@pytest.
|
|
48
|
-
def client
|
|
49
|
-
|
|
43
|
+
@pytest.mark.integration
|
|
44
|
+
def test_create_user(client):
|
|
45
|
+
"""Test create_user API."""
|
|
46
|
+
result = client.create_user()
|
|
47
|
+
logger.info(f"create_user result: {result}")
|
|
48
|
+
assert result["status"] == "success"
|
|
49
|
+
assert "subject" in result
|
|
50
|
+
assert "namespace" in result
|
|
51
|
+
assert result["namespace"].startswith("ns-")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.integration
|
|
55
|
+
def test_create_user_idempotent(client):
|
|
56
|
+
"""Test create_user is idempotent (can be called multiple times)."""
|
|
57
|
+
result1 = client.create_user()
|
|
58
|
+
logger.info(f"create_user first call: {result1}")
|
|
59
|
+
assert result1["status"] == "success"
|
|
60
|
+
|
|
61
|
+
result2 = client.create_user()
|
|
62
|
+
logger.info(f"create_user second call: {result2}")
|
|
63
|
+
assert result2["status"] == "success"
|
|
64
|
+
# Should return same namespace
|
|
65
|
+
assert result1["namespace"] == result2["namespace"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.integration
|
|
69
|
+
def test_create_key(client):
|
|
70
|
+
"""Test create_key API."""
|
|
71
|
+
# Ensure user exists first
|
|
72
|
+
client.create_user()
|
|
73
|
+
|
|
74
|
+
result = client.create_key()
|
|
75
|
+
logger.info(f"create_key result: {result}")
|
|
76
|
+
assert result["status"] == "success"
|
|
77
|
+
assert "access_key" in result
|
|
78
|
+
assert "secret_key" in result
|
|
79
|
+
assert "create_date" in result
|
|
80
|
+
assert "endpoint" in result
|
|
81
|
+
assert len(result["access_key"]) > 0
|
|
82
|
+
assert len(result["secret_key"]) > 0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.mark.integration
|
|
86
|
+
def test_create_key_idempotent(client):
|
|
87
|
+
"""Test create_key is idempotent (returns existing key if present)."""
|
|
88
|
+
# Ensure user exists
|
|
89
|
+
client.create_user()
|
|
90
|
+
|
|
91
|
+
result1 = client.create_key()
|
|
92
|
+
logger.info(f"create_key first call: {result1}")
|
|
93
|
+
assert result1["status"] == "success"
|
|
94
|
+
access_key1 = result1["access_key"]
|
|
95
|
+
|
|
96
|
+
result2 = client.create_key()
|
|
97
|
+
logger.info(f"create_key second call: {result2}")
|
|
98
|
+
assert result2["status"] == "success"
|
|
99
|
+
# Should return same access key (idempotent)
|
|
100
|
+
assert result2["access_key"] == access_key1
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.integration
|
|
104
|
+
def test_list_namespaces(client):
|
|
105
|
+
"""Test list_namespaces API."""
|
|
106
|
+
# Ensure user and namespace exist
|
|
107
|
+
client.create_user()
|
|
108
|
+
|
|
109
|
+
result = client.list_namespaces()
|
|
110
|
+
logger.info(f"list_namespaces result: {result}")
|
|
111
|
+
assert result["status"] == "success"
|
|
112
|
+
assert "namespaces" in result
|
|
113
|
+
assert isinstance(result["namespaces"], dict)
|
|
114
|
+
# Should include the default namespace
|
|
115
|
+
assert client.namespace in result["namespaces"]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@pytest.mark.integration
|
|
119
|
+
def test_create_topic(client):
|
|
120
|
+
"""Test create_topic API."""
|
|
121
|
+
# Ensure user exists
|
|
122
|
+
client.create_user()
|
|
123
|
+
|
|
124
|
+
topic_name = f"test-topic-{str(uuid.uuid4())[:8]}"
|
|
125
|
+
result = client.create_topic(topic_name)
|
|
126
|
+
logger.info(f"create_topic result: {result}")
|
|
127
|
+
assert result["status"] == "success"
|
|
128
|
+
assert "topics" in result
|
|
129
|
+
assert isinstance(result["topics"], list)
|
|
130
|
+
assert topic_name in result["topics"]
|
|
131
|
+
|
|
132
|
+
# Cleanup
|
|
133
|
+
client.delete_topic(topic_name)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.integration
|
|
137
|
+
def test_create_topic_idempotent(client):
|
|
138
|
+
"""Test create_topic is idempotent (can be called multiple times)."""
|
|
139
|
+
# Ensure user exists
|
|
140
|
+
client.create_user()
|
|
141
|
+
|
|
142
|
+
topic_name = f"test-topic-{str(uuid.uuid4())[:8]}"
|
|
143
|
+
result1 = client.create_topic(topic_name)
|
|
144
|
+
logger.info(f"create_topic first call: {result1}")
|
|
145
|
+
assert result1["status"] == "success"
|
|
146
|
+
|
|
147
|
+
result2 = client.create_topic(topic_name)
|
|
148
|
+
logger.info(f"create_topic second call: {result2}")
|
|
149
|
+
assert result2["status"] == "success"
|
|
150
|
+
# Topic should still be in the list
|
|
151
|
+
assert topic_name in result2["topics"]
|
|
152
|
+
|
|
153
|
+
# Cleanup
|
|
154
|
+
client.delete_topic(topic_name)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@pytest.mark.integration
|
|
158
|
+
def test_delete_topic(client):
|
|
159
|
+
"""Test delete_topic API."""
|
|
160
|
+
# Ensure user exists
|
|
161
|
+
client.create_user()
|
|
162
|
+
|
|
163
|
+
topic_name = f"test-topic-{str(uuid.uuid4())[:8]}"
|
|
164
|
+
# Create topic first
|
|
165
|
+
create_result = client.create_topic(topic_name)
|
|
166
|
+
assert create_result["status"] == "success"
|
|
167
|
+
assert topic_name in create_result["topics"]
|
|
168
|
+
|
|
169
|
+
# Delete topic
|
|
170
|
+
result = client.delete_topic(topic_name)
|
|
171
|
+
logger.info(f"delete_topic result: {result}")
|
|
172
|
+
assert result["status"] == "success"
|
|
173
|
+
assert "topics" in result
|
|
174
|
+
assert topic_name not in result["topics"]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@pytest.mark.integration
|
|
178
|
+
def test_delete_topic_idempotent(client):
|
|
179
|
+
"""Test delete_topic is idempotent (can be called multiple times)."""
|
|
180
|
+
# Ensure user exists
|
|
181
|
+
client.create_user()
|
|
182
|
+
|
|
183
|
+
topic_name = f"test-topic-{str(uuid.uuid4())[:8]}"
|
|
184
|
+
# Create topic first
|
|
185
|
+
client.create_topic(topic_name)
|
|
186
|
+
|
|
187
|
+
# Delete topic first time
|
|
188
|
+
result1 = client.delete_topic(topic_name)
|
|
189
|
+
logger.info(f"delete_topic first call: {result1}")
|
|
190
|
+
assert result1["status"] == "success"
|
|
191
|
+
|
|
192
|
+
# Delete topic second time (should still succeed)
|
|
193
|
+
result2 = client.delete_topic(topic_name)
|
|
194
|
+
logger.info(f"delete_topic second call: {result2}")
|
|
195
|
+
assert result2["status"] in ("success", "failure") # May fail if already deleted
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@pytest.mark.integration
|
|
199
|
+
def test_recreate_topic(client):
|
|
200
|
+
"""Test recreate_topic API."""
|
|
201
|
+
# Ensure user exists
|
|
202
|
+
client.create_user()
|
|
203
|
+
|
|
204
|
+
topic_name = f"test-topic-{str(uuid.uuid4())[:8]}"
|
|
205
|
+
# Create topic first
|
|
206
|
+
create_result = client.create_topic(topic_name)
|
|
207
|
+
assert create_result["status"] == "success"
|
|
208
|
+
|
|
209
|
+
# Recreate topic
|
|
210
|
+
result = client.recreate_topic(topic_name)
|
|
211
|
+
logger.info(f"recreate_topic result: {result}")
|
|
212
|
+
assert result["status"] == "success"
|
|
213
|
+
|
|
214
|
+
# Cleanup
|
|
215
|
+
client.delete_topic(topic_name)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@pytest.mark.integration
|
|
219
|
+
def test_delete_key(client):
|
|
220
|
+
"""Test delete_key API."""
|
|
221
|
+
# Ensure user and key exist
|
|
222
|
+
client.create_user()
|
|
223
|
+
client.create_key()
|
|
224
|
+
|
|
225
|
+
result = client.delete_key()
|
|
226
|
+
logger.info(f"delete_key result: {result}")
|
|
227
|
+
assert result["status"] == "success"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@pytest.mark.integration
|
|
231
|
+
def test_delete_key_idempotent(client):
|
|
232
|
+
"""Test delete_key is idempotent (can be called multiple times)."""
|
|
233
|
+
# Ensure user and key exist
|
|
234
|
+
client.create_user()
|
|
235
|
+
client.create_key()
|
|
236
|
+
|
|
237
|
+
# Delete key first time
|
|
238
|
+
result1 = client.delete_key()
|
|
239
|
+
logger.info(f"delete_key first call: {result1}")
|
|
240
|
+
assert result1["status"] == "success"
|
|
241
|
+
|
|
242
|
+
# Delete key second time (should still succeed)
|
|
243
|
+
result2 = client.delete_key()
|
|
244
|
+
logger.info(f"delete_key second call: {result2}")
|
|
245
|
+
assert result2["status"] in ("success", "failure") # May fail if already deleted
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@pytest.mark.integration
|
|
249
|
+
def test_full_lifecycle(client):
|
|
250
|
+
"""Test full user lifecycle: create user, key, topic, then cleanup."""
|
|
251
|
+
# 1. Create user
|
|
252
|
+
user_result = client.create_user()
|
|
253
|
+
logger.info(f"Created user: {user_result}")
|
|
254
|
+
assert user_result["status"] == "success"
|
|
255
|
+
namespace = user_result["namespace"]
|
|
256
|
+
|
|
257
|
+
# 2. Create key
|
|
258
|
+
key_result = client.create_key()
|
|
259
|
+
logger.info(f"Created key: {key_result}")
|
|
260
|
+
assert key_result["status"] == "success"
|
|
261
|
+
assert "access_key" in key_result
|
|
262
|
+
|
|
263
|
+
# 3. List namespaces
|
|
264
|
+
namespaces_result = client.list_namespaces()
|
|
265
|
+
logger.info(f"Listed namespaces: {namespaces_result}")
|
|
266
|
+
assert namespaces_result["status"] == "success"
|
|
267
|
+
assert namespace in namespaces_result["namespaces"]
|
|
268
|
+
|
|
269
|
+
# 4. Create topic
|
|
270
|
+
topic_name = f"test-topic-{str(uuid.uuid4())[:8]}"
|
|
271
|
+
topic_result = client.create_topic(topic_name)
|
|
272
|
+
logger.info(f"Created topic: {topic_result}")
|
|
273
|
+
assert topic_result["status"] == "success"
|
|
274
|
+
assert topic_name in topic_result["topics"]
|
|
275
|
+
|
|
276
|
+
# 5. Delete topic
|
|
277
|
+
delete_topic_result = client.delete_topic(topic_name)
|
|
278
|
+
logger.info(f"Deleted topic: {delete_topic_result}")
|
|
279
|
+
assert delete_topic_result["status"] == "success"
|
|
50
280
|
|
|
281
|
+
# 6. Delete key
|
|
282
|
+
delete_key_result = client.delete_key()
|
|
283
|
+
logger.info(f"Deleted key: {delete_key_result}")
|
|
284
|
+
assert delete_key_result["status"] == "success"
|
|
51
285
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
assert "
|
|
56
|
-
assert "secret_key" in key_response
|
|
57
|
-
assert "endpoint" in key_response
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def test_register_topic(setup, client):
|
|
61
|
-
topic = "topic" + client.subject_openid[-12:]
|
|
62
|
-
register_response = client.register_topic(topic)
|
|
63
|
-
assert register_response["status"] in ["success", "no-op"]
|
|
64
|
-
assert "message" in register_response
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def test_list_topics(setup, client):
|
|
68
|
-
topics = client.list_topics()
|
|
69
|
-
print(client.subject_openid, topics)
|
|
70
|
-
assert topics["status"] == "success"
|
|
71
|
-
assert isinstance(topics["topics"], list)
|
|
72
|
-
assert len(topics["topics"]) > 0
|
|
73
|
-
expected_topics = ["diaspora-cicd"]
|
|
74
|
-
assert set(expected_topics).issubset(set(topics["topics"]))
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def test_get_topic_configs(setup, client):
|
|
78
|
-
topic = "topic" + client.subject_openid[-12:]
|
|
79
|
-
client.register_topic(topic)
|
|
80
|
-
configs_response = client.get_topic_configs(topic)
|
|
81
|
-
assert configs_response["status"] == "success"
|
|
82
|
-
assert "configs" in configs_response
|
|
83
|
-
assert isinstance(configs_response["configs"], dict)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def test_update_topic_configs(setup, client):
|
|
87
|
-
topic = "topic" + client.subject_openid[-12:]
|
|
88
|
-
client.register_topic(topic)
|
|
89
|
-
configs = {"min.insync.replicas": 1}
|
|
90
|
-
update_response = client.update_topic_configs(topic, configs)
|
|
91
|
-
assert update_response["status"] == "success"
|
|
92
|
-
assert "before" in update_response
|
|
93
|
-
assert "after" in update_response
|
|
94
|
-
assert isinstance(update_response["before"], dict)
|
|
95
|
-
assert isinstance(update_response["after"], dict)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def test_update_topic_partitions(setup, client):
|
|
99
|
-
topic = "topic" + client.subject_openid[-12:]
|
|
100
|
-
client.register_topic(topic)
|
|
101
|
-
new_partitions = 2
|
|
102
|
-
partitions_response = client.update_topic_partitions(topic, new_partitions)
|
|
103
|
-
assert partitions_response["status"] in ["success", "error"]
|
|
104
|
-
if partitions_response["status"] == "error":
|
|
105
|
-
assert "message" in partitions_response
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def test_reset_topic(setup, client):
|
|
109
|
-
topic = "topic" + client.subject_openid[-12:]
|
|
110
|
-
client.register_topic(topic)
|
|
111
|
-
reset_response = client.reset_topic(topic)
|
|
112
|
-
assert reset_response["status"] in ["success", "error"]
|
|
113
|
-
if reset_response["status"] == "error":
|
|
114
|
-
assert "message" in reset_response
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def test_user_access_management(setup, client):
|
|
118
|
-
topic = "topic" + client.subject_openid[-12:]
|
|
119
|
-
client.register_topic(topic)
|
|
120
|
-
user_id = "diaspora-cicd"
|
|
121
|
-
grant_response = client.grant_user_access(topic, user_id)
|
|
122
|
-
assert grant_response["status"] in ["success", "no-op"]
|
|
123
|
-
assert "message" in grant_response
|
|
124
|
-
|
|
125
|
-
list_users_response = client.list_topic_users(topic)
|
|
126
|
-
assert list_users_response["status"] == "success"
|
|
127
|
-
assert "users" in list_users_response
|
|
128
|
-
assert isinstance(list_users_response["users"], list)
|
|
129
|
-
|
|
130
|
-
revoke_response = client.revoke_user_access(topic, user_id)
|
|
131
|
-
assert revoke_response["status"] in ["success", "no-op"]
|
|
132
|
-
assert "message" in revoke_response
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if __name__ == "__main__":
|
|
136
|
-
pytest.main(["-s", "tests/unit/test_apis.py"])
|
|
286
|
+
# 7. Delete user
|
|
287
|
+
delete_user_result = client.delete_user()
|
|
288
|
+
logger.info(f"Deleted user: {delete_user_result}")
|
|
289
|
+
assert delete_user_result["status"] == "success"
|