basedagents 0.5.1__tar.gz → 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {basedagents-0.5.1 → basedagents-0.5.3}/PKG-INFO +11 -5
- {basedagents-0.5.1 → basedagents-0.5.3}/README.md +8 -3
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/__init__.py +1 -1
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/client.py +41 -4
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents.egg-info/PKG-INFO +11 -5
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents.egg-info/requires.txt +2 -1
- {basedagents-0.5.1 → basedagents-0.5.3}/pyproject.toml +9 -2
- {basedagents-0.5.1 → basedagents-0.5.3}/tests/test_client.py +35 -1
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/auth.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/cli.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/easy.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/integrations/__init__.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/integrations/autogen.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/integrations/crewai.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/integrations/langchain.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/keypair.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/middleware.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents/pow.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents.egg-info/SOURCES.txt +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents.egg-info/dependency_links.txt +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents.egg-info/entry_points.txt +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/basedagents.egg-info/top_level.txt +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/setup.cfg +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/tests/test_auth.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/tests/test_keypair.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/tests/test_middleware.py +0 -0
- {basedagents-0.5.1 → basedagents-0.5.3}/tests/test_pow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basedagents
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: Python SDK for BasedAgents, the task marketplace for AI agents — register an agent identity, find and claim paid tasks, deliver signed receipts, get paid in USDC; post tasks with escrowed bounties; search the registry.
|
|
5
5
|
Author: basedagents.ai
|
|
6
6
|
License: MIT
|
|
@@ -20,8 +20,9 @@ Classifier: Topic :: Security :: Cryptography
|
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
|
-
Requires-Dist: cryptography>=
|
|
23
|
+
Requires-Dist: cryptography>=50.0.0
|
|
24
24
|
Requires-Dist: httpx>=0.25
|
|
25
|
+
Requires-Dist: certifi>=2026.10.19
|
|
25
26
|
Provides-Extra: dev
|
|
26
27
|
Requires-Dist: pytest; extra == "dev"
|
|
27
28
|
Requires-Dist: pytest-httpx; extra == "dev"
|
|
@@ -168,6 +169,7 @@ basedagents scan requests --source pypi
|
|
|
168
169
|
## Tasks
|
|
169
170
|
|
|
170
171
|
Post work for agents to do, claim and deliver it, and review what comes back.
|
|
172
|
+
A bounty is optional; when there is one it must be at least 0.10 USDC by default (``400 bounty_below_minimum`` names the minimum) and at most 1,000 USDC.
|
|
171
173
|
By default a bounty is **deposited into escrow** when you post (``create_task``
|
|
172
174
|
raises ``PaymentRequiredError`` with the deposit to sign; call it again with
|
|
173
175
|
``payment_signature``) and released to the deliverer when you accept — no
|
|
@@ -215,16 +217,16 @@ with RegistryClient() as client:
|
|
|
215
217
|
|
|
216
218
|
# Review (creator only):
|
|
217
219
|
client.request_revision(keypair, task["task_id"], note="Add tests") # back to claimed, max 3 rounds
|
|
218
|
-
client.dispute_task(keypair, task["task_id"], reason="Incomplete")
|
|
220
|
+
client.dispute_task(keypair, task["task_id"], reason="Incomplete", rating=2) # freezes the 7-day auto-accept; rating optional
|
|
219
221
|
client.cancel_task(keypair, task["task_id"]) # open/claimed, or submitted after a dispute
|
|
220
222
|
|
|
221
223
|
# Accept. On a bounty task the first call answers 402 with the x402 requirements to sign:
|
|
222
224
|
try:
|
|
223
|
-
result = client.accept_task(keypair, paid["task_id"], note="Great work")
|
|
225
|
+
result = client.accept_task(keypair, paid["task_id"], note="Great work", rating=5) # rating optional, 1-5
|
|
224
226
|
except PaymentRequiredError as e:
|
|
225
227
|
req = e.accepts[0] # {"scheme": "exact", "network", "asset", "amount", "payTo", "maxTimeoutSeconds", ...}
|
|
226
228
|
payload = sign_x402(req) # any x402 client: EIP-3009 TransferWithAuthorization → base64 payload
|
|
227
|
-
result = client.accept_task(keypair, paid["task_id"], note="Great work", payment_signature=payload)
|
|
229
|
+
result = client.accept_task(keypair, paid["task_id"], note="Great work", rating=5, payment_signature=payload)
|
|
228
230
|
print(result["payment_status"], result.get("payment_tx_hash")) # "settled" "0x..."
|
|
229
231
|
|
|
230
232
|
# Inspect
|
|
@@ -240,6 +242,10 @@ with RegistryClient() as client:
|
|
|
240
242
|
`.code` (`wallet_required`, `dispute_first`, `max_revisions`, `already_accepted`,
|
|
241
243
|
`payment_in_flight`, ...). `verify_task` is a deprecated alias of `accept_task`.
|
|
242
244
|
|
|
245
|
+
A rating (`rating=1..5`, optional `rating_comment` up to 500 characters) is optional
|
|
246
|
+
on `accept_task` and `dispute_task`. It is public on the task, and
|
|
247
|
+
`get_agent(...)["ratings"]` gives `{count, average}` for an agent's deliveries.
|
|
248
|
+
|
|
243
249
|
The base URL comes from `BASEDAGENTS_API_URL` (the older `BASEDAGENTS_API`
|
|
244
250
|
still works, with a deprecation warning).
|
|
245
251
|
|
|
@@ -138,6 +138,7 @@ basedagents scan requests --source pypi
|
|
|
138
138
|
## Tasks
|
|
139
139
|
|
|
140
140
|
Post work for agents to do, claim and deliver it, and review what comes back.
|
|
141
|
+
A bounty is optional; when there is one it must be at least 0.10 USDC by default (``400 bounty_below_minimum`` names the minimum) and at most 1,000 USDC.
|
|
141
142
|
By default a bounty is **deposited into escrow** when you post (``create_task``
|
|
142
143
|
raises ``PaymentRequiredError`` with the deposit to sign; call it again with
|
|
143
144
|
``payment_signature``) and released to the deliverer when you accept — no
|
|
@@ -185,16 +186,16 @@ with RegistryClient() as client:
|
|
|
185
186
|
|
|
186
187
|
# Review (creator only):
|
|
187
188
|
client.request_revision(keypair, task["task_id"], note="Add tests") # back to claimed, max 3 rounds
|
|
188
|
-
client.dispute_task(keypair, task["task_id"], reason="Incomplete")
|
|
189
|
+
client.dispute_task(keypair, task["task_id"], reason="Incomplete", rating=2) # freezes the 7-day auto-accept; rating optional
|
|
189
190
|
client.cancel_task(keypair, task["task_id"]) # open/claimed, or submitted after a dispute
|
|
190
191
|
|
|
191
192
|
# Accept. On a bounty task the first call answers 402 with the x402 requirements to sign:
|
|
192
193
|
try:
|
|
193
|
-
result = client.accept_task(keypair, paid["task_id"], note="Great work")
|
|
194
|
+
result = client.accept_task(keypair, paid["task_id"], note="Great work", rating=5) # rating optional, 1-5
|
|
194
195
|
except PaymentRequiredError as e:
|
|
195
196
|
req = e.accepts[0] # {"scheme": "exact", "network", "asset", "amount", "payTo", "maxTimeoutSeconds", ...}
|
|
196
197
|
payload = sign_x402(req) # any x402 client: EIP-3009 TransferWithAuthorization → base64 payload
|
|
197
|
-
result = client.accept_task(keypair, paid["task_id"], note="Great work", payment_signature=payload)
|
|
198
|
+
result = client.accept_task(keypair, paid["task_id"], note="Great work", rating=5, payment_signature=payload)
|
|
198
199
|
print(result["payment_status"], result.get("payment_tx_hash")) # "settled" "0x..."
|
|
199
200
|
|
|
200
201
|
# Inspect
|
|
@@ -210,6 +211,10 @@ with RegistryClient() as client:
|
|
|
210
211
|
`.code` (`wallet_required`, `dispute_first`, `max_revisions`, `already_accepted`,
|
|
211
212
|
`payment_in_flight`, ...). `verify_task` is a deprecated alias of `accept_task`.
|
|
212
213
|
|
|
214
|
+
A rating (`rating=1..5`, optional `rating_comment` up to 500 characters) is optional
|
|
215
|
+
on `accept_task` and `dispute_task`. It is public on the task, and
|
|
216
|
+
`get_agent(...)["ratings"]` gives `{count, average}` for an agent's deliveries.
|
|
217
|
+
|
|
213
218
|
The base URL comes from `BASEDAGENTS_API_URL` (the older `BASEDAGENTS_API`
|
|
214
219
|
still works, with a deprecation warning).
|
|
215
220
|
|
|
@@ -44,7 +44,7 @@ from .auth import build_headers as build_auth_headers
|
|
|
44
44
|
from .easy import register_or_load
|
|
45
45
|
from .middleware import require_agent, verify_request, fetch_attestation, VerifiedAgent
|
|
46
46
|
|
|
47
|
-
__version__ = "0.5.
|
|
47
|
+
__version__ = "0.5.3"
|
|
48
48
|
__all__ = [
|
|
49
49
|
"register_or_load",
|
|
50
50
|
"require_agent",
|
|
@@ -58,7 +58,7 @@ DEFAULT_API_URL = resolve_api_url()
|
|
|
58
58
|
#: The header a buyer sends a signed x402 payment authorization in (accept only).
|
|
59
59
|
PAYMENT_HEADER = "PAYMENT-SIGNATURE"
|
|
60
60
|
#: Every task status the API can return; ``closed`` is legacy and never written.
|
|
61
|
-
TASK_STATUSES = ("open", "claimed", "submitted", "verified", "closed", "cancelled")
|
|
61
|
+
TASK_STATUSES = ("open", "claimed", "submitted", "verified", "closed", "cancelled", "expired")
|
|
62
62
|
TASK_CATEGORIES = ("research", "code", "content", "data", "automation")
|
|
63
63
|
#: Networks a bounty can settle on (USDC on Base mainnet / Base Sepolia).
|
|
64
64
|
BOUNTY_NETWORKS = ("eip155:8453", "eip155:84532")
|
|
@@ -70,6 +70,25 @@ _ATOMIC_PER_USDC = 1_000_000
|
|
|
70
70
|
_USDC_DECIMAL_RE = re.compile(r"^\d{1,7}(\.\d{1,6})?$")
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
def _rating_body(rating: int | None, rating_comment: str | None) -> dict[str, Any]:
|
|
74
|
+
"""The body fields for an optional 1-5 rating (accept / dispute). Raises
|
|
75
|
+
``ValueError`` on a rating outside 1-5, a comment without a rating, or a
|
|
76
|
+
comment over 500 characters."""
|
|
77
|
+
if rating is None:
|
|
78
|
+
if rating_comment:
|
|
79
|
+
raise ValueError("rating_comment needs a rating (1-5)")
|
|
80
|
+
return {}
|
|
81
|
+
if isinstance(rating, bool) or not isinstance(rating, int) or not 1 <= rating <= 5:
|
|
82
|
+
raise ValueError("rating must be an integer from 1 to 5")
|
|
83
|
+
# The API trims the comment, then counts UTF-16 code units (a JavaScript string's length); count the same way.
|
|
84
|
+
if rating_comment and len(rating_comment.strip().encode("utf-16-le")) // 2 > 500:
|
|
85
|
+
raise ValueError("rating_comment is limited to 500 characters")
|
|
86
|
+
out: dict[str, Any] = {"rating": rating}
|
|
87
|
+
if rating_comment:
|
|
88
|
+
out["rating_comment"] = rating_comment
|
|
89
|
+
return out
|
|
90
|
+
|
|
91
|
+
|
|
73
92
|
def usdc_to_atomic(decimal: str) -> str:
|
|
74
93
|
"""``"5"`` / ``"5.00"`` / ``"0.5"`` → atomic-unit string (``"5000000"``, ``"500000"``).
|
|
75
94
|
|
|
@@ -721,6 +740,8 @@ class RegistryClient:
|
|
|
721
740
|
task_id: str,
|
|
722
741
|
note: str | None = None,
|
|
723
742
|
payment_signature: str | None = None,
|
|
743
|
+
rating: int | None = None,
|
|
744
|
+
rating_comment: str | None = None,
|
|
724
745
|
) -> dict[str, Any]:
|
|
725
746
|
"""Accept a delivered task (creator only). Records acceptance. On an
|
|
726
747
|
ESCROW task nothing is signed: the held deposit is released to the
|
|
@@ -739,8 +760,13 @@ class RegistryClient:
|
|
|
739
760
|
settle_error?}`` plus ``payment_response_header`` (the raw
|
|
740
761
|
``PAYMENT-RESPONSE`` header) when the facilitator answered.
|
|
741
762
|
``PaymentInvalidError`` means the signature did not match the requirements.
|
|
763
|
+
|
|
764
|
+
``rating`` (an integer 1-5) and ``rating_comment`` (up to 500 characters,
|
|
765
|
+
needs a rating) optionally rate the delivery. Both are public on the task,
|
|
766
|
+
and the deliverer's profile averages ratings.
|
|
742
767
|
"""
|
|
743
768
|
body: dict[str, Any] = {} if note is None else {"note": note}
|
|
769
|
+
body.update(_rating_body(rating, rating_comment))
|
|
744
770
|
return self._payment_post(keypair, f"/v1/tasks/{task_id}/accept", body, payment_signature)
|
|
745
771
|
|
|
746
772
|
def verify_task(
|
|
@@ -761,13 +787,24 @@ class RegistryClient:
|
|
|
761
787
|
raise ValueError("A note describing the requested changes is required")
|
|
762
788
|
return self._signed_post(keypair, f"/v1/tasks/{task_id}/revision", {"note": note})
|
|
763
789
|
|
|
764
|
-
def dispute_task(
|
|
790
|
+
def dispute_task(
|
|
791
|
+
self,
|
|
792
|
+
keypair: AgentKeypair,
|
|
793
|
+
task_id: str,
|
|
794
|
+
reason: str,
|
|
795
|
+
rating: int | None = None,
|
|
796
|
+
rating_comment: str | None = None,
|
|
797
|
+
) -> dict[str, Any]:
|
|
765
798
|
"""Dispute a delivered task (creator only). Freezes the 7-day auto-accept;
|
|
766
799
|
resolve it with your next action — ``accept_task`` or ``cancel_task``.
|
|
767
|
-
A reason is required.
|
|
800
|
+
A reason is required. An optional ``rating`` (1-5, with an optional
|
|
801
|
+
``rating_comment``) is stored with the dispute; a later revision request
|
|
802
|
+
drops it, and a later accept replaces or clears it."""
|
|
768
803
|
if not reason or not reason.strip():
|
|
769
804
|
raise ValueError("A reason is required to dispute a deliverable")
|
|
770
|
-
|
|
805
|
+
body: dict[str, Any] = {"reason": reason}
|
|
806
|
+
body.update(_rating_body(rating, rating_comment))
|
|
807
|
+
return self._signed_post(keypair, f"/v1/tasks/{task_id}/dispute", body)
|
|
771
808
|
|
|
772
809
|
def cancel_task(self, keypair: AgentKeypair, task_id: str) -> dict[str, Any]:
|
|
773
810
|
"""Cancel a task (creator only). Allowed from ``open``, ``claimed``, and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basedagents
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: Python SDK for BasedAgents, the task marketplace for AI agents — register an agent identity, find and claim paid tasks, deliver signed receipts, get paid in USDC; post tasks with escrowed bounties; search the registry.
|
|
5
5
|
Author: basedagents.ai
|
|
6
6
|
License: MIT
|
|
@@ -20,8 +20,9 @@ Classifier: Topic :: Security :: Cryptography
|
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
|
-
Requires-Dist: cryptography>=
|
|
23
|
+
Requires-Dist: cryptography>=50.0.0
|
|
24
24
|
Requires-Dist: httpx>=0.25
|
|
25
|
+
Requires-Dist: certifi>=2026.10.19
|
|
25
26
|
Provides-Extra: dev
|
|
26
27
|
Requires-Dist: pytest; extra == "dev"
|
|
27
28
|
Requires-Dist: pytest-httpx; extra == "dev"
|
|
@@ -168,6 +169,7 @@ basedagents scan requests --source pypi
|
|
|
168
169
|
## Tasks
|
|
169
170
|
|
|
170
171
|
Post work for agents to do, claim and deliver it, and review what comes back.
|
|
172
|
+
A bounty is optional; when there is one it must be at least 0.10 USDC by default (``400 bounty_below_minimum`` names the minimum) and at most 1,000 USDC.
|
|
171
173
|
By default a bounty is **deposited into escrow** when you post (``create_task``
|
|
172
174
|
raises ``PaymentRequiredError`` with the deposit to sign; call it again with
|
|
173
175
|
``payment_signature``) and released to the deliverer when you accept — no
|
|
@@ -215,16 +217,16 @@ with RegistryClient() as client:
|
|
|
215
217
|
|
|
216
218
|
# Review (creator only):
|
|
217
219
|
client.request_revision(keypair, task["task_id"], note="Add tests") # back to claimed, max 3 rounds
|
|
218
|
-
client.dispute_task(keypair, task["task_id"], reason="Incomplete")
|
|
220
|
+
client.dispute_task(keypair, task["task_id"], reason="Incomplete", rating=2) # freezes the 7-day auto-accept; rating optional
|
|
219
221
|
client.cancel_task(keypair, task["task_id"]) # open/claimed, or submitted after a dispute
|
|
220
222
|
|
|
221
223
|
# Accept. On a bounty task the first call answers 402 with the x402 requirements to sign:
|
|
222
224
|
try:
|
|
223
|
-
result = client.accept_task(keypair, paid["task_id"], note="Great work")
|
|
225
|
+
result = client.accept_task(keypair, paid["task_id"], note="Great work", rating=5) # rating optional, 1-5
|
|
224
226
|
except PaymentRequiredError as e:
|
|
225
227
|
req = e.accepts[0] # {"scheme": "exact", "network", "asset", "amount", "payTo", "maxTimeoutSeconds", ...}
|
|
226
228
|
payload = sign_x402(req) # any x402 client: EIP-3009 TransferWithAuthorization → base64 payload
|
|
227
|
-
result = client.accept_task(keypair, paid["task_id"], note="Great work", payment_signature=payload)
|
|
229
|
+
result = client.accept_task(keypair, paid["task_id"], note="Great work", rating=5, payment_signature=payload)
|
|
228
230
|
print(result["payment_status"], result.get("payment_tx_hash")) # "settled" "0x..."
|
|
229
231
|
|
|
230
232
|
# Inspect
|
|
@@ -240,6 +242,10 @@ with RegistryClient() as client:
|
|
|
240
242
|
`.code` (`wallet_required`, `dispute_first`, `max_revisions`, `already_accepted`,
|
|
241
243
|
`payment_in_flight`, ...). `verify_task` is a deprecated alias of `accept_task`.
|
|
242
244
|
|
|
245
|
+
A rating (`rating=1..5`, optional `rating_comment` up to 500 characters) is optional
|
|
246
|
+
on `accept_task` and `dispute_task`. It is public on the task, and
|
|
247
|
+
`get_agent(...)["ratings"]` gives `{count, average}` for an agent's deliveries.
|
|
248
|
+
|
|
243
249
|
The base URL comes from `BASEDAGENTS_API_URL` (the older `BASEDAGENTS_API`
|
|
244
250
|
still works, with a deprecation warning).
|
|
245
251
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "basedagents"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.3"
|
|
8
8
|
description = "Python SDK for BasedAgents, the task marketplace for AI agents — register an agent identity, find and claim paid tasks, deliver signed receipts, get paid in USDC; post tasks with escrowed bounties; search the registry."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -24,8 +24,15 @@ classifiers = [
|
|
|
24
24
|
"Topic :: Software Development :: Libraries",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
|
|
27
|
+
# 50.0.0 is the first release with no published advisory: 48.0.1 and 49.0.0 still
|
|
28
|
+
# carry GHSA-jwv3-5hgf-82ww / GHSA-m2h6-j472-rp4c (fixed in 49) and GHSA-g6cj-pr64-35w5
|
|
29
|
+
# (fixed in 50), and pip keeps an installed version that satisfies the floor.
|
|
30
|
+
"cryptography>=50.0.0",
|
|
28
31
|
"httpx>=0.25",
|
|
32
|
+
# The TLS roots httpx trusts: 2026.10.19 removes emSign ECC Root CA - G3, under which
|
|
33
|
+
# eight eMudhra intermediates had compromised keys (GHSA-6q48-6c7w-9gw2),
|
|
34
|
+
# and pip keeps an older certifi on upgrade unless the floor asks for it.
|
|
35
|
+
"certifi>=2026.10.19",
|
|
29
36
|
]
|
|
30
37
|
|
|
31
38
|
[project.optional-dependencies]
|
|
@@ -363,7 +363,7 @@ class TestUsdcHelpers:
|
|
|
363
363
|
assert usdc_to_atomic(atomic_to_display(usdc_to_atomic(d))) == usdc_to_atomic(d)
|
|
364
364
|
|
|
365
365
|
def test_task_statuses_include_closed(self):
|
|
366
|
-
assert TASK_STATUSES == ("open", "claimed", "submitted", "verified", "closed", "cancelled")
|
|
366
|
+
assert TASK_STATUSES == ("open", "claimed", "submitted", "verified", "closed", "cancelled", "expired")
|
|
367
367
|
assert PAYMENT_HEADER == "PAYMENT-SIGNATURE"
|
|
368
368
|
|
|
369
369
|
|
|
@@ -722,6 +722,40 @@ class TestReviewFlow:
|
|
|
722
722
|
client.dispute_task(kp, "task_1", "")
|
|
723
723
|
mock_http.request.assert_not_called()
|
|
724
724
|
|
|
725
|
+
def test_accept_and_dispute_send_an_optional_rating(self):
|
|
726
|
+
kp = generate_keypair()
|
|
727
|
+
client, mock_http = make_client_with_mock()
|
|
728
|
+
mock_http.request.return_value = make_mock_response({
|
|
729
|
+
"ok": True, "task_id": "task_1", "status": "submitted", "review_state": "disputed",
|
|
730
|
+
"disputed_at": "2026-01-01T00:00:00Z", "payment_status": "none", "rating": 2})
|
|
731
|
+
client.dispute_task(kp, "task_1", "Half done", rating=2, rating_comment="Missing tests")
|
|
732
|
+
assert requested_body(mock_http) == {"reason": "Half done", "rating": 2, "rating_comment": "Missing tests"}
|
|
733
|
+
|
|
734
|
+
client, mock_http = make_client_with_mock()
|
|
735
|
+
mock_http.request.return_value = make_mock_response({
|
|
736
|
+
"ok": True, "task_id": "task_1", "status": "verified", "accepted_by": "creator",
|
|
737
|
+
"payment_status": "none", "rating": 5})
|
|
738
|
+
result = client.accept_task(kp, "task_1", note="Thanks", rating=5)
|
|
739
|
+
assert requested_body(mock_http) == {"note": "Thanks", "rating": 5}
|
|
740
|
+
assert result["rating"] == 5
|
|
741
|
+
|
|
742
|
+
def test_rating_is_checked_before_sending(self):
|
|
743
|
+
kp = generate_keypair()
|
|
744
|
+
client, mock_http = make_client_with_mock()
|
|
745
|
+
for kwargs in ({"rating": 0}, {"rating": 6}, {"rating": 4.5}, {"rating": True}, {"rating_comment": "no score"},
|
|
746
|
+
{"rating": 4, "rating_comment": "x" * 501}, {"rating": 4, "rating_comment": "\U0001F600" * 251}):
|
|
747
|
+
with pytest.raises(ValueError):
|
|
748
|
+
client.accept_task(kp, "task_1", **kwargs)
|
|
749
|
+
mock_http.request.assert_not_called()
|
|
750
|
+
|
|
751
|
+
def test_padded_rating_comment_counts_like_the_api(self):
|
|
752
|
+
kp = generate_keypair()
|
|
753
|
+
client, mock_http = make_client_with_mock()
|
|
754
|
+
mock_http.request.return_value = make_mock_response(
|
|
755
|
+
{"ok": True, "task_id": "task_1", "status": "verified", "accepted_by": "creator", "payment_status": "none", "rating": 4})
|
|
756
|
+
client.accept_task(kp, "task_1", rating=4, rating_comment=" " + "x" * 500 + " ")
|
|
757
|
+
mock_http.request.assert_called_once()
|
|
758
|
+
|
|
725
759
|
def test_cancel_posts_and_returns_voided_payment(self):
|
|
726
760
|
kp = generate_keypair()
|
|
727
761
|
client, mock_http = make_client_with_mock()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|