Files
maigret/tests/test_mastodon_engine.py
T
Julio César SuásteguiandGitHub 334436fff1 Add a shared Mastodon engine with the accounts/lookup urlProbe (#3121)
Moves 77 Mastodon instances onto a shared engine instead of copying urlProbe into every entry. The lookup API answers 404 for a missing account, so twelve instances that until now returned 200 for any username stop reporting everyone as found.

Closes #3119. Supersedes #3115, #3116, #3117.
2026-09-11 18:31:17 +03:00

148 lines
4.9 KiB
Python

"""Shared Mastodon engine: lookup urlProbe lives on the engine, inside site."""
from unittest.mock import Mock
from maigret.checking import make_site_result
from maigret.sites import MaigretEngine, MaigretSite
# Hosts that use the same /@{username} profile URL shape but are not Mastodon.
# Verified by GET /api/v1/instance — they do not return an instance document.
URL_SHAPE_LOOKALIKES = ("TikTok", "Threads", "Figma")
# Pychess uses /@/{username} (note the extra slash) and is issue #3100 — leave it.
PYCHESS_URL = "https://www.pychess.org/@/{username}"
def _mastodon_engine() -> MaigretEngine:
return MaigretEngine(
"Mastodon",
{
"name": "Mastodon",
"site": {
"checkType": "status_code",
"url": "{urlMain}/@{username}",
"urlProbe": "{urlMain}/api/v1/accounts/lookup?acct={username}",
},
},
)
def test_engine_fields_live_inside_site():
"""update_from_engine copies engine.site only. A sibling key is ignored.
That is how 34 vBulletin sites ended up with empty presenseStrs: the
strings were declared next to "site", not inside it.
"""
engine = _mastodon_engine()
assert list(engine.site.keys()) == ["checkType", "url", "urlProbe"]
assert "urlProbe" not in {k for k in engine.json if k != "site"}
assert engine.site["url"] == "{urlMain}/@{username}"
assert engine.site["urlProbe"] == "{urlMain}/api/v1/accounts/lookup?acct={username}"
def test_site_inherits_lookup_urlprobe_from_engine():
site = MaigretSite(
"ExampleMastodon",
{
"engine": "Mastodon",
"urlMain": "https://example.social",
"usernameClaimed": "alice",
"usernameUnclaimed": "noonewouldeverusethis7",
},
)
site.update_from_engine(_mastodon_engine())
assert site.check_type == "status_code"
assert site.url == "{urlMain}/@{username}"
assert site.url_probe == "{urlMain}/api/v1/accounts/lookup?acct={username}"
stripped = site.strip_engine_data().json
assert stripped["engine"] == "Mastodon"
assert "url" not in stripped
assert "urlProbe" not in stripped
assert "checkType" not in stripped
def test_make_site_result_uses_lookup_probe_and_profile_url():
site = MaigretSite(
"ExampleMastodon",
{"urlMain": "https://example.social"},
)
site.update_from_engine(_mastodon_engine())
checker = Mock()
checker.prepare.return_value = None
results = make_site_result(
site,
"alice",
{
"id_type": "username",
"parsing": False,
"timeout": 1,
"forced": False,
"checkers": {"": lambda: checker},
},
Mock(),
)
assert results["url_user"] == "https://example.social/@alice"
assert (
results["url_probe"]
== "https://example.social/api/v1/accounts/lookup?acct=alice"
)
def test_shipped_engine_matches_template(default_db):
engine = default_db.engines_dict["Mastodon"]
assert engine.site == _mastodon_engine().site
# nothing inherited except through site — no stray top-level check fields
assert list(engine.site.keys()) == ["checkType", "url", "urlProbe"]
def test_confirmed_instances_use_engine(default_db):
mastodon_sites = [s for s in default_db.sites if s.engine == "Mastodon"]
assert len(mastodon_sites) >= 70
names = {s.name for s in mastodon_sites}
for expected in (
"mastodon.social",
"social.tchncs.de",
"Framapiaf",
"infosec.exchange",
"hachyderm.io",
"foxes.day",
"Fosstodon",
"fuzzies.wtf",
):
assert expected in names
site = default_db.sites_dict[expected]
assert site.check_type == "status_code"
assert site.url == "{urlMain}/@{username}"
assert site.url_probe == "{urlMain}/api/v1/accounts/lookup?acct={username}"
assert not site.url_main.endswith("/")
# disabled instance still moved so a later re-enable gets the probe
assert default_db.sites_dict["fuzzies.wtf"].disabled is True
# urlMain used to point at chaos.social; the engine templates need the
# host from the profile URL.
assert default_db.sites_dict["mastodon.social"].url_main == (
"https://mastodon.social"
)
def test_lookalikes_and_pychess_are_not_on_the_engine(default_db):
for name in URL_SHAPE_LOOKALIKES:
site = default_db.sites_dict[name]
assert site.engine != "Mastodon"
assert "/@{username}" in site.url
assert not (site.url_probe or "").endswith("accounts/lookup?acct={username}")
pychess = default_db.sites_dict["Pychess"]
assert pychess.engine != "Mastodon"
assert pychess.url == PYCHESS_URL
# unverified /api/v1/instance (TLS failure from this environment)
chaos = default_db.sites_dict["chaos.social"]
assert chaos.engine != "Mastodon"