#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Tests for fb_probe.py's two secret-handling seams: the key loader (R-453) and redact().""" import os import sys import tempfile import unittest sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import fb_probe # noqa: E402 from read_credential import CredentialError # noqa: E402 FAKE = "EAAfakeprobe0123456789abcdef" def creds(text): fd, p = tempfile.mkstemp() with os.fdopen(fd, "w") as fh: fh.write(text) return p class LoadKey(unittest.TestCase): def load(self, text): p = creds(text) try: return fb_probe.load_key(p) finally: os.unlink(p) def test_single_quotes(self): self.assertEqual(self.load("OTHER='x'\nFACEBOOK_API='%s'\n" % FAKE), FAKE) def test_double_quotes_and_export(self): self.assertEqual(self.load('export FACEBOOK_API="%s"\n' % FAKE), FAKE) def test_trailing_whitespace_stripped(self): self.assertEqual(self.load("FACEBOOK_API='%s' \n" % FAKE), FAKE) def test_mismatched_quote_refused(self): with self.assertRaises(CredentialError): self.load("FACEBOOK_API='%s\"\n" % FAKE) def test_inner_whitespace_refused(self): with self.assertRaises(CredentialError): self.load("FACEBOOK_API='EAA abc'\n") def test_not_a_meta_token_refused(self): with self.assertRaises(CredentialError): self.load("FACEBOOK_API='xyz123'\n") def test_missing_key_refused(self): with self.assertRaises(CredentialError): self.load("FACEBOOK_APIX='%s'\n" % FAKE) class Redact(unittest.TestCase): def test_access_token_key_dropped_everywhere(self): out = fb_probe.redact({"data": [{"id": "1", "access_token": "zzz"}], "access_token": "y"}, secrets=[]) self.assertEqual(out, {"data": [{"id": "1"}]}) def test_held_secret_and_token_shape_replaced(self): out = fb_probe.redact({"a": "pre-sekret-post", "b": "x " + FAKE, "c": "fine"}, secrets=["sekret"]) self.assertEqual(out, {"a": "[REDACTED]", "b": "[REDACTED]", "c": "fine"}) def test_hungarian_text_survives(self): self.assertEqual(fb_probe.redact(fb_probe.TEST_TEXT, secrets=[]), fb_probe.TEST_TEXT) class GoneError(unittest.TestCase): def test_measured_code_10_does_not_exist(self): self.assertTrue(fb_probe.gone_error({"code": 10, "message": "(#10) Object does not exist, cannot be loaded due to missing permission"})) def test_code_100(self): self.assertTrue(fb_probe.gone_error({"code": 100, "message": "Unsupported get request"})) def test_other_code_10_is_not_removal(self): self.assertFalse(fb_probe.gone_error({"code": 10, "message": "(#10) Application does not have permission"})) def test_success_body_is_not_removal(self): self.assertFalse(fb_probe.gone_error(None)) # ---------------------------------------------------------------- schedule-post (R-917) class ScheduleForm(unittest.TestCase): """The guard that matters: this command can only SCHEDULE. The operator's review in Planner is the safety net, so an immediate post must be unreachable — not merely undefaulted.""" def test_published_is_always_false(self): f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600) self.assertEqual(f["published"], "false") def test_no_argument_can_publish_immediately(self): # every keyword the function accepts, tried: none of them flips `published` import inspect names = [p for p in inspect.signature(fb_probe.schedule_form).parameters if p not in ("message", "when")] for name in names: f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600, **{name: "true"}) self.assertEqual(f["published"], "false", "%s changed published" % name) def test_link_is_included_by_default(self): f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600) self.assertEqual(f["link"], "https://felhom.eu/") def test_link_can_be_dropped(self): f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600, link=None) self.assertNotIn("link", f) class ScheduleWindow(unittest.TestCase): NOW = 1_760_000_000 def test_too_soon_is_refused(self): with self.assertRaises(ValueError): fb_probe.check_when(self.NOW + 9 * 60, now=self.NOW) def test_in_the_past_is_refused(self): with self.assertRaises(ValueError): fb_probe.check_when(self.NOW - 60, now=self.NOW) def test_too_far_is_refused(self): with self.assertRaises(ValueError): fb_probe.check_when(self.NOW + fb_probe.SCHED_MAX_S + 60, now=self.NOW) def test_ten_minutes_exactly_is_allowed(self): self.assertEqual(fb_probe.check_when(self.NOW + 600, now=self.NOW), self.NOW + 600) def test_a_normal_time_is_allowed(self): self.assertEqual(fb_probe.check_when(self.NOW + 86400, now=self.NOW), self.NOW + 86400) def test_schedule_form_refuses_a_bad_time_too(self): # the guard is not only on check_when: the form builder must not assemble a bad post with self.assertRaises(ValueError): fb_probe.schedule_form("x", 0) class CopySection(unittest.TestCase): """The post body is READ FROM COPY.md, never passed through a shell (brief 9.6).""" def test_reads_the_fenced_block(self): txt = fb_probe.read_copy_section(fb_probe.COPY_MD, "6.1 Rövid változat") self.assertIn("felhom.eu/kapcsolat", txt) self.assertFalse(txt.startswith("```")) self.assertFalse(txt.endswith(chr(10))) def test_sections_differ(self): a = fb_probe.read_copy_section(fb_probe.COPY_MD, "6.1 Rövid változat") b = fb_probe.read_copy_section(fb_probe.COPY_MD, "6.2 Közepes változat") self.assertNotEqual(a, b) self.assertGreater(len(b), len(a)) def test_missing_section_raises(self): with self.assertRaises(ValueError): fb_probe.read_copy_section(fb_probe.COPY_MD, "9.9 nincs ilyen") class ScheduleRedaction(unittest.TestCase): def test_a_token_in_a_scheduled_form_is_redacted(self): f = fb_probe.schedule_form("see " + FAKE, fb_probe.time.time() + 3600) self.assertNotIn(FAKE, fb_probe.redact(f, secrets=[FAKE])["message"]) def _has_budapest_tz(): try: from zoneinfo import ZoneInfo ZoneInfo("Europe/Budapest") return True except Exception: return False @unittest.skipUnless(_has_budapest_tz(), "no Europe/Budapest in this interpreter's tz database (Windows ships none; " "the command runs on the Linux host, which has the system zoneinfo)") class BudapestTime(unittest.TestCase): def test_summer_and_winter_offsets_differ(self): # CEST in July (+02:00), CET in January (+01:00) — the tz database, not a guessed offset jul = fb_probe.budapest_to_epoch("2026-07-01 19:00") jan = fb_probe.budapest_to_epoch("2026-01-01 19:00") import datetime self.assertEqual(datetime.datetime.utcfromtimestamp(jul).hour, 17) self.assertEqual(datetime.datetime.utcfromtimestamp(jan).hour, 18) def test_bad_format_raises(self): with self.assertRaises(ValueError): fb_probe.budapest_to_epoch("tomorrow evening") if __name__ == "__main__": unittest.main()