Download tests/test_graph.py from codegood/job-chat: direct link, hf CLI and curl.
- Browser
- Download file 5.49 kB
-
https://huggingface.co/spaces/codegood/job-chat/resolve/main/tests/test_graph.py
- Command line
-
hf download hf://spaces/codegood/job-chat/tests/test_graph.py
-
curl -L -o test_graph.py https://huggingface.co/spaces/codegood/job-chat/resolve/main/tests/test_graph.py
5.49 kB
| """Routing and history trimming. Neither needs a model call.""" | |
| import pytest | |
| from langchain_core.messages import AIMessage, HumanMessage, trim_messages | |
| from config import ConfigError | |
| from graph import build_graph, latest_text, route_action | |
| def test_each_skill_button_routes_to_its_own_node(action, expected): | |
| assert route_action({"action": action}) == expected | |
| def test_missing_action_key_falls_back_to_chat(): | |
| assert route_action({}) == "answer" | |
| def test_graph_rejects_empty_profile_before_model_setup(): | |
| with pytest.raises(ConfigError, match="Upload profile documents"): | |
| build_graph(api_key="test-key", profile_data=" ") | |
| class StubRouter: | |
| def __init__(self, decision): | |
| self.decision = decision | |
| self.seen = [] | |
| def route(self, query): | |
| self.seen.append(query) | |
| return self.decision | |
| def test_free_chat_follows_the_semantic_router(decision, expected): | |
| state = {"action": "", "messages": [HumanMessage(content="a question")]} | |
| assert route_action(state, semantic_router=StubRouter(decision)) == expected | |
| def test_skill_buttons_bypass_the_router_and_keep_full_context(action): | |
| """A retrieval miss inside a review or cover letter would drop real evidence.""" | |
| stub = StubRouter("trivial") | |
| state = {"action": action, "messages": [HumanMessage(content="hello")]} | |
| assert route_action(state, semantic_router=stub) == f"skill_{action}" | |
| assert stub.seen == [] | |
| def test_the_ats_action_never_reaches_the_router(): | |
| """Retrieval could omit a skill and silently corrupt the score.""" | |
| stub = StubRouter("specific") | |
| state = {"action": "ats", "messages": [HumanMessage(content="hi")]} | |
| assert route_action(state, semantic_router=stub) == "ats_analysis" | |
| assert stub.seen == [] | |
| def test_the_router_sees_the_latest_message(): | |
| stub = StubRouter("full") | |
| state = {"action": "", "messages": [HumanMessage(content="old"), HumanMessage(content="newest")]} | |
| route_action(state, semantic_router=stub) | |
| assert stub.seen == ["newest"] | |
| class TestContentBlocks: | |
| """Checkpointed messages come back with list content, not a plain string. | |
| Reading .content directly crashed the first real conversation with | |
| 'list object has no attribute strip'. | |
| """ | |
| def test_latest_text_reads_plain_string_content(self): | |
| assert latest_text([HumanMessage(content="hello")]) == "hello" | |
| def test_latest_text_reads_block_content(self): | |
| blocks = [HumanMessage(content=[{"type": "text", "text": "hi there"}])] | |
| assert latest_text(blocks) == "hi there" | |
| def test_latest_text_returns_a_real_string(self): | |
| text = latest_text([HumanMessage(content=[{"type": "text", "text": "x"}])]) | |
| assert type(text) is str | |
| assert text.strip() == "x" | |
| def test_latest_text_handles_no_messages(self): | |
| assert latest_text([]) == "" | |
| def test_routing_survives_block_content(self): | |
| stub = StubRouter("specific") | |
| state = {"action": "", "messages": [HumanMessage(content=[{"type": "text", "text": "do I know Rust?"}])]} | |
| assert route_action(state, semantic_router=stub) == "rag_answer" | |
| assert stub.seen == ["do I know Rust?"] | |
| def test_router_rejects_non_string_queries_without_crashing(): | |
| from router import FULL, SemanticRouter | |
| from tests.test_router import REFERENCES, fake_embed | |
| router = SemanticRouter(fake_embed, references=REFERENCES) | |
| for bad in ([{"type": "text", "text": "hi"}], None, 42): | |
| assert router.route(bad) == FULL | |
| def conversation(turns): | |
| messages = [] | |
| for i in range(turns): | |
| messages.append(HumanMessage(content=f"q{i}")) | |
| messages.append(AIMessage(content=f"a{i}")) | |
| return messages | |
| def trim(messages, max_turns): | |
| return trim_messages( | |
| messages, | |
| max_tokens=max_turns * 2, | |
| token_counter=len, | |
| strategy="last", | |
| start_on="human", | |
| include_system=False, | |
| ) | |
| def test_history_never_exceeds_the_window(turns): | |
| assert len(trim(conversation(turns), max_turns=3)) <= 6 | |
| def test_short_history_is_left_alone(): | |
| messages = conversation(2) | |
| assert trim(messages, max_turns=5) == messages | |
| def test_trimming_keeps_the_most_recent_turns(): | |
| trimmed = trim(conversation(10), max_turns=2) | |
| assert trimmed[-1].content == "a9" | |
| assert "q0" not in [m.content for m in trimmed] | |
| def test_window_starts_on_a_human_message(): | |
| """Starting mid-exchange on an orphaned AI reply confuses the model.""" | |
| assert isinstance(trim(conversation(10), max_turns=3)[0], HumanMessage) | |
| def test_cost_plateaus_once_the_window_is_full(): | |
| sizes = [len(trim(conversation(t), max_turns=3)) for t in range(1, 11)] | |
| assert sizes[-1] == sizes[-2] == 6 | |