2026-02-28 14:38:15 +08:00
""" Live integration tests for DeerFlowClient with real API.
These tests require a working config . yaml with valid API credentials .
They are skipped in CI and must be run explicitly :
PYTHONPATH = . uv run pytest tests / test_client_live . py - v - s
"""
import json
import os
from pathlib import Path
import pytest
refactor: split backend into harness (deerflow.*) and app (app.*) (#1131)
* refactor: extract shared utils to break harness→app cross-layer imports
Move _validate_skill_frontmatter to src/skills/validation.py and
CONVERTIBLE_EXTENSIONS + convert_file_to_markdown to src/utils/file_conversion.py.
This eliminates the two reverse dependencies from client.py (harness layer)
into gateway/routers/ (app layer), preparing for the harness/app package split.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: split backend/src into harness (deerflow.*) and app (app.*)
Physically split the monolithic backend/src/ package into two layers:
- **Harness** (`packages/harness/deerflow/`): publishable agent framework
package with import prefix `deerflow.*`. Contains agents, sandbox, tools,
models, MCP, skills, config, and all core infrastructure.
- **App** (`app/`): unpublished application code with import prefix `app.*`.
Contains gateway (FastAPI REST API) and channels (IM integrations).
Key changes:
- Move 13 harness modules to packages/harness/deerflow/ via git mv
- Move gateway + channels to app/ via git mv
- Rename all imports: src.* → deerflow.* (harness) / app.* (app layer)
- Set up uv workspace with deerflow-harness as workspace member
- Update langgraph.json, config.example.yaml, all scripts, Docker files
- Add build-system (hatchling) to harness pyproject.toml
- Add PYTHONPATH=. to gateway startup commands for app.* resolution
- Update ruff.toml with known-first-party for import sorting
- Update all documentation to reflect new directory structure
Boundary rule enforced: harness code never imports from app.
All 429 tests pass. Lint clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add harness→app boundary check test and update docs
Add test_harness_boundary.py that scans all Python files in
packages/harness/deerflow/ and fails if any `from app.*` or
`import app.*` statement is found. This enforces the architectural
rule that the harness layer never depends on the app layer.
Update CLAUDE.md to document the harness/app split architecture,
import conventions, and the boundary enforcement test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add config versioning with auto-upgrade on startup
When config.example.yaml schema changes, developers' local config.yaml
files can silently become outdated. This adds a config_version field and
auto-upgrade mechanism so breaking changes (like src.* → deerflow.*
renames) are applied automatically before services start.
- Add config_version: 1 to config.example.yaml
- Add startup version check warning in AppConfig.from_file()
- Add scripts/config-upgrade.sh with migration registry for value replacements
- Add `make config-upgrade` target
- Auto-run config-upgrade in serve.sh and start-daemon.sh before starting services
- Add config error hints in service failure messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix comments
* fix: update src.* import in test_sandbox_tools_security to deerflow.*
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle empty config and search parent dirs for config.example.yaml
Address Copilot review comments on PR #1131:
- Guard against yaml.safe_load() returning None for empty config files
- Search parent directories for config.example.yaml instead of only
looking next to config.yaml, fixing detection in common setups
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correct skills root path depth and config_version type coercion
- loader.py: fix get_skills_root_path() to use 5 parent levels (was 3)
after harness split, file lives at packages/harness/deerflow/skills/
so parent×3 resolved to backend/packages/harness/ instead of backend/
- app_config.py: coerce config_version to int() before comparison in
_check_config_version() to prevent TypeError when YAML stores value
as string (e.g. config_version: "1")
- tests: add regression tests for both fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update test imports from src.* to deerflow.*/app.* after harness refactor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:55:52 +08:00
from deerflow . client import DeerFlowClient , StreamEvent
2026-03-01 12:36:09 +08:00
2026-02-28 14:38:15 +08:00
# Skip entire module in CI or when no config.yaml exists
_skip_reason = None
if os . environ . get ( " CI " ) :
_skip_reason = " Live tests skipped in CI "
elif not Path ( __file__ ) . resolve ( ) . parents [ 2 ] . joinpath ( " config.yaml " ) . exists ( ) :
_skip_reason = " No config.yaml found — live tests require valid API credentials "
if _skip_reason :
pytest . skip ( _skip_reason , allow_module_level = True )
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
@pytest.fixture ( scope = " module " )
def client ( ) :
""" Create a real DeerFlowClient (no mocks). """
return DeerFlowClient ( thinking_enabled = False )
@pytest.fixture
def thread_tmp ( tmp_path ) :
""" Provide a unique thread_id + tmp directory for file operations. """
import uuid
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
tid = f " live-test- { uuid . uuid4 ( ) . hex [ : 8 ] } "
return tid , tmp_path
# ===========================================================================
# Scenario 1: Basic chat — model responds coherently
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveBasicChat :
def test_chat_returns_nonempty_string ( self , client ) :
""" chat() returns a non-empty response from the real model. """
response = client . chat ( " Reply with exactly: HELLO " )
assert isinstance ( response , str )
assert len ( response ) > 0
print ( f " chat response: { response } " )
def test_chat_follows_instruction ( self , client ) :
""" Model can follow a simple instruction. """
response = client . chat ( " What is 7 * 8? Reply with just the number. " )
assert " 56 " in response
print ( f " math response: { response } " )
# ===========================================================================
# Scenario 2: Streaming — events arrive in correct order
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveStreaming :
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
def test_stream_yields_messages_tuple_and_end ( self , client ) :
""" stream() produces at least one messages-tuple event and ends with end. """
2026-02-28 14:38:15 +08:00
events = list ( client . stream ( " Say hi in one word. " ) )
types = [ e . type for e in events ]
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert " messages-tuple " in types , f " Expected ' messages-tuple ' event, got: { types } "
assert " values " in types , f " Expected ' values ' event, got: { types } "
assert types [ - 1 ] == " end "
2026-02-28 14:38:15 +08:00
for e in events :
assert isinstance ( e , StreamEvent )
print ( f " [ { e . type } ] { e . data } " )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
def test_stream_ai_content_nonempty ( self , client ) :
""" Streamed messages-tuple AI events contain non-empty content. """
2026-03-07 21:07:21 +08:00
ai_messages = [ e for e in client . stream ( " What color is the sky? One word. " ) if e . type == " messages-tuple " and e . data . get ( " type " ) == " ai " and e . data . get ( " content " ) ]
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert len ( ai_messages ) > = 1
for m in ai_messages :
2026-02-28 14:38:15 +08:00
assert len ( m . data . get ( " content " , " " ) ) > 0
# ===========================================================================
# Scenario 3: Tool use — agent calls a tool and returns result
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveToolUse :
def test_agent_uses_bash_tool ( self , client ) :
""" Agent uses bash tool when asked to run a command. """
2026-03-07 21:07:21 +08:00
events = list ( client . stream ( " Use the bash tool to run: echo ' LIVE_TEST_OK ' . Then tell me the output. " ) )
2026-02-28 14:38:15 +08:00
types = [ e . type for e in events ]
print ( f " event types: { types } " )
for e in events :
print ( f " [ { e . type } ] { e . data } " )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
# All message events are now messages-tuple
mt_events = [ e for e in events if e . type == " messages-tuple " ]
tc_events = [ e for e in mt_events if e . data . get ( " type " ) == " ai " and " tool_calls " in e . data ]
tr_events = [ e for e in mt_events if e . data . get ( " type " ) == " tool " ]
ai_events = [ e for e in mt_events if e . data . get ( " type " ) == " ai " and e . data . get ( " content " ) ]
2026-02-28 14:38:15 +08:00
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert len ( tc_events ) > = 1 , f " Expected tool_call event, got types: { types } "
assert len ( tr_events ) > = 1 , f " Expected tool result event, got types: { types } "
assert len ( ai_events ) > = 1
2026-02-28 14:38:15 +08:00
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert tc_events [ 0 ] . data [ " tool_calls " ] [ 0 ] [ " name " ] == " bash "
assert " LIVE_TEST_OK " in tr_events [ 0 ] . data [ " content " ]
2026-02-28 14:38:15 +08:00
def test_agent_uses_ls_tool ( self , client ) :
""" Agent uses ls tool to list a directory. """
2026-03-07 21:07:21 +08:00
events = list ( client . stream ( " Use the ls tool to list the contents of /mnt/user-data/workspace. Just report what you see. " ) )
2026-02-28 14:38:15 +08:00
types = [ e . type for e in events ]
print ( f " event types: { types } " )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
tc_events = [ e for e in events if e . type == " messages-tuple " and e . data . get ( " type " ) == " ai " and " tool_calls " in e . data ]
assert len ( tc_events ) > = 1
assert tc_events [ 0 ] . data [ " tool_calls " ] [ 0 ] [ " name " ] == " ls "
2026-02-28 14:38:15 +08:00
# ===========================================================================
# Scenario 4: Multi-tool chain — agent chains tools in sequence
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveMultiToolChain :
def test_write_then_read ( self , client ) :
""" Agent writes a file, then reads it back. """
2026-03-07 21:07:21 +08:00
events = list ( client . stream ( " Step 1: Use write_file to write ' integration_test_content ' to /mnt/user-data/outputs/live_test.txt. Step 2: Use read_file to read that file back. Step 3: Tell me the content you read. " ) )
2026-02-28 14:38:15 +08:00
types = [ e . type for e in events ]
print ( f " event types: { types } " )
for e in events :
print ( f " [ { e . type } ] { e . data } " )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
tc_events = [ e for e in events if e . type == " messages-tuple " and e . data . get ( " type " ) == " ai " and " tool_calls " in e . data ]
tool_names = [ tc . data [ " tool_calls " ] [ 0 ] [ " name " ] for tc in tc_events ]
2026-02-28 14:38:15 +08:00
assert " write_file " in tool_names , f " Expected write_file, got: { tool_names } "
assert " read_file " in tool_names , f " Expected read_file, got: { tool_names } "
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
# Final AI message or tool result should mention the content
ai_events = [ e for e in events if e . type == " messages-tuple " and e . data . get ( " type " ) == " ai " and e . data . get ( " content " ) ]
tr_events = [ e for e in events if e . type == " messages-tuple " and e . data . get ( " type " ) == " tool " ]
final_text = ai_events [ - 1 ] . data [ " content " ] if ai_events else " "
2026-03-07 21:07:21 +08:00
assert " integration_test_content " in final_text . lower ( ) or any ( " integration_test_content " in e . data . get ( " content " , " " ) for e in tr_events )
2026-02-28 14:38:15 +08:00
# ===========================================================================
# Scenario 5: File upload lifecycle with real filesystem
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveFileUpload :
def test_upload_list_delete ( self , client , thread_tmp ) :
""" Upload → list → delete → verify deletion. """
thread_id , tmp_path = thread_tmp
# Create test files
f1 = tmp_path / " test_upload_a.txt "
f1 . write_text ( " content A " )
f2 = tmp_path / " test_upload_b.txt "
f2 . write_text ( " content B " )
# Upload
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
result = client . upload_files ( thread_id , [ f1 , f2 ] )
assert result [ " success " ] is True
assert len ( result [ " files " ] ) == 2
filenames = { r [ " filename " ] for r in result [ " files " ] }
2026-02-28 14:38:15 +08:00
assert filenames == { " test_upload_a.txt " , " test_upload_b.txt " }
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
for r in result [ " files " ] :
assert int ( r [ " size " ] ) > 0
2026-02-28 14:38:15 +08:00
assert r [ " virtual_path " ] . startswith ( " /mnt/user-data/uploads/ " )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert " artifact_url " in r
2026-02-28 14:38:15 +08:00
print ( f " uploaded: { filenames } " )
# List
listed = client . list_uploads ( thread_id )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert listed [ " count " ] == 2
print ( f " listed: { [ f [ ' filename ' ] for f in listed [ ' files ' ] ] } " )
2026-02-28 14:38:15 +08:00
# Delete one
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
del_result = client . delete_upload ( thread_id , " test_upload_a.txt " )
assert del_result [ " success " ] is True
2026-02-28 14:38:15 +08:00
remaining = client . list_uploads ( thread_id )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert remaining [ " count " ] == 1
assert remaining [ " files " ] [ 0 ] [ " filename " ] == " test_upload_b.txt "
print ( f " after delete: { [ f [ ' filename ' ] for f in remaining [ ' files ' ] ] } " )
2026-02-28 14:38:15 +08:00
# Delete the other
client . delete_upload ( thread_id , " test_upload_b.txt " )
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
empty = client . list_uploads ( thread_id )
assert empty [ " count " ] == 0
assert empty [ " files " ] == [ ]
2026-02-28 14:38:15 +08:00
def test_upload_nonexistent_file_raises ( self , client ) :
with pytest . raises ( FileNotFoundError ) :
client . upload_files ( " t-fail " , [ " /nonexistent/path/file.txt " ] )
# ===========================================================================
# Scenario 6: Configuration query — real config loading
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveConfigQueries :
2026-03-03 21:32:01 +08:00
def test_list_models_returns_configured_model ( self , client ) :
""" list_models() returns at least one configured model with Gateway-aligned fields. """
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
result = client . list_models ( )
assert " models " in result
assert len ( result [ " models " ] ) > = 1
names = [ m [ " name " ] for m in result [ " models " ] ]
# Verify Gateway-aligned fields
for m in result [ " models " ] :
assert " display_name " in m
assert " supports_thinking " in m
2026-02-28 14:38:15 +08:00
print ( f " models: { names } " )
def test_get_model_found ( self , client ) :
2026-03-03 21:32:01 +08:00
""" get_model() returns details for the first configured model. """
result = client . list_models ( )
first_model_name = result [ " models " ] [ 0 ] [ " name " ]
model = client . get_model ( first_model_name )
2026-02-28 14:38:15 +08:00
assert model is not None
2026-03-03 21:32:01 +08:00
assert model [ " name " ] == first_model_name
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
assert " display_name " in model
assert " supports_thinking " in model
2026-02-28 14:38:15 +08:00
print ( f " model detail: { model } " )
def test_get_model_not_found ( self , client ) :
assert client . get_model ( " nonexistent-model-xyz " ) is None
def test_list_skills ( self , client ) :
""" list_skills() runs without error. """
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
result = client . list_skills ( )
assert " skills " in result
assert isinstance ( result [ " skills " ] , list )
print ( f " skills count: { len ( result [ ' skills ' ] ) } " )
for s in result [ " skills " ] [ : 3 ] :
2026-02-28 14:38:15 +08:00
print ( f " - { s [ ' name ' ] } : { s [ ' enabled ' ] } " )
# ===========================================================================
# Scenario 7: Artifact read after agent writes
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveArtifact :
def test_get_artifact_after_write ( self , client ) :
""" Agent writes a file → client reads it back via get_artifact(). """
import uuid
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
thread_id = f " live-artifact- { uuid . uuid4 ( ) . hex [ : 8 ] } "
# Ask agent to write a file
2026-03-07 21:07:21 +08:00
events = list (
client . stream (
' Use write_file to create /mnt/user-data/outputs/artifact_test.json with content: { " status " : " ok " , " source " : " live_test " } ' ,
thread_id = thread_id ,
)
)
2026-02-28 14:38:15 +08:00
# Verify write happened
test: add Gateway conformance tests for DeerFlowClient (#931)
Validate that all dict-returning client methods conform to Gateway
Pydantic response models (ModelsListResponse, ModelResponse,
SkillsListResponse, SkillResponse, SkillInstallResponse,
McpConfigResponse, UploadResponse, MemoryConfigResponse,
MemoryStatusResponse). Pydantic ValidationError in CI catches
schema drift between client and Gateway with zero production coupling.
Also includes prior review fixes: enhanced client methods, expanded
unit tests (67→77), live integration test improvements, and updated
documentation.
Co-authored-by: greatmengqi <chenmengqi.0376@bytedance.com>
2026-02-28 16:08:04 +08:00
tc_events = [ e for e in events if e . type == " messages-tuple " and e . data . get ( " type " ) == " ai " and " tool_calls " in e . data ]
2026-03-07 21:07:21 +08:00
assert any ( any ( tc [ " name " ] == " write_file " for tc in e . data [ " tool_calls " ] ) for e in tc_events )
2026-02-28 14:38:15 +08:00
# Read artifact
content , mime = client . get_artifact ( thread_id , " mnt/user-data/outputs/artifact_test.json " )
data = json . loads ( content )
assert data [ " status " ] == " ok "
assert data [ " source " ] == " live_test "
assert " json " in mime
print ( f " artifact: { data } , mime: { mime } " )
def test_get_artifact_not_found ( self , client ) :
with pytest . raises ( FileNotFoundError ) :
client . get_artifact ( " nonexistent-thread " , " mnt/user-data/outputs/nope.txt " )
# ===========================================================================
# Scenario 8: Per-call overrides
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveOverrides :
def test_thinking_disabled_still_works ( self , client ) :
""" Explicit thinking_enabled=False override produces a response. """
response = client . chat (
2026-03-07 21:07:21 +08:00
" Say OK. " ,
thinking_enabled = False ,
2026-02-28 14:38:15 +08:00
)
assert len ( response ) > 0
print ( f " response: { response } " )
# ===========================================================================
# Scenario 9: Error resilience
# ===========================================================================
2026-03-07 21:07:21 +08:00
2026-02-28 14:38:15 +08:00
class TestLiveErrorResilience :
def test_delete_nonexistent_upload ( self , client ) :
with pytest . raises ( FileNotFoundError ) :
client . delete_upload ( " nonexistent-thread " , " ghost.txt " )
def test_bad_artifact_path ( self , client ) :
with pytest . raises ( ValueError ) :
client . get_artifact ( " t " , " invalid/path " )
def test_path_traversal_blocked ( self , client ) :
with pytest . raises ( PermissionError ) :
client . delete_upload ( " t " , " ../../etc/passwd " )