Initial release — APIClient - Agent v2.0.0

AI-first API testing desktop client built with Python + PyQt6.

Features:
- Multi-tab HTTP request editor with params/headers/body/auth/tests
- KeyValueTable with per-row enable/disable checkboxes and 36px rows
- Format JSON button, syntax highlighting, pre-request & test scripts
- Collections, environments, history, import/export (Postman v2.1, cURL)
- OpenAPI 3.x / Swagger 2.0 local parser (no AI tokens)
- EKIKA Odoo API Framework generator — JSON-API, REST JSON, GraphQL,
  Custom REST JSON with all auth types (instant, no AI tokens)
- Persistent AI chat sidebar (Claude-powered co-pilot) with streaming,
  context-aware suggestions, and one-click Apply to request editor
- AI collection generator from any docs URL or pasted spec
- WebSocket client, Mock server, Collection runner, Code generator
- Dark/light theme engine (global QSS, object-name selectors)
- SSL error detection with actionable hints
- MIT License

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-28 17:34:18 +05:30
parent 1dbbb4320b
commit 01662f7e0e
37 changed files with 7822 additions and 1 deletions

86
app/core/test_runner.py Normal file
View File

@@ -0,0 +1,86 @@
"""Run test scripts after a response is received."""
import json
from app.models import HttpResponse, TestResult
class TestContext:
"""Exposes pm.test(), pm.expect(), pm.response in test scripts."""
def __init__(self, response: HttpResponse):
self.results: list[TestResult] = []
self.response = _ResponseProxy(response)
self.expect = _expect
def test(self, name: str, fn):
try:
fn()
self.results.append(TestResult(name=name, passed=True))
except AssertionError as e:
self.results.append(TestResult(name=name, passed=False, message=str(e)))
except Exception as e:
self.results.append(TestResult(name=name, passed=False, message=f"Error: {e}"))
class _ResponseProxy:
def __init__(self, resp: HttpResponse):
self._resp = resp
self.status = resp.status
self.responseTime = resp.elapsed_ms
self.text = resp.body
try:
self._json = json.loads(resp.body)
except Exception:
self._json = None
def json(self):
return self._json
def to_have_status(self, code: int):
assert self._resp.status == code, f"Expected status {code}, got {self._resp.status}"
class _Assertion:
def __init__(self, value):
self._value = value
def to_equal(self, expected):
assert self._value == expected, f"Expected {expected!r}, got {self._value!r}"
return self
def to_be_truthy(self):
assert self._value, f"Expected truthy, got {self._value!r}"
return self
def to_include(self, substr):
assert substr in str(self._value), f"Expected {substr!r} in {self._value!r}"
return self
def to_be_below(self, n):
assert self._value < n, f"Expected {self._value} < {n}"
return self
def to_have_property(self, key):
assert hasattr(self._value, key) or (isinstance(self._value, dict) and key in self._value), \
f"Expected property {key!r}"
return self
def _expect(value) -> _Assertion:
return _Assertion(value)
def run_tests(script: str, response: HttpResponse) -> list[TestResult]:
if not script or not script.strip():
return []
ctx = TestContext(response)
namespace = {
"pm": ctx,
"response": ctx.response,
"expect": _expect,
}
try:
exec(script, namespace)
except Exception as e:
ctx.results.append(TestResult(name="Script Error", passed=False, message=str(e)))
return ctx.results