mirror of
https://github.com/MervinPraison/PraisonAIUI.git
synced 2026-11-07 05:56:17 -10:00
* feat: standardize bootstrap entrypoint to 'aiui run app.py' across examples Standardizes platform bootstrap patterns across examples per issue #71: - Update examples README table to use 'aiui run app.py' consistently - Update docstrings in examples 10, 12, 19-21 to show 'aiui run' instead of 'python app.py' - Remove fragmented bootstrap approaches (uvicorn.run, asyncio patterns) - Enforce single bootstrap SSOT as designed in CLI Co-authored-by: Mervin Praison <[email protected]> * feat: consolidate bootstrap entrypoints - auto-detect gateway apps (fixes #71) - Add bootstrap detection in CLI run command - Auto-detect gateway pattern (async main + AIUIGateway imports) - Auto-switch to praisonai backend for gateway apps - Delegate to user-defined main() when present - Remove sys.path hacks from example 14 - Update examples README to show unified aiui run entrypoint - Add BOOTSTRAP.md documentation for migration patterns Co-authored-by: Mervin Praison <[email protected]> * fix: resolve critical bootstrap entrypoint issues - Fix AST parsing bug in gateway auto-detection (cli.py:1419) - Revert background bots (email-bot, agentmail-bot) to python app.py - Revert custom UI apps (agent-dashboard) to python app.py - Update README.md to reflect correct run commands - Fix import formatting and remove unused imports These changes ensure that: 1. Gateway auto-detection works correctly by properly checking ast.alias.name 2. Background bots execute their __main__ blocks and don't start unnecessary web servers 3. Custom UI apps register their custom routes in __main__ blocks 4. Documentation matches actual working run commands 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Mervin Praison <[email protected]> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mervin Praison <[email protected]> Co-authored-by: Mervin Praison <[email protected]> Co-authored-by: Cursor <[email protected]>
100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
"""Per-feature test: Schedules — API + CLI parity."""
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
|
|
from starlette.testclient import TestClient
|
|
|
|
from praisonaiui.server import create_app
|
|
|
|
client = TestClient(create_app())
|
|
passed = failed = 0
|
|
errors = []
|
|
|
|
def check(name, r, status=200, keys=None):
|
|
global passed, failed
|
|
try:
|
|
assert r.status_code == status, f"Expected {status}, got {r.status_code}: {r.text[:200]}"
|
|
if keys:
|
|
data = r.json()
|
|
for k in keys:
|
|
assert k in data, f"Missing '{k}' in {list(data.keys())}"
|
|
passed += 1
|
|
print(f" ✓ {name}")
|
|
except AssertionError as e:
|
|
failed += 1; errors.append(f" ✗ {name}: {e}"); print(f" ✗ {name}: {e}")
|
|
|
|
print("\n── Schedules: API Tests ──")
|
|
|
|
# Empty
|
|
check("GET /api/schedules (empty)", client.get("/api/schedules"), 200, ["schedules", "count"])
|
|
|
|
# Create interval job
|
|
r = client.post("/api/schedules", json={
|
|
"name": "daily-report", "message": "Generate report",
|
|
"schedule": {"kind": "every", "every_seconds": 86400},
|
|
})
|
|
check("POST create (interval)", r, 201, ["id", "name", "schedule", "enabled"])
|
|
jid = r.json()["id"]
|
|
assert r.json()["enabled"] == True
|
|
|
|
# Create cron job
|
|
r2 = client.post("/api/schedules", json={
|
|
"name": "midnight-cleanup", "message": "Clean temp files",
|
|
"schedule": {"kind": "cron", "cron_expression": "0 0 * * *"},
|
|
})
|
|
check("POST create (cron)", r2, 201)
|
|
jid2 = r2.json()["id"]
|
|
|
|
# List (should have 2)
|
|
r = client.get("/api/schedules")
|
|
check("GET list (2 jobs)", r, 200)
|
|
assert r.json()["count"] == 2
|
|
|
|
# Get by ID
|
|
check("GET /:id", client.get(f"/api/schedules/{jid}"), 200, ["id", "schedule"])
|
|
|
|
# Toggle off
|
|
r = client.post(f"/api/schedules/{jid}/toggle")
|
|
check("POST toggle (disable)", r, 200, ["id", "enabled"])
|
|
assert r.json()["enabled"] == False
|
|
|
|
# Toggle back on
|
|
r = client.post(f"/api/schedules/{jid}/toggle")
|
|
check("POST toggle (enable)", r, 200)
|
|
assert r.json()["enabled"] == True
|
|
|
|
# Run immediately
|
|
r = client.post(f"/api/schedules/{jid}/run")
|
|
check("POST run", r, 200, ["triggered", "last_run_at"])
|
|
|
|
# Delete
|
|
check("DELETE /:id", client.delete(f"/api/schedules/{jid}"), 200, ["deleted"])
|
|
check("DELETE second", client.delete(f"/api/schedules/{jid2}"), 200)
|
|
|
|
# 404
|
|
check("DELETE nonexistent → 404", client.delete("/api/schedules/xxx"), 404)
|
|
check("GET nonexistent → 404", client.get("/api/schedules/xxx"), 404)
|
|
|
|
print("\n── Schedules: CLI Parity ──")
|
|
from typer.testing import CliRunner
|
|
|
|
from praisonaiui.cli import app as cli_app
|
|
|
|
runner = CliRunner()
|
|
|
|
result = runner.invoke(cli_app, ["schedule", "--help"])
|
|
check("CLI schedule --help", type("R", (), {"status_code": 200 if result.exit_code == 0 else 500, "json": lambda: {}, "text": result.output})(), 200)
|
|
assert "list" in result.output
|
|
assert "add" in result.output
|
|
assert "remove" in result.output
|
|
assert "status" in result.output
|
|
|
|
print(f"\n{'='*50}")
|
|
print(f" Schedules: {passed} passed, {failed} failed")
|
|
print(f"{'='*50}")
|
|
if errors:
|
|
for e in errors: print(e)
|
|
sys.exit(1)
|