"""Comprehensive tests for every core module. Runs with: python -m tests.test_comprehensive """ import os import sys import tempfile # Ensure stdout handles Unicode on Windows. if sys.stdout.encoding and sys.stdout.encoding.lower() not in ("utf-8", "utf8"): sys.stdout.reconfigure(encoding="utf-8", errors="replace") sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from core.config import Settings, get_settings, PROVIDER_ENDPOINTS, DEFAULT_MODELS from core.schemas import (CrawledPage, BrandBrain, Brief, FactPack, GuardrailReport, Variant, GenerationResult) from core.llm import LLMClient, _extract_json, LLMError from core import mock_backend from core import crawler from core import brand_brain as bb_mod from core import brief as brief_mod from core import fact_pack as fp_mod from core import writers as writers_mod from core import guardrails from core import ranker from core import ppt as ppt_mod from core.pipeline import ContentAgent passed = 0 failed = 0 def check(name: str, condition: bool, detail: str = ""): global passed, failed if condition: passed += 1 print(f" ✅ {name}") else: failed += 1 print(f" ❌ {name} — {detail}") # ──────────────────────── SCHEMAS ──────────────────────── def test_schemas(): print("\n── schemas ──") # CrawledPage p = CrawledPage(url="https://x.com", title="Home", headings=["H1"], text="body") check("CrawledPage fields", p.url == "https://x.com" and p.title == "Home") # BrandBrain bb = BrandBrain(product_name="Test") d = bb.to_dict() check("BrandBrain.to_dict", isinstance(d, dict) and d["product_name"] == "Test") check("BrandBrain defaults", bb.value_props == [] and bb.forbidden_claims == []) # Brief b = Brief(raw_input="note") check("Brief defaults", b.channels == ["linkedin", "instagram", "whatsapp"]) check("Brief.to_dict", "raw_input" in b.to_dict()) # FactPack.allowed_text fp = FactPack(approved_facts=["fact1"], approved_proof=["proof1"], user_update="update") check("FactPack.allowed_text", "fact1" in fp.allowed_text() and "update" in fp.allowed_text()) fp_empty = FactPack() check("FactPack empty allowed_text", fp_empty.allowed_text() == "") # GuardrailReport.all_flags gr = GuardrailReport(claim_flags=["c1"], lint_flags=["l1"], policy_flags=["p1"]) check("GuardrailReport.all_flags", len(gr.all_flags()) == 3) gr_clean = GuardrailReport() check("GuardrailReport clean passed", gr_clean.passed is True and gr_clean.all_flags() == []) # Variant v = Variant(channel="linkedin", text="hello") check("Variant defaults", v.score == 0.0 and v.guardrails.passed) # GenerationResult result = GenerationResult(brief=b, fact_pack=fp) check("GenerationResult", result.variants_by_channel == {} and result.brand_brain is None) # ──────────────────────── CONFIG ──────────────────────── def test_config(): print("\n── config ──") s = Settings() check("Settings defaults", s.provider == "groq") check("Settings.resolved_model", s.resolved_model() == DEFAULT_MODELS["groq"]) check("Settings.endpoint", s.endpoint() == PROVIDER_ENDPOINTS["groq"]) # Mock mode + no key → not live s_mock = Settings(mock_mode=True) check("mock_mode → not live", not s_mock.is_live()) # Live mode + key → live s_live = Settings(mock_mode=False, api_key="test-key-1234") check("live + key → live", s_live.is_live()) # to_dict masks key d = s_live.to_dict() check("to_dict masks key", d["api_key"] == "test…") # get_settings with overrides s2 = get_settings(mock_mode=True, provider="openrouter") check("get_settings overrides", s2.mock_mode is True and s2.provider == "openrouter") # Unknown provider falls back to groq defaults s3 = Settings(provider="unknown") check("unknown provider resolved_model fallback", s3.resolved_model() == DEFAULT_MODELS["groq"]) check("unknown provider endpoint fallback", s3.endpoint() == PROVIDER_ENDPOINTS["groq"]) # ──────────────────────── LLM / JSON EXTRACT ──────────────────────── def test_llm(): print("\n── llm ──") # _extract_json: plain JSON check("extract plain JSON", _extract_json('{"a": 1}') == {"a": 1}) # With code fences check("extract fenced JSON", _extract_json('```json\n{"b": 2}\n```') == {"b": 2}) # With surrounding text check("extract embedded JSON", _extract_json('Sure! Here is: {"c": 3} hope that helps') == {"c": 3}) # Array check("extract JSON array", _extract_json('[1, 2, 3]') == [1, 2, 3]) # Bad JSON raises try: _extract_json("not json at all") check("bad JSON raises", False, "should have raised") except LLMError: check("bad JSON raises", True) # LLMClient mock mode settings = Settings(mock_mode=True) client = LLMClient(settings) check("LLMClient.live is False in mock", not client.live) data = client.generate_json("suggest_prompts", "sys", "user", {}) check("LLMClient mock returns data", "suggestions" in data) # ──────────────────────── MOCK BACKEND ──────────────────────── def test_mock_backend(): print("\n── mock_backend ──") tasks = ["suggest_prompts", "build_brief", "brand_brain", "fact_pack", "write_linkedin", "write_instagram", "write_whatsapp", "ppt_outline"] for task in tasks: ctx = {"product_name": "TestProd", "raw_input": "test", "user_update": "test", "num_variants": 2} result = mock_backend.respond(task, ctx) check(f"mock {task}", isinstance(result, dict) and len(result) > 0, f"got: {type(result)}") # Unknown task → empty dict check("mock unknown task", mock_backend.respond("nonexistent", {}) == {}) # ──────────────────────── CRAWLER ──────────────────────── def test_crawler(): print("\n── crawler ──") settings = Settings(mock_mode=True) pages = crawler.crawl("https://example.com", settings) check("mock crawl returns pages", len(pages) == 3) check("mock crawl page has text", bool(pages[0].text)) check("mock crawl page has URL", "example.com" in pages[0].url) # _same_domain check("_same_domain match", crawler._same_domain("https://a.com/foo", "https://a.com/bar")) check("_same_domain mismatch", not crawler._same_domain("https://a.com", "https://b.com")) # ──────────────────────── BRAND BRAIN ──────────────────────── def test_brand_brain(): print("\n── brand_brain ──") settings = Settings(mock_mode=True) client = LLMClient(settings) pages = crawler._mock_site("https://acme.com") bb = bb_mod.build_brand_brain(pages, client) check("brand_brain product_name", bool(bb.product_name)) check("brand_brain value_props", len(bb.value_props) > 0) check("brand_brain source_urls", len(bb.source_urls) == len(pages)) check("brand_brain forbidden_claims", len(bb.forbidden_claims) > 0) # _guess_name check("_guess_name from URL", bb_mod._guess_name(pages) == "Acme") check("_guess_name empty", bb_mod._guess_name([]) == "YourProduct") # ──────────────────────── BRIEF ──────────────────────── def test_brief(): print("\n── brief ──") settings = Settings(mock_mode=True) client = LLMClient(settings) bb = BrandBrain(product_name="Test", one_liner="Test product", value_props=["fast"]) suggestions = brief_mod.suggest_prompts(bb, client) check("suggest_prompts returns list", isinstance(suggestions, list) and len(suggestions) >= 1) built = brief_mod.build_brief("shipped a feature", bb, client, channels=["linkedin"], num_variants=2) check("build_brief returns brief", "brief" in built and isinstance(built["brief"], Brief)) check("build_brief suggested_prompt", bool(built["suggested_prompt"])) check("build_brief channels override", built["brief"].channels == ["linkedin"]) check("build_brief num_variants", built["brief"].num_variants == 2) # ──────────────────────── FACT PACK ──────────────────────── def test_fact_pack(): print("\n── fact_pack ──") settings = Settings(mock_mode=True) client = LLMClient(settings) bb = BrandBrain(product_name="Test", one_liner="A test product", value_props=["fast"], features=["auto"]) brief = Brief(raw_input="launched today", objective="launch") fp = fp_mod.build_fact_pack(bb, brief, client) check("fact_pack approved_facts", len(fp.approved_facts) > 0) check("fact_pack cannot_claim", len(fp.cannot_claim) > 0) check("fact_pack user_update preserved", fp.user_update == "launched today") # ──────────────────────── WRITERS ──────────────────────── def test_writers(): print("\n── writers ──") settings = Settings(mock_mode=True) client = LLMClient(settings) fp = FactPack(approved_facts=["fact1"], approved_proof=["proof1"], user_update="shipped a feature") brief = Brief(raw_input="shipped a feature", num_variants=2) for channel in ["linkedin", "instagram", "whatsapp"]: variants = writers_mod.write_channel(channel, fp, brief, client) check(f"write_{channel} returns variants", isinstance(variants, list) and len(variants) == 2) check(f"write_{channel} variant has text", bool(variants[0].text)) check(f"write_{channel} variant.channel", variants[0].channel == channel) # ──────────────────────── GUARDRAILS ──────────────────────── def test_guardrails(): print("\n── guardrails ──") fp = FactPack(approved_facts=["We integrate with Slack"]) # Clean variant clean = Variant(channel="linkedin", text="We integrate with Slack for better collaboration.") report = guardrails.evaluate(clean, fp) check("clean variant passes", report.passed) # Unsupported claims bad_claims = Variant(channel="linkedin", text="We have 10000 users and are the #1 fastest guaranteed platform.") report2 = guardrails.evaluate(bad_claims, fp) check("bad claims flagged", not report2.passed) check("claim_flags populated", len(report2.claim_flags) > 0) # Specific claim patterns for text, expected_claims in [ ("50% faster", ["50%"]), ("guaranteed results", ["guaranteed"]), ("world's best", ["world's", "best"]), ("HIPAA compliant", ["compliant", "hipaa"]), ("$2M ARR", ["arr"]), ]: v = Variant(channel="linkedin", text=text) flags = guardrails.check_claims(v, fp) check(f"claim pattern: '{text}'", len(flags) > 0, f"flags={flags}") # Lint checks long_v = Variant(channel="whatsapp", text="x" * 800) lint_flags = guardrails.check_lints(long_v) check("lint: too long whatsapp", any("Too long" in f for f in lint_flags)) hashtag_v = Variant(channel="linkedin", text="hello", hashtags=["#" + str(i) for i in range(20)]) lint2 = guardrails.check_lints(hashtag_v) check("lint: too many hashtags", any("hashtags" in f.lower() for f in lint2)) empty_v = Variant(channel="linkedin", text=" ") lint3 = guardrails.check_lints(empty_v) check("lint: empty content", any("Empty" in f for f in lint3)) # Policy filter bad_policy = Variant(channel="linkedin", text="This is a scam product") policy_flags = guardrails.check_policy(bad_policy) check("policy: banned term", len(policy_flags) > 0) clean_policy = Variant(channel="linkedin", text="Great product for teams") check("policy: clean text", len(guardrails.check_policy(clean_policy)) == 0) # ──────────────────────── RANKER ──────────────────────── def test_ranker(): print("\n── ranker ──") fp = FactPack(approved_facts=["Ships integrations", "Built for lean teams"]) # Passed guardrails + CTA + good length → high score good = Variant(channel="linkedin", text="Ships integrations for lean teams. " * 5, cta="Try it now", guardrails=GuardrailReport(passed=True)) score = ranker.score_variant(good, fp) check("good variant score > 3", score > 3.0, f"score={score}") # Failed guardrails → low score bad = Variant(channel="linkedin", text="This is a game changer. Revolutionary and next level.", guardrails=GuardrailReport(passed=False, claim_flags=["flag1", "flag2"])) bad_score = ranker.score_variant(bad, fp) check("bad variant score < good", bad_score < score) # Generic phrases penalized generic = Variant(channel="linkedin", text="This is a game changer and cutting edge next level tool " * 3, guardrails=GuardrailReport(passed=True)) generic_score = ranker.score_variant(generic, fp) non_generic = Variant(channel="linkedin", text="Our integrations connect with Slack and Notion for faster workflows " * 3, guardrails=GuardrailReport(passed=True)) non_generic_score = ranker.score_variant(non_generic, fp) check("generic penalized vs non-generic", generic_score < non_generic_score, f"generic={generic_score}, non_generic={non_generic_score}") # Ranking sorts correctly variants = [bad, good, generic, non_generic] ranked = ranker.rank(variants, fp) check("rank returns sorted desc", ranked[0].score >= ranked[-1].score) # Very short text → low specificity short = Variant(channel="linkedin", text="Hello", guardrails=GuardrailReport(passed=True)) check("short text specificity = 0.3", ranker._specificity(short.text) == 0.3) # ──────────────────────── PPT ──────────────────────── def test_ppt(): print("\n── ppt ──") settings = Settings(mock_mode=True) client = LLMClient(settings) fp = FactPack(approved_facts=["fact1"], approved_proof=["proof1"], user_update="shipped integrations") brief = Brief(raw_input="shipped integrations", objective="daily_update") outline = ppt_mod.build_outline(fp, brief, client) check("ppt outline has title", "title" in outline) check("ppt outline has slides", len(outline.get("slides", [])) > 0) out_dir = tempfile.mkdtemp() out_path = os.path.join(out_dir, "test_deck.pptx") result_path = ppt_mod.render_pptx(outline, out_path) check("pptx file created", os.path.exists(result_path)) check("pptx file non-empty", os.path.getsize(result_path) > 0) # Render with empty outline empty_outline = {"title": "Empty", "subtitle": "", "slides": []} empty_path = os.path.join(out_dir, "empty_deck.pptx") ppt_mod.render_pptx(empty_outline, empty_path) check("empty outline renders", os.path.exists(empty_path)) # ──────────────────────── PIPELINE (INTEGRATION) ──────────────────────── def test_pipeline(): print("\n── pipeline (integration) ──") settings = get_settings(mock_mode=True) agent = ContentAgent(settings) # Full flow brand = agent.ingest_website("https://testproduct.io") check("pipeline: ingest", bool(brand.product_name)) suggestions = agent.suggest_prompts(brand) check("pipeline: suggestions", len(suggestions) >= 1) built = agent.build_brief("we added dark mode", brand, channels=["linkedin", "whatsapp"], num_variants=2) brief = built["brief"] check("pipeline: brief channels", brief.channels == ["linkedin", "whatsapp"]) result = agent.generate(brief, brand) check("pipeline: generate has channels", set(result.variants_by_channel.keys()) == {"linkedin", "whatsapp"}) for ch, variants in result.variants_by_channel.items(): check(f"pipeline: {ch} variants count", len(variants) == 2) check(f"pipeline: {ch} ranked (scores desc)", variants[0].score >= variants[-1].score) # PPT export out = os.path.join(tempfile.mkdtemp(), "pipeline_deck.pptx") path = agent.export_ppt(result.fact_pack, brief, out) check("pipeline: pptx exists", os.path.exists(path) and os.path.getsize(path) > 0) # ──────────────────────── RUN ALL ──────────────────────── def main(): test_schemas() test_config() test_llm() test_mock_backend() test_crawler() test_brand_brain() test_brief() test_fact_pack() test_writers() test_guardrails() test_ranker() test_ppt() test_pipeline() print(f"\n{'='*50}") print(f" Results: {passed} passed, {failed} failed") print(f"{'='*50}") if failed: sys.exit(1) else: print(" ALL COMPREHENSIVE TESTS PASSED ✅") if __name__ == "__main__": main()