mirror of
https://github.com/soxoj/maigret.git
synced 2026-09-22 17:54:55 +02:00
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.
148 lines
4.9 KiB
Python
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"
|