from goodnews.db import connect, init_db from goodnews import enrich def _setup(tmp_path): c = connect(str(tmp_path / "t.db")); init_db(c) c.execute("INSERT INTO sources (id,name,feed_url) VALUES (1,'S','http://s/f')") return c def _add(c, aid, *, image=None, accepted=1, dup=None, summarized=True): c.execute( "INSERT INTO articles (id,source_id,canonical_url,title,url_hash,image_url,duplicate_of,published_at) " "VALUES (?,1,?,?,?,?,?,datetime('now'))", (aid, f"http://s/{aid}", f"T{aid}", f"h{aid}", image, dup), ) c.execute("INSERT INTO article_scores (article_id,accepted) VALUES (?,?)", (aid, accepted)) if summarized: c.execute("INSERT INTO article_summaries (article_id,summary) VALUES (?, 's')", (aid,)) c.commit() def test_enrich_article_image_fills_missing(tmp_path): c = _setup(tmp_path); _add(c, 1, image=None) got = enrich.enrich_article_image(c, 1, fetch=lambda url: "http://img/og.jpg") assert got is True assert c.execute("SELECT image_url FROM articles WHERE id=1").fetchone()[0] == "http://img/og.jpg" # image_checked_at is stamped so we don't re-fetch endlessly assert c.execute("SELECT image_checked_at FROM articles WHERE id=1").fetchone()[0] is not None def test_enrich_skips_when_image_present(tmp_path): c = _setup(tmp_path); _add(c, 1, image="http://existing/img.jpg") calls = [] got = enrich.enrich_article_image(c, 1, fetch=lambda url: calls.append(url) or "http://new.jpg") assert got is False and calls == [] # never fetched assert c.execute("SELECT image_url FROM articles WHERE id=1").fetchone()[0] == "http://existing/img.jpg" def test_enrich_records_checked_even_when_none_found(tmp_path): c = _setup(tmp_path); _add(c, 1, image=None) got = enrich.enrich_article_image(c, 1, fetch=lambda url: None) assert got is False # checked_at stamped → won't retry until the retry window passes assert c.execute("SELECT image_checked_at FROM articles WHERE id=1").fetchone()[0] is not None again = enrich.enrich_article_image(c, 1, fetch=lambda url: "http://late.jpg", retry_days=7) assert again is False # still within retry window def test_prune_clears_only_broken_images(tmp_path): c = _setup(tmp_path) _add(c, 1, image="http://good/a.jpg") _add(c, 2, image="http://broken/b.jpg") _add(c, 3, image=None) # nothing to check # Pretend only the 'good' host loads. cleared = enrich.prune_broken_images(c, check=lambda u: "good" in u) assert cleared == 1 assert c.execute("SELECT image_url FROM articles WHERE id=1").fetchone()[0] == "http://good/a.jpg" assert c.execute("SELECT image_url FROM articles WHERE id=2").fetchone()[0] is None def test_backfill_only_targets_summarized_accepted_imageless(tmp_path): c = _setup(tmp_path) _add(c, 1, image=None, summarized=True, accepted=1) # eligible _add(c, 2, image=None, summarized=False, accepted=1) # no summary → skip _add(c, 3, image=None, summarized=True, accepted=0) # rejected → skip _add(c, 4, image="http://has.jpg", summarized=True) # has image → skip n = enrich.enrich_summarized_images(c, fetch=lambda url: "http://og.jpg", limit=50) assert n == 1 assert c.execute("SELECT image_url FROM articles WHERE id=2").fetchone()[0] is None def test_image_dimensions_parses_headers(): import struct from goodnews import enrich png = b"\x89PNG\r\n\x1a\n" + b"\x00\x00\x00\x0d" + b"IHDR" + struct.pack(">II", 1200, 630) assert enrich._image_dimensions(png) == (1200, 630) gif = b"GIF89a" + struct.pack("