mirror of
https://git.mirrors.martin98.com/https://github.com/bytedance/deer-flow
synced 2025-08-18 02:25:56 +08:00
feat(ut): add ut coverage check (#170)
This commit is contained in:
parent
a43db94fb6
commit
9cff113862
20
.github/workflows/unittest.yaml
vendored
20
.github/workflows/unittest.yaml
vendored
@ -23,7 +23,23 @@ jobs:
|
|||||||
uv pip install -e ".[dev]"
|
uv pip install -e ".[dev]"
|
||||||
uv pip install -e ".[test]"
|
uv pip install -e ".[test]"
|
||||||
|
|
||||||
- name: Run test cases
|
- name: Run test cases with coverage
|
||||||
run: |
|
run: |
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
TAVILY_API_KEY=mock-key make test
|
TAVILY_API_KEY=mock-key make coverage
|
||||||
|
|
||||||
|
- name: Generate HTML Coverage Report
|
||||||
|
run: |
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m coverage html -d coverage_html
|
||||||
|
|
||||||
|
- name: Upload Coverage Report
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-report
|
||||||
|
path: coverage_html/
|
||||||
|
|
||||||
|
- name: Display Coverage Summary
|
||||||
|
run: |
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m coverage report
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -21,3 +21,6 @@ conf.yaml
|
|||||||
.idea/
|
.idea/
|
||||||
.langgraph_api/
|
.langgraph_api/
|
||||||
|
|
||||||
|
# coverage report
|
||||||
|
coverage.xml
|
||||||
|
coverage/
|
||||||
|
2
Makefile
2
Makefile
@ -19,4 +19,4 @@ langgraph-dev:
|
|||||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
uv run pytest --cov=src tests/ --cov-report=term-missing
|
uv run pytest --cov=src tests/ --cov-report=term-missing --cov-report=xml
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://www.python.org/downloads/)
|
[](https://www.python.org/downloads/)
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://deepwiki.com/bytedance/deer-flow)
|
[](https://deepwiki.com/bytedance/deer-flow)
|
||||||
|
|
||||||
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
||||||
|
|
||||||
|
@ -52,6 +52,9 @@ filterwarnings = [
|
|||||||
"ignore::UserWarning",
|
"ignore::UserWarning",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
fail_under = 25
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src"]
|
packages = ["src"]
|
||||||
|
|
||||||
|
24
test_fix.py
Normal file
24
test_fix.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This script manually patches sys.modules to fix the LLM import issue
|
||||||
|
so that tests can run without requiring LLM configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
# Create mocks
|
||||||
|
mock_llm = MagicMock()
|
||||||
|
mock_llm.invoke.return_value = "Mock LLM response"
|
||||||
|
|
||||||
|
# Create a mock module for llm.py
|
||||||
|
mock_llm_module = MagicMock()
|
||||||
|
mock_llm_module.get_llm_by_type = lambda llm_type: mock_llm
|
||||||
|
mock_llm_module.basic_llm = mock_llm
|
||||||
|
mock_llm_module._create_llm_use_conf = lambda llm_type, conf: mock_llm
|
||||||
|
|
||||||
|
# Set the mock module
|
||||||
|
sys.modules["src.llms.llm"] = mock_llm_module
|
||||||
|
|
||||||
|
print("Successfully patched LLM module. You can now run your tests.")
|
||||||
|
print("Example: uv run pytest tests/test_types.py -v")
|
131
tests/test_state.py
Normal file
131
tests/test_state.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from typing import Annotated, List, Optional
|
||||||
|
|
||||||
|
# Import MessagesState directly from langgraph rather than through our application
|
||||||
|
from langgraph.graph import MessagesState
|
||||||
|
|
||||||
|
|
||||||
|
# Create stub versions of Plan/Step/StepType to avoid dependencies
|
||||||
|
class StepType:
|
||||||
|
RESEARCH = "research"
|
||||||
|
PROCESSING = "processing"
|
||||||
|
|
||||||
|
|
||||||
|
class Step:
|
||||||
|
def __init__(self, need_web_search, title, description, step_type):
|
||||||
|
self.need_web_search = need_web_search
|
||||||
|
self.title = title
|
||||||
|
self.description = description
|
||||||
|
self.step_type = step_type
|
||||||
|
|
||||||
|
|
||||||
|
class Plan:
|
||||||
|
def __init__(self, locale, has_enough_context, thought, title, steps):
|
||||||
|
self.locale = locale
|
||||||
|
self.has_enough_context = has_enough_context
|
||||||
|
self.thought = thought
|
||||||
|
self.title = title
|
||||||
|
self.steps = steps
|
||||||
|
|
||||||
|
|
||||||
|
# Import the actual State class by loading the module directly
|
||||||
|
# This avoids the cascade of imports that would normally happen
|
||||||
|
def load_state_class():
|
||||||
|
# Get the absolute path to the types.py file
|
||||||
|
src_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||||
|
types_path = os.path.join(src_dir, "graph", "types.py")
|
||||||
|
|
||||||
|
# Create a namespace for the module
|
||||||
|
import types
|
||||||
|
|
||||||
|
module_name = "src.graph.types_direct"
|
||||||
|
spec = types.ModuleType(module_name)
|
||||||
|
|
||||||
|
# Add the module to sys.modules to avoid import loops
|
||||||
|
sys.modules[module_name] = spec
|
||||||
|
|
||||||
|
# Set up the namespace with required imports
|
||||||
|
spec.__dict__["operator"] = __import__("operator")
|
||||||
|
spec.__dict__["Annotated"] = Annotated
|
||||||
|
spec.__dict__["MessagesState"] = MessagesState
|
||||||
|
spec.__dict__["Plan"] = Plan
|
||||||
|
|
||||||
|
# Execute the module code
|
||||||
|
with open(types_path, "r") as f:
|
||||||
|
module_code = f.read()
|
||||||
|
|
||||||
|
exec(module_code, spec.__dict__)
|
||||||
|
|
||||||
|
# Return the State class
|
||||||
|
return spec.State
|
||||||
|
|
||||||
|
|
||||||
|
# Load the actual State class
|
||||||
|
State = load_state_class()
|
||||||
|
|
||||||
|
|
||||||
|
def test_state_initialization():
|
||||||
|
"""Test that State class has correct default attribute definitions."""
|
||||||
|
# Test that the class has the expected attribute definitions
|
||||||
|
assert State.locale == "en-US"
|
||||||
|
assert State.observations == []
|
||||||
|
assert State.plan_iterations == 0
|
||||||
|
assert State.current_plan is None
|
||||||
|
assert State.final_report == ""
|
||||||
|
assert State.auto_accepted_plan is False
|
||||||
|
assert State.enable_background_investigation is True
|
||||||
|
assert State.background_investigation_results is None
|
||||||
|
|
||||||
|
# Verify state initialization
|
||||||
|
state = State(messages=[])
|
||||||
|
assert "messages" in state
|
||||||
|
|
||||||
|
# Without explicitly passing attributes, they're not in the state
|
||||||
|
assert "locale" not in state
|
||||||
|
assert "observations" not in state
|
||||||
|
|
||||||
|
|
||||||
|
def test_state_with_custom_values():
|
||||||
|
"""Test that State can be initialized with custom values."""
|
||||||
|
test_step = Step(
|
||||||
|
need_web_search=True,
|
||||||
|
title="Test Step",
|
||||||
|
description="Step description",
|
||||||
|
step_type=StepType.RESEARCH,
|
||||||
|
)
|
||||||
|
|
||||||
|
test_plan = Plan(
|
||||||
|
locale="en-US",
|
||||||
|
has_enough_context=False,
|
||||||
|
thought="Test thought",
|
||||||
|
title="Test Plan",
|
||||||
|
steps=[test_step],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize state with custom values and required messages field
|
||||||
|
state = State(
|
||||||
|
messages=[],
|
||||||
|
locale="fr-FR",
|
||||||
|
observations=["Observation 1"],
|
||||||
|
plan_iterations=2,
|
||||||
|
current_plan=test_plan,
|
||||||
|
final_report="Test report",
|
||||||
|
auto_accepted_plan=True,
|
||||||
|
enable_background_investigation=False,
|
||||||
|
background_investigation_results="Test results",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Access state keys - these are explicitly initialized
|
||||||
|
assert state["locale"] == "fr-FR"
|
||||||
|
assert state["observations"] == ["Observation 1"]
|
||||||
|
assert state["plan_iterations"] == 2
|
||||||
|
assert state["current_plan"].title == "Test Plan"
|
||||||
|
assert state["current_plan"].thought == "Test thought"
|
||||||
|
assert len(state["current_plan"].steps) == 1
|
||||||
|
assert state["current_plan"].steps[0].title == "Test Step"
|
||||||
|
assert state["final_report"] == "Test report"
|
||||||
|
assert state["auto_accepted_plan"] is True
|
||||||
|
assert state["enable_background_investigation"] is False
|
||||||
|
assert state["background_investigation_results"] == "Test results"
|
Loading…
x
Reference in New Issue
Block a user