Build & Deploy ResearchOwl / build-and-push (push) Successful in 10s
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>
110 lines
4.2 KiB
Python
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
|