CI / test (3.11) (push) Canceled after 0s
CI / test (3.12) (push) Canceled after 0s
CI / test (3.13) (push) Canceled after 0s
CI / test (3.14) (push) Canceled after 0s
CI / package (push) Canceled after 0s
CI / precommit-and-security (push) Canceled after 11s
CI / typecheck (push) Canceled after 11s
106 lines
3.2 KiB
Python
106 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
import json
|
|
from datetime import UTC, datetime
|
|
|
|
from schedls.models import (
|
|
Backend,
|
|
Command,
|
|
CronDetails,
|
|
JobSource,
|
|
Schedule,
|
|
ScheduledJob,
|
|
ScheduleKind,
|
|
Scope,
|
|
SystemdDetails,
|
|
)
|
|
from schedls.output import Output, job_to_dict, jobs_document, sanitize_text
|
|
|
|
|
|
def _systemd_job() -> ScheduledJob:
|
|
return ScheduledJob(
|
|
name="backup",
|
|
backend=Backend.SYSTEMD,
|
|
scope=Scope.USER,
|
|
managed=True,
|
|
enabled=True,
|
|
schedule=Schedule(ScheduleKind.CALENDAR, "*-*-* 02:00:00", ("*-*-* 02:00:00",)),
|
|
command=Command(argv=("/usr/local/bin/backup", "/srv/data")),
|
|
source=JobSource("systemd user timer", path="/home/u/.config/systemd/user/schedls-backup.timer"),
|
|
next_run=datetime(2026, 9, 25, 2, 0, tzinfo=UTC),
|
|
systemd=SystemdDetails(
|
|
timer_unit="schedls-backup.timer",
|
|
service_unit="schedls-backup.service",
|
|
persistent=True,
|
|
),
|
|
)
|
|
|
|
|
|
def _cron_job() -> ScheduledJob:
|
|
return ScheduledJob(
|
|
name="cron-3",
|
|
backend=Backend.CRON,
|
|
scope=Scope.USER,
|
|
managed=False,
|
|
enabled=None,
|
|
schedule=Schedule(ScheduleKind.CRON, "0 4 * * 0"),
|
|
command=Command(raw="/usr/local/bin/cleanup"),
|
|
source=JobSource("current user's crontab", line=3),
|
|
cron=CronDetails(expression="0 4 * * 0", line=3),
|
|
)
|
|
|
|
|
|
def test_job_to_dict_systemd() -> None:
|
|
data = job_to_dict(_systemd_job())
|
|
assert data["name"] == "backup"
|
|
assert data["backend"] == "systemd"
|
|
assert data["managed"] is True
|
|
assert data["next_run"].endswith("+00:00")
|
|
assert data["systemd"]["persistent"] is True
|
|
|
|
|
|
def test_job_to_dict_cron_missing_data_is_null() -> None:
|
|
data = job_to_dict(_cron_job())
|
|
assert data["next_run"] is None
|
|
assert data["last_run"] is None
|
|
assert data["enabled"] is None
|
|
|
|
|
|
def test_jobs_document_schema_and_no_ansi() -> None:
|
|
document = jobs_document([_systemd_job(), _cron_job()], ["a warning"])
|
|
assert document["schema_version"] == 1
|
|
assert len(document["jobs"]) == 2
|
|
dumped = json.dumps(document)
|
|
assert "\x1b" not in dumped
|
|
assert "backup" in dumped
|
|
|
|
|
|
def test_output_table_and_key_values() -> None:
|
|
stream = io.StringIO()
|
|
output = Output(json_mode=False, color="never", stdout=stream)
|
|
output.table(["NAME", "BACKEND"], [["backup", "systemd"], ["cleanup", "cron"]])
|
|
text = stream.getvalue()
|
|
assert "NAME" in text
|
|
assert "backup" in text
|
|
assert "\x1b" not in text
|
|
|
|
|
|
def test_output_json_mode() -> None:
|
|
stream = io.StringIO()
|
|
output = Output(json_mode=True, color="never", stdout=stream)
|
|
output.emit_json({"schema_version": 1, "jobs": []})
|
|
assert json.loads(stream.getvalue())["schema_version"] == 1
|
|
|
|
|
|
def test_sanitize_text_escapes_control_characters() -> None:
|
|
text = "a\x1b[31mred\x1b[0m\nb\tc\x00d\x7f"
|
|
assert sanitize_text(text) == "a\\x1b[31mred\\x1b[0m\nb\tc\\x00d\\x7f"
|
|
|
|
|
|
def test_output_write_is_verbatim() -> None:
|
|
stream = io.StringIO()
|
|
output = Output(color="never", stdout=stream)
|
|
output.write("line one\nline two")
|
|
assert stream.getvalue() == "line one\nline two"
|