chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,921 @@
|
||||
"""CLI cron command coverage.
|
||||
|
||||
Verifies the new helpers (_parse_duration_seconds, _resolve_webhook_token,
|
||||
_build_delivery_params) and that cron add/update build the right RPC params
|
||||
when the new flags are exercised. Network is stubbed so we never actually
|
||||
hit a gateway.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
import typer
|
||||
|
||||
from opensquilla.cli import cron_cmd
|
||||
|
||||
# --- _parse_duration_seconds ---------------------------------------------
|
||||
|
||||
|
||||
def test_parse_duration_none_returns_none() -> None:
|
||||
assert cron_cmd._parse_duration_seconds(None) is None
|
||||
assert cron_cmd._parse_duration_seconds("") is None
|
||||
|
||||
|
||||
def test_parse_duration_seconds_suffix() -> None:
|
||||
assert cron_cmd._parse_duration_seconds("30s") == 30.0
|
||||
assert cron_cmd._parse_duration_seconds("5m") == 300.0
|
||||
assert cron_cmd._parse_duration_seconds("1h") == 3600.0
|
||||
assert cron_cmd._parse_duration_seconds("90sec") == 90.0
|
||||
assert cron_cmd._parse_duration_seconds("2hrs") == 7200.0
|
||||
|
||||
|
||||
def test_parse_duration_plain_number() -> None:
|
||||
assert cron_cmd._parse_duration_seconds("45") == 45.0
|
||||
assert cron_cmd._parse_duration_seconds("0") == 0.0
|
||||
assert cron_cmd._parse_duration_seconds(60) == 60.0
|
||||
assert cron_cmd._parse_duration_seconds(60.5) == 60.5
|
||||
|
||||
|
||||
def test_parse_duration_rejects_garbage() -> None:
|
||||
with pytest.raises(typer.BadParameter):
|
||||
cron_cmd._parse_duration_seconds("nope")
|
||||
with pytest.raises(typer.BadParameter):
|
||||
cron_cmd._parse_duration_seconds("5x")
|
||||
|
||||
|
||||
# --- _resolve_webhook_token ----------------------------------------------
|
||||
|
||||
|
||||
def test_webhook_token_none_when_no_source() -> None:
|
||||
assert cron_cmd._resolve_webhook_token(inline=None, env=None, path=None) is None
|
||||
|
||||
|
||||
def test_webhook_token_from_env(monkeypatch) -> None:
|
||||
monkeypatch.setenv("MY_HOOK_TOKEN", "secret-bearer")
|
||||
assert (
|
||||
cron_cmd._resolve_webhook_token(inline=None, env="MY_HOOK_TOKEN", path=None)
|
||||
== "secret-bearer"
|
||||
)
|
||||
|
||||
|
||||
def test_webhook_token_from_env_unset(monkeypatch) -> None:
|
||||
monkeypatch.delenv("UNSET_TOKEN", raising=False)
|
||||
with pytest.raises(typer.BadParameter, match="UNSET_TOKEN"):
|
||||
cron_cmd._resolve_webhook_token(inline=None, env="UNSET_TOKEN", path=None)
|
||||
|
||||
|
||||
def test_webhook_token_from_file(tmp_path: Path) -> None:
|
||||
secret = tmp_path / "token.txt"
|
||||
secret.write_text(" file-token \n", encoding="utf-8")
|
||||
assert (
|
||||
cron_cmd._resolve_webhook_token(inline=None, env=None, path=str(secret))
|
||||
== "file-token"
|
||||
)
|
||||
|
||||
|
||||
def test_webhook_token_inline_emits_warning(capsys) -> None:
|
||||
out = cron_cmd._resolve_webhook_token(
|
||||
inline="raw-token", env=None, path=None
|
||||
)
|
||||
assert out == "raw-token"
|
||||
err = capsys.readouterr().err
|
||||
assert "shell history" in err
|
||||
|
||||
|
||||
def test_webhook_token_rejects_multiple_sources(monkeypatch, tmp_path: Path) -> None:
|
||||
monkeypatch.setenv("T", "x")
|
||||
secret = tmp_path / "t.txt"
|
||||
secret.write_text("y", encoding="utf-8")
|
||||
with pytest.raises(typer.BadParameter, match="at most one"):
|
||||
cron_cmd._resolve_webhook_token(inline=None, env="T", path=str(secret))
|
||||
|
||||
|
||||
# --- _build_delivery_params ---------------------------------------------
|
||||
|
||||
|
||||
def test_build_delivery_none_when_no_flags() -> None:
|
||||
assert (
|
||||
cron_cmd._build_delivery_params(
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
def test_build_delivery_announce_with_target() -> None:
|
||||
d = cron_cmd._build_delivery_params(
|
||||
announce=True,
|
||||
no_deliver=False,
|
||||
channel="slack",
|
||||
to="C123",
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
assert d == {"mode": "announce", "channelName": "slack", "to": "C123"}
|
||||
|
||||
|
||||
def test_build_delivery_announce_with_last_omits_channel_name() -> None:
|
||||
"""'last' is a CLI sentinel meaning 'let the backend infer'."""
|
||||
d = cron_cmd._build_delivery_params(
|
||||
announce=True,
|
||||
no_deliver=False,
|
||||
channel="last",
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
assert d == {"mode": "announce"}
|
||||
assert "channelName" not in d
|
||||
|
||||
|
||||
def test_build_delivery_no_deliver() -> None:
|
||||
d = cron_cmd._build_delivery_params(
|
||||
announce=False,
|
||||
no_deliver=True,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
assert d == {"mode": "none"}
|
||||
|
||||
|
||||
def test_build_delivery_webhook() -> None:
|
||||
d = cron_cmd._build_delivery_params(
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=True,
|
||||
webhook_url="https://hooks.example/cron",
|
||||
webhook_token="bearer-x",
|
||||
)
|
||||
assert d == {
|
||||
"mode": "webhook",
|
||||
"webhookUrl": "https://hooks.example/cron",
|
||||
"webhookToken": "bearer-x",
|
||||
"bestEffort": True,
|
||||
}
|
||||
|
||||
|
||||
def test_build_delivery_mutex_webhook_vs_announce() -> None:
|
||||
with pytest.raises(typer.BadParameter, match="at most one"):
|
||||
cron_cmd._build_delivery_params(
|
||||
announce=True,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url="https://hooks.example/x",
|
||||
webhook_token=None,
|
||||
)
|
||||
|
||||
|
||||
def test_build_delivery_mutex_announce_vs_no_deliver() -> None:
|
||||
with pytest.raises(typer.BadParameter, match="at most one"):
|
||||
cron_cmd._build_delivery_params(
|
||||
announce=True,
|
||||
no_deliver=True,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
|
||||
|
||||
def test_build_delivery_token_without_url() -> None:
|
||||
with pytest.raises(typer.BadParameter, match="requires --webhook-url"):
|
||||
cron_cmd._build_delivery_params(
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort=False,
|
||||
webhook_url=None,
|
||||
webhook_token="dangling-token",
|
||||
)
|
||||
|
||||
|
||||
def test_build_delivery_with_account_implies_announce() -> None:
|
||||
d = cron_cmd._build_delivery_params(
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel="slack",
|
||||
to="C123",
|
||||
account="acct-1",
|
||||
best_effort=True,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
assert d == {
|
||||
"mode": "announce",
|
||||
"channelName": "slack",
|
||||
"to": "C123",
|
||||
"accountId": "acct-1",
|
||||
"bestEffort": True,
|
||||
}
|
||||
|
||||
|
||||
# --- _build_failure_destination_dict -------------------------------------
|
||||
|
||||
|
||||
def test_failure_dest_none_when_no_flags() -> None:
|
||||
assert (
|
||||
cron_cmd._build_failure_destination_dict(
|
||||
mode=None,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
def test_failure_dest_webhook() -> None:
|
||||
fd = cron_cmd._build_failure_destination_dict(
|
||||
mode="webhook",
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
webhook_url="https://hooks.example/alert",
|
||||
webhook_token="bearer-x",
|
||||
)
|
||||
assert fd == {
|
||||
"mode": "webhook",
|
||||
"webhookUrl": "https://hooks.example/alert",
|
||||
"webhookToken": "bearer-x",
|
||||
}
|
||||
|
||||
|
||||
def test_failure_dest_channel() -> None:
|
||||
fd = cron_cmd._build_failure_destination_dict(
|
||||
mode="channel",
|
||||
channel="Slack",
|
||||
to="C-ops",
|
||||
account="acct-1",
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
assert fd == {
|
||||
"mode": "channel",
|
||||
"channelName": "slack",
|
||||
"to": "C-ops",
|
||||
"accountId": "acct-1",
|
||||
}
|
||||
|
||||
|
||||
def test_failure_dest_webhook_requires_url() -> None:
|
||||
with pytest.raises(typer.BadParameter, match="--failure-webhook-url"):
|
||||
cron_cmd._build_failure_destination_dict(
|
||||
mode="webhook",
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
|
||||
|
||||
def test_failure_dest_rejects_unknown_mode() -> None:
|
||||
with pytest.raises(typer.BadParameter, match="channel.*webhook"):
|
||||
cron_cmd._build_failure_destination_dict(
|
||||
mode="email",
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
|
||||
|
||||
def test_failure_dest_flag_without_mode() -> None:
|
||||
with pytest.raises(typer.BadParameter, match="--failure-mode"):
|
||||
cron_cmd._build_failure_destination_dict(
|
||||
mode=None,
|
||||
channel="slack",
|
||||
to="C-x",
|
||||
account=None,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
)
|
||||
|
||||
|
||||
# --- cron_add / cron_update RPC param construction ------------------------
|
||||
|
||||
|
||||
class _StubClient:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[tuple[str, dict[str, Any]]] = []
|
||||
|
||||
async def call(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
|
||||
self.calls.append((method, params))
|
||||
return {"id": "stub", "method": method}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stub_gateway(monkeypatch):
|
||||
client = _StubClient()
|
||||
|
||||
def _runner(fn, *, json_output: bool = False): # noqa: ARG001
|
||||
import asyncio
|
||||
return asyncio.run(fn(client))
|
||||
|
||||
monkeypatch.setattr(cron_cmd, "run_gateway_sync", _runner)
|
||||
monkeypatch.setattr(cron_cmd, "confirm_or_exit", lambda *a, **kw: None)
|
||||
monkeypatch.setattr(cron_cmd, "_emit_success", lambda *a, **kw: None)
|
||||
return client
|
||||
|
||||
|
||||
def test_cron_add_with_announce_and_channel(stub_gateway) -> None:
|
||||
cron_cmd.cron_add(
|
||||
expression="0 9 * * *",
|
||||
text="Daily brief",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="isolated",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=True,
|
||||
no_deliver=False,
|
||||
channel="slack",
|
||||
to="C123",
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
assert stub_gateway.calls, "no RPC call was issued"
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.add"
|
||||
assert params["delivery"] == {
|
||||
"mode": "announce",
|
||||
"channelName": "slack",
|
||||
"to": "C123",
|
||||
}
|
||||
|
||||
|
||||
def test_cron_add_rejects_current_target_without_session_binding(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="current.*session-bound"):
|
||||
cron_cmd.cron_add(
|
||||
expression="0 9 * * *",
|
||||
text="Daily brief",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="current",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
assert stub_gateway.calls == []
|
||||
|
||||
|
||||
def test_cron_add_with_webhook(stub_gateway, monkeypatch) -> None:
|
||||
monkeypatch.setenv("HOOK_TOKEN", "from-env")
|
||||
cron_cmd.cron_add(
|
||||
expression="*/5 * * * *",
|
||||
text="poke",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="isolated",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url="https://hooks.example/cron",
|
||||
webhook_token=None,
|
||||
webhook_token_env="HOOK_TOKEN",
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.add"
|
||||
assert params["delivery"] == {
|
||||
"mode": "webhook",
|
||||
"webhookUrl": "https://hooks.example/cron",
|
||||
"webhookToken": "from-env",
|
||||
}
|
||||
|
||||
|
||||
def test_cron_add_with_failure_destination(stub_gateway) -> None:
|
||||
cron_cmd.cron_add(
|
||||
expression="0 9 * * *",
|
||||
text="Daily brief",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="isolated",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=True,
|
||||
no_deliver=False,
|
||||
channel="slack",
|
||||
to="C123",
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode="webhook",
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url="https://hooks.example/alert",
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.add"
|
||||
assert params["delivery"] == {
|
||||
"mode": "announce",
|
||||
"channelName": "slack",
|
||||
"to": "C123",
|
||||
"failureDestination": {
|
||||
"mode": "webhook",
|
||||
"webhookUrl": "https://hooks.example/alert",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_cron_add_with_wake_and_jitter_duration(stub_gateway) -> None:
|
||||
cron_cmd.cron_add(
|
||||
expression="*/5 * * * *",
|
||||
text="x",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="main",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake="next-heartbeat",
|
||||
exact=False,
|
||||
jitter="30s",
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert params["wakeMode"] == "next-heartbeat"
|
||||
assert params["jitterSeconds"] == 30.0
|
||||
assert "delivery" not in params # no delivery flags → not sent
|
||||
|
||||
|
||||
def test_cron_add_with_every_builds_canonical_schedule(stub_gateway) -> None:
|
||||
cron_cmd.cron_add(
|
||||
expression=None,
|
||||
cron=None,
|
||||
every="5m",
|
||||
at=None,
|
||||
text="Drink water",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="isolated",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.add"
|
||||
assert params["schedule"] == {"kind": "every", "every_seconds": 300}
|
||||
assert params["payloadKind"] == "reminder"
|
||||
assert "expression" not in params
|
||||
|
||||
|
||||
def test_cron_add_rejects_multiple_schedule_sources(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="exactly one"):
|
||||
cron_cmd.cron_add(
|
||||
expression="*/5 * * * *",
|
||||
cron="0 9 * * *",
|
||||
every=None,
|
||||
at=None,
|
||||
text="x",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="isolated",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
|
||||
def test_cron_add_rejects_fractional_every(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="whole seconds"):
|
||||
cron_cmd.cron_add(
|
||||
expression=None,
|
||||
cron=None,
|
||||
every="1.9s",
|
||||
at=None,
|
||||
text="x",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="isolated",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
assert stub_gateway.calls == []
|
||||
|
||||
|
||||
def test_cron_add_rejects_invalid_wake(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="now or next-heartbeat"):
|
||||
cron_cmd.cron_add(
|
||||
expression="*/5 * * * *",
|
||||
text="x",
|
||||
name=None,
|
||||
agent=None,
|
||||
session_target="main",
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake="LATER",
|
||||
exact=False,
|
||||
jitter=None,
|
||||
announce=False,
|
||||
no_deliver=False,
|
||||
channel=None,
|
||||
to=None,
|
||||
account=None,
|
||||
best_effort_deliver=False,
|
||||
webhook_url=None,
|
||||
webhook_token=None,
|
||||
webhook_token_env=None,
|
||||
webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
|
||||
def test_cron_update_with_wake(stub_gateway) -> None:
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
cron=None,
|
||||
every=None,
|
||||
at=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake="now",
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.update"
|
||||
assert params["id"] == "job-1"
|
||||
assert params["wakeMode"] == "now"
|
||||
|
||||
|
||||
def test_cron_update_with_every_builds_canonical_schedule(stub_gateway) -> None:
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
cron=None,
|
||||
every="10m",
|
||||
at=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.update"
|
||||
assert params["schedule"] == {"kind": "every", "every_seconds": 600}
|
||||
assert "expression" not in params
|
||||
|
||||
|
||||
def test_cron_update_with_tz_only_sends_timezone_patch(stub_gateway) -> None:
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
cron=None,
|
||||
every=None,
|
||||
at=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz="Asia/Shanghai",
|
||||
wake=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.update"
|
||||
assert params == {"id": "job-1", "tz": "Asia/Shanghai"}
|
||||
|
||||
|
||||
def test_cron_update_rejects_multiple_schedule_sources(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="at most one"):
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression="*/5 * * * *",
|
||||
cron="0 9 * * *",
|
||||
every=None,
|
||||
at=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
|
||||
def test_cron_update_requires_at_least_one_field(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="at least one"):
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
failure_mode=None,
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
|
||||
|
||||
def test_cron_update_with_failure_destination_webhook(stub_gateway) -> None:
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
failure_mode="webhook",
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url="https://hooks.example/alert",
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert method == "cron.update"
|
||||
assert params["delivery"] == {
|
||||
"failureDestination": {
|
||||
"mode": "webhook",
|
||||
"webhookUrl": "https://hooks.example/alert",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_cron_update_with_failure_destination_channel(stub_gateway) -> None:
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
failure_mode="channel",
|
||||
failure_channel="slack",
|
||||
failure_to="C-ops",
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
method, params = stub_gateway.calls[-1]
|
||||
assert params["delivery"] == {
|
||||
"failureDestination": {
|
||||
"mode": "channel",
|
||||
"channelName": "slack",
|
||||
"to": "C-ops",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_cron_update_failure_webhook_missing_url(stub_gateway) -> None:
|
||||
with pytest.raises(typer.BadParameter, match="--failure-webhook-url"):
|
||||
cron_cmd.cron_update(
|
||||
job_id="job-1",
|
||||
expression=None,
|
||||
text=None,
|
||||
name=None,
|
||||
enabled=None,
|
||||
timeout=None,
|
||||
tz=None,
|
||||
wake=None,
|
||||
failure_mode="webhook",
|
||||
failure_channel=None,
|
||||
failure_to=None,
|
||||
failure_account=None,
|
||||
failure_webhook_url=None,
|
||||
failure_webhook_token=None,
|
||||
failure_webhook_token_env=None,
|
||||
failure_webhook_token_file=None,
|
||||
json_output=False,
|
||||
)
|
||||
Reference in New Issue
Block a user