andrewyng--aisuite
06f93e12f4
Addresses the first-run review (sign-up -> setup -> first task), focused on non-developers getting past model setup. - Fix the "send twice" bug: drop `workspace` from the session-connect effect deps so adopting the server's Cowork scratch dir no longer tears down and rebuilds the socket right after connect (which silently dropped the first message). Add an outbound queue that flushes on open as a backstop. - Composer: replace the misleading model picker with a "No model" warning when no provider is configured; sending routes to Configure Models and keeps the draft. The picker now lists only configured providers' models (drop the hardcoded seed list, including the phantom deepseek-chat). - Onboarding: lead with the privacy-first value prop; auto-detect the provider from the key shape; add a "Test" button (live, no-save credential check) and a "Don't have a key?" helper; warn on Skip when nothing is connected. - Settings > Configure Models: clearer connected/not-connected states, a Test button, a "Done - back to chat" footer, and a reassuring save confirmation. - Backend: POST /v1/providers/verify (transient httpx model-list check) plus provider auto-detect; model_ready flag in settings; filter the model list to configured providers. - Automations: a "New automation" CTA and one-click templates (morning news briefing, inbox digest, folder cleanup) via a new POST /v1/automations. - Start screen leads with one-click task cards; add a topbar "Artifacts (N)" affordance when the side panel is hidden. Tests: GUI tsc + build clean; backend 351 passed (adds provider-verify and automation-create tests).
74 行
2.3 KiB
Python
74 行
2.3 KiB
Python
"""Tests for the GUI-driven `create_automation` path (the "New automation" / template flow).
|
|
|
|
No network and no LLM: this exercises validation + that a valid create lands in the task store
|
|
with a freshly provisioned scratch workspace.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from coworker.server.manager import SessionManager
|
|
|
|
|
|
def _manager(tmp_path, monkeypatch) -> SessionManager:
|
|
monkeypatch.setenv("COWORKER_STATE_DIR", str(tmp_path / "state"))
|
|
return SessionManager(data_dir=tmp_path / "data")
|
|
|
|
|
|
def test_create_automation_success(tmp_path, monkeypatch):
|
|
manager = _manager(tmp_path, monkeypatch)
|
|
out = manager.create_automation(
|
|
{
|
|
"title": "Morning news briefing",
|
|
"instructions": "Search the web and write a 5-bullet briefing.",
|
|
"cron": "0 8 * * *",
|
|
}
|
|
)
|
|
assert out["ok"] is True
|
|
task = out["task"]
|
|
assert task["title"] == "Morning news briefing"
|
|
assert task["schedule"] == "Every day at ~8:00 AM"
|
|
# it really landed in the store and is bound to a fresh scratch workspace
|
|
saved = manager.task_store.get(task["id"])
|
|
assert saved is not None
|
|
assert saved.agent == "cowork"
|
|
assert Path(saved.workspace).is_dir()
|
|
|
|
|
|
def test_create_automation_invalid_cron(tmp_path, monkeypatch):
|
|
manager = _manager(tmp_path, monkeypatch)
|
|
out = manager.create_automation(
|
|
{
|
|
"title": "Bad",
|
|
"instructions": "do something",
|
|
"cron": "not-a-cron",
|
|
}
|
|
)
|
|
assert out["ok"] is False
|
|
assert "invalid cron" in out["error"]
|
|
assert manager.task_store.list() == []
|
|
|
|
|
|
def test_create_automation_missing_instructions(tmp_path, monkeypatch):
|
|
manager = _manager(tmp_path, monkeypatch)
|
|
out = manager.create_automation(
|
|
{
|
|
"title": "No instructions",
|
|
"instructions": " ",
|
|
"cron": "0 8 * * *",
|
|
}
|
|
)
|
|
assert out["ok"] is False
|
|
assert "instructions" in out["error"]
|
|
assert manager.task_store.list() == []
|
|
|
|
|
|
def test_create_automation_requires_schedule(tmp_path, monkeypatch):
|
|
manager = _manager(tmp_path, monkeypatch)
|
|
out = manager.create_automation(
|
|
{"title": "No schedule", "instructions": "do something"}
|
|
)
|
|
assert out["ok"] is False
|
|
assert manager.task_store.list() == []
|