Some checks failed
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Infra CI / mc (push) Successful in 11s
Deploy / report (push) Successful in 12s
CI / test (push) Has been cancelled
CI / lint (push) Failing after 31s
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / mc (push) Has been skipped
Infra CI / notebooks (push) Failing after 12s
Infra CI / zotero (push) Successful in 12s
Infra CI / docs (push) Successful in 1m12s
Infra CI / api (push) Successful in 21s
Wires the existing idempotent `stack bib refresh-iom` command behind a systemd user timer (daily 03:30) and auto-files a Gitea issue when watch-iom detects a futurepdf.pdf SHA-256 change. - src/bib/iom_alert.py — file_iom_change_alert() opens an issue tagged 'cms-update', creating the label first if needed. - src/cli/bib.py — refresh-iom gains --file-issue/--no-file-issue (default True). Calls the alert helper when changed=True. - tests/bib/test_iom_alert.py — 4 unit tests. Systemd user units (host-specific, not in repo): - ~/.config/systemd/user/bib-refresh-iom.service - ~/.config/systemd/user/bib-refresh-iom.timer (OnCalendar=03:30) Out of scope: adaptive 6h-after-change scheduling; Slack alternative. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
67 lines
2.5 KiB
Python
67 lines
2.5 KiB
Python
"""Tests for bib.iom_alert — Gitea issue filing on futurepdf.pdf changes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from bib.iom_alert import _LABEL, file_iom_change_alert
|
|
|
|
|
|
def test_no_token_returns_none(monkeypatch):
|
|
monkeypatch.delenv("GITEA_TOKEN", raising=False)
|
|
assert file_iom_change_alert(sha256="deadbeef" * 8) is None
|
|
|
|
|
|
@patch("api.clients.gitea.GiteaClient")
|
|
def test_creates_label_then_files_issue(mock_client_cls):
|
|
client = MagicMock()
|
|
# Pretend label doesn't exist on first call
|
|
client.list_labels.return_value = []
|
|
client.resolve_labels.return_value = [42]
|
|
client.create_issue.return_value = {"number": 999, "html_url": "https://x"}
|
|
mock_client_cls.return_value = client
|
|
|
|
result = file_iom_change_alert(sha256="abc123", token="fake-token")
|
|
|
|
assert result == {"number": 999, "html_url": "https://x"}
|
|
# Label create attempted because list_labels returned empty
|
|
client.post.assert_called_once()
|
|
call_kwargs = client.post.call_args
|
|
assert call_kwargs[0][0] == "/repos/homelab/stack/labels"
|
|
assert call_kwargs[1]["json"]["name"] == _LABEL
|
|
# Issue created with title containing 'cms-update' + body with sha256
|
|
issue_payload = client.create_issue.call_args[0][2]
|
|
assert issue_payload["title"].startswith("cms-update:")
|
|
assert "abc123" in issue_payload["body"]
|
|
assert issue_payload["labels"] == [42]
|
|
|
|
|
|
@patch("api.clients.gitea.GiteaClient")
|
|
def test_skips_label_create_when_existing(mock_client_cls):
|
|
client = MagicMock()
|
|
client.list_labels.return_value = [{"name": _LABEL, "id": 7}]
|
|
client.resolve_labels.return_value = [7]
|
|
client.create_issue.return_value = {"number": 5}
|
|
mock_client_cls.return_value = client
|
|
|
|
file_iom_change_alert(sha256="x", token="t")
|
|
|
|
# Did NOT call POST to create the label
|
|
client.post.assert_not_called()
|
|
|
|
|
|
@patch("api.clients.gitea.GiteaClient")
|
|
def test_files_without_label_when_resolve_returns_empty(mock_client_cls):
|
|
"""resolve_labels silently skips unknown — issue should still file."""
|
|
client = MagicMock()
|
|
client.list_labels.return_value = [{"name": _LABEL, "id": 1}]
|
|
# Simulate resolve_labels returning [] (e.g. cache stale)
|
|
client.resolve_labels.return_value = []
|
|
client.create_issue.return_value = {"number": 10}
|
|
mock_client_cls.return_value = client
|
|
|
|
file_iom_change_alert(sha256="x", token="t")
|
|
|
|
issue_payload = client.create_issue.call_args[0][2]
|
|
assert "labels" not in issue_payload
|