Add Linkki broadcast image
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8
context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; }
name:ph… (push) Successful in 48s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18
POSTGIS_VERSION=3
VCHORD_VERSION=0.5.3
context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 55s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3
context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 54s
Build custom container images / build (map[base_image:python:3.12-slim build_args:PYTHON_VERSION=3.12-slim
context:linkki-tiedotus fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:linkki-tiedotus oci_labels:… (push) Successful in 39s
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8
context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; }
name:ph… (push) Successful in 48s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18
POSTGIS_VERSION=3
VCHORD_VERSION=0.5.3
context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 55s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3
context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 54s
Build custom container images / build (map[base_image:python:3.12-slim build_args:PYTHON_VERSION=3.12-slim
context:linkki-tiedotus fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:linkki-tiedotus oci_labels:… (push) Successful in 39s
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
from linkki_poster.broadcast_ops import (
|
||||
DiscussionResolutionStats,
|
||||
apply_discussion_group_pinning,
|
||||
build_discussion_resolution_detail,
|
||||
prune_binding_after_deletions,
|
||||
)
|
||||
from linkki_poster.models import Config, Language, LanguageBinding, PublishBinding, TextSegmentBinding
|
||||
from linkki_poster.telegram_api import TelegramApiError
|
||||
|
||||
|
||||
def test_discussion_resolution_detail_mentions_missing_group_updates() -> None:
|
||||
detail = build_discussion_resolution_detail(
|
||||
DiscussionResolutionStats(
|
||||
updates_seen=4,
|
||||
discussion_group_updates=0,
|
||||
automatic_forward_updates=0,
|
||||
linked_channel_forward_updates=0,
|
||||
),
|
||||
expected_count=2,
|
||||
matched_count=0,
|
||||
)
|
||||
|
||||
assert "matched 0/2" in detail
|
||||
assert "none were from the configured discussion group" in detail
|
||||
|
||||
|
||||
def test_discussion_resolution_detail_mentions_linked_channel_forwards() -> None:
|
||||
detail = build_discussion_resolution_detail(
|
||||
DiscussionResolutionStats(
|
||||
updates_seen=6,
|
||||
discussion_group_updates=5,
|
||||
automatic_forward_updates=3,
|
||||
linked_channel_forward_updates=2,
|
||||
),
|
||||
expected_count=3,
|
||||
matched_count=2,
|
||||
)
|
||||
|
||||
assert "matched 2/3" in detail
|
||||
assert "saw 2 automatic forwards from the linked channel" in detail
|
||||
|
||||
|
||||
def test_discussion_resolution_detail_mentions_manual_linking_for_old_posts() -> None:
|
||||
detail = build_discussion_resolution_detail(
|
||||
DiscussionResolutionStats(
|
||||
updates_seen=0,
|
||||
discussion_group_updates=0,
|
||||
automatic_forward_updates=0,
|
||||
linked_channel_forward_updates=0,
|
||||
),
|
||||
expected_count=3,
|
||||
matched_count=0,
|
||||
)
|
||||
|
||||
assert "they must be linked manually" in detail
|
||||
|
||||
|
||||
def test_apply_discussion_group_pinning_skips_service_message_cleanup_when_disabled(monkeypatch) -> None:
|
||||
calls = []
|
||||
|
||||
class FakeTelegramAPI:
|
||||
def __init__(self, bot_token: str):
|
||||
self.bot_token = bot_token
|
||||
|
||||
def pin_chat_message(self, chat_id: str, message_id: int, disable_notification: bool = True) -> bool:
|
||||
calls.append(("pin", chat_id, message_id, disable_notification))
|
||||
return True
|
||||
|
||||
def unpin_chat_message(self, chat_id: str, message_id: int) -> bool:
|
||||
calls.append(("unpin", chat_id, message_id))
|
||||
return True
|
||||
|
||||
def fail_if_delete_called(**kwargs):
|
||||
raise AssertionError("delete_pin_service_message should not be called")
|
||||
|
||||
monkeypatch.setattr("linkki_poster.broadcast_ops.TelegramAPI", FakeTelegramAPI)
|
||||
monkeypatch.setattr("linkki_poster.broadcast_ops.delete_pin_service_message", fail_if_delete_called)
|
||||
|
||||
binding = PublishBinding(
|
||||
global_message_ids=[],
|
||||
global_attached_to_language=None,
|
||||
languages={
|
||||
Language.FI: LanguageBinding(
|
||||
language=Language.FI,
|
||||
image_message_ids=[],
|
||||
text_segments=[TextSegmentBinding(message_id=101, mode="message")],
|
||||
primary_message_id=101,
|
||||
primary_message_url="https://t.me/c/123/101",
|
||||
discussion_message_id=201,
|
||||
),
|
||||
Language.SV: LanguageBinding(
|
||||
language=Language.SV,
|
||||
image_message_ids=[],
|
||||
text_segments=[TextSegmentBinding(message_id=102, mode="message")],
|
||||
primary_message_id=102,
|
||||
primary_message_url="https://t.me/c/123/102",
|
||||
discussion_message_id=202,
|
||||
),
|
||||
},
|
||||
discussion_message_ids={101: 201, 102: 202},
|
||||
)
|
||||
|
||||
result = apply_discussion_group_pinning(
|
||||
Config(bot_token="token", chat_id="-1001", channel_username="channel", discussion_group_id="-2001"),
|
||||
binding,
|
||||
[Language.FI, Language.SV],
|
||||
"first",
|
||||
delete_service_messages=False,
|
||||
)
|
||||
|
||||
assert result.binding == binding
|
||||
assert result.pinning_failed is False
|
||||
assert calls == [
|
||||
("pin", "-2001", 201, True),
|
||||
("unpin", "-2001", 202),
|
||||
]
|
||||
|
||||
|
||||
def test_prune_binding_after_deletions_keeps_remaining_channel_links() -> None:
|
||||
binding = PublishBinding(
|
||||
global_message_ids=[],
|
||||
global_attached_to_language=None,
|
||||
languages={
|
||||
Language.FI: LanguageBinding(
|
||||
language=Language.FI,
|
||||
image_message_ids=[],
|
||||
text_segments=[TextSegmentBinding(message_id=101, mode="message")],
|
||||
primary_message_id=101,
|
||||
primary_message_url="https://t.me/c/123/101",
|
||||
discussion_message_id=201,
|
||||
),
|
||||
Language.SV: LanguageBinding(
|
||||
language=Language.SV,
|
||||
image_message_ids=[],
|
||||
text_segments=[TextSegmentBinding(message_id=102, mode="message")],
|
||||
primary_message_id=102,
|
||||
primary_message_url="https://t.me/c/123/102",
|
||||
discussion_message_id=202,
|
||||
),
|
||||
},
|
||||
discussion_message_ids={101: 201, 102: 202},
|
||||
)
|
||||
|
||||
remaining = prune_binding_after_deletions(
|
||||
binding,
|
||||
deleted_channel_ids=set(),
|
||||
deleted_discussion_channel_ids={101},
|
||||
deleted_discussion_message_ids={201},
|
||||
chat_id="-100123",
|
||||
channel_username="",
|
||||
)
|
||||
|
||||
assert remaining is not None
|
||||
assert remaining.languages[Language.FI].text_segments[0].message_id == 101
|
||||
assert remaining.languages[Language.SV].text_segments[0].message_id == 102
|
||||
assert remaining.discussion_message_ids == {102: 202}
|
||||
|
||||
|
||||
def test_apply_discussion_group_pinning_reports_permission_failures(monkeypatch) -> None:
|
||||
class FakeTelegramAPI:
|
||||
def __init__(self, bot_token: str):
|
||||
self.bot_token = bot_token
|
||||
|
||||
def pin_chat_message(self, chat_id: str, message_id: int, disable_notification: bool = True) -> bool:
|
||||
raise TelegramApiError("Bad Request: not enough rights to manage pinned messages in the chat")
|
||||
|
||||
def unpin_chat_message(self, chat_id: str, message_id: int) -> bool:
|
||||
raise TelegramApiError("Bad Request: not enough rights to manage pinned messages in the chat")
|
||||
|
||||
monkeypatch.setattr("linkki_poster.broadcast_ops.TelegramAPI", FakeTelegramAPI)
|
||||
|
||||
binding = PublishBinding(
|
||||
global_message_ids=[],
|
||||
global_attached_to_language=None,
|
||||
languages={
|
||||
Language.FI: LanguageBinding(
|
||||
language=Language.FI,
|
||||
image_message_ids=[],
|
||||
text_segments=[TextSegmentBinding(message_id=101, mode="message")],
|
||||
primary_message_id=101,
|
||||
primary_message_url="https://t.me/c/123/101",
|
||||
discussion_message_id=201,
|
||||
),
|
||||
Language.SV: LanguageBinding(
|
||||
language=Language.SV,
|
||||
image_message_ids=[],
|
||||
text_segments=[TextSegmentBinding(message_id=102, mode="message")],
|
||||
primary_message_id=102,
|
||||
primary_message_url="https://t.me/c/123/102",
|
||||
discussion_message_id=202,
|
||||
),
|
||||
},
|
||||
discussion_message_ids={101: 201, 102: 202},
|
||||
)
|
||||
|
||||
result = apply_discussion_group_pinning(
|
||||
Config(bot_token="token", chat_id="-1001", channel_username="channel", discussion_group_id="-2001"),
|
||||
binding,
|
||||
[Language.FI, Language.SV],
|
||||
"first",
|
||||
)
|
||||
|
||||
assert result.binding == binding
|
||||
assert result.pinning_failed is True
|
||||
Reference in New Issue
Block a user