Files
researchowl/tests/test_seo_autofill.py
T
ChemaVXandClaude Opus 4.8 6029bebae5
Build & Deploy ResearchOwl / build-and-push (push) Successful in 10s
fix(seo): el canónico del ES es el APEX, no www
Cada enlace interno que el generador escribía en un borrador español apuntaba
a www.zonadeexclusion.com y se comía un 301. El canónico de los dos blogs está
INVERTIDO —EN es www, ES es el apex—, la misma inversión que provocó el 522 y
de la que ya avisa el wrapper ghst-es.

No es teórico: el enlace www suelto que apareció en los-villares salió de aquí.
El corpus ES está limpio hoy, pero el próximo /generate con enlaces lo habría
reintroducido. Nada río abajo lo paraba: seo_watch solo ve el enlace DESPUÉS de
publicar, y ningún test cubría el host.

Añado los dos tests que faltaban, uno por idioma, para fijar la inversión. El
del ES falla contra el valor viejo con el href entero en el mensaje.

Sin tocar rules.py, así que el vendor-sync sigue en verde. Queda documentado el
matiz que este cambio NO arregla: rules.internal_links cuenta cero enlaces en
ES bajo cualquiera de los dos hosts, porque ese módulo está clavado al host EN
(se vendoriza byte a byte y la CI lo verifica). Inofensivo hoy —
internal_links.too_few no bloquea en borrador.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 15:47:39 +00:00

110 lines
4.2 KiB
Python

from src.seo.autofill import (ALLOWED_TAGS, DEFAULT_TAG, _coerce, _system_prompt,
insert_internal_links)
BASE = {
"meta_title": "t",
"meta_description": "d",
"custom_excerpt": "e",
"image_query": "q",
"image_context": "c",
}
def test_coerce_es_drops_invented_tags():
obj = dict(BASE, tags=["uap", "humanoides", "Desclasificados", "investigacion-2"])
out = _coerce(obj, "es")
assert out["tags"] == ["uap", "desclasificados"]
def test_coerce_es_falls_back_to_default():
obj = dict(BASE, tags=["pentagono", "encuentros-cercanos"])
out = _coerce(obj, "es")
assert out["tags"] == [DEFAULT_TAG["es"]]
def test_coerce_en_still_constrained():
obj = dict(BASE, tags=["uap", "investigacion"])
out = _coerce(obj, "en")
assert out["tags"] == ["uap"]
def test_system_prompt_lists_allowed_tags_per_lang():
es = _system_prompt("es")
en = _system_prompt("en")
for tag in ALLOWED_TAGS["es"]:
assert tag in es
# La regla anti-legacy 'never use "investigacion"' es solo para EN: en ES
# "investigacion" es el tag canónico del allow-list.
assert 'never use "investigacion"' in en
assert 'never use "investigacion"' not in es
assert "ONLY from this exact list" in es
# ─── topic collision ─────────────────────────────────────────────────────────
from src.seo.autofill import collision_notice, _slugify_title
CORPUS = [
{"id": "1", "status": "published", "slug": "kecksburg-1965-acorn-ufo-missing-nasa-files",
"title": 'Kecksburg 1965: The Acorn-Shaped Object, the Missing NASA Files, and "Pennsylvania\'s Roswell"'},
{"id": "2", "status": "scheduled", "slug": "uss-russell-2019-pyramid-uap-channel-islands",
"title": "USS Russell 2019: The Pyramid UAP Video and the Channel Islands Drone Swarm"},
]
def test_collision_fires_on_same_case_and_year():
note = collision_notice("Kecksburg 1965: New Acorn Evidence", CORPUS)
assert note is not None
assert "kecksburg" in note.lower()
assert "1965" in note
def test_collision_none_on_distinct_case():
assert collision_notice("Tehran 1976: The Jet-Disabling Encounter", CORPUS) is None
def test_collision_none_on_empty_corpus():
assert collision_notice("Kecksburg 1965: Anything", []) is None
def test_collision_note_is_markdown_safe():
corpus = [{"id": "9", "status": "published", "slug": "weird-1990-case",
"title": "Weird *1990* [Case] with_underscores and `ticks`"}]
note = collision_notice("Weird 1990: Case Revisited", corpus)
assert note is not None
# las entidades Markdown de títulos ajenos se sanean (solo quedan las nuestras)
bullets = [line for line in note.split("\n") if line.startswith("• ")]
assert bullets
for line in bullets:
for ch in "*_`[]":
assert ch not in line
def test_slugify_title():
assert _slugify_title("USS Russell 2019: The Pyramid UAP!") == "uss-russell-2019-the-pyramid-uap"
# --- canonical host per language -------------------------------------------
# EN canonicalizes on www, ES on the APEX. They are INVERTED, and the ES entry
# said "www." until 2026-07-21, so every internal link written into a Spanish
# draft ate a 301. Nothing caught it: no test covered the host, and seo_watch
# only sees a link once the post is published. These two pin it.
def test_internal_link_uses_es_apex_canonical():
html = "<p>El caso de Manises sigue abierto.</p>"
out, pairs = insert_internal_links(
html, [{"phrase": "Manises", "slug": "manises-1979"}],
[{"slug": "manises-1979", "title": "Manises"}], "es")
assert 'href="https://zonadeexclusion.com/manises-1979/"' in out
assert "www.zonadeexclusion.com" not in out
assert len(pairs) == 1
def test_internal_link_uses_en_www_canonical():
html = "<p>The Roswell debris was recovered.</p>"
out, pairs = insert_internal_links(
html, [{"phrase": "Roswell", "slug": "roswell-1947"}],
[{"slug": "roswell-1947", "title": "Roswell"}], "en")
assert 'href="https://www.theexclusionzone.com/roswell-1947/"' in out
assert len(pairs) == 1