mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-03 07:38:43 +02:00
test(cli): add tests for http.apps config field
Add tests for: - Validation of http.apps entries (prefix format, app path format) - _is_js_http_app helper for JS/TS and npm package detection - _has_js_http_apps helper for config-level JS app detection - Docker generation with Python-only http.apps (no Node install) - Docker generation with JS http.apps (triggers Node install) - Docker generation with npm package http.apps - Docker generation with mixed Python/JS http.apps Co-authored-by: Christian Bromann <christian-bromann@users.noreply.github.com>
This commit is contained in:
co-authored by
Christian Bromann
parent
468c7546c8
commit
8699989b1a
@@ -11,6 +11,8 @@ import pytest
|
||||
from langgraph_cli.config import (
|
||||
_BUILD_TOOLS,
|
||||
_get_pip_cleanup_lines,
|
||||
_has_js_http_apps,
|
||||
_is_js_http_app,
|
||||
config_to_compose,
|
||||
config_to_docker,
|
||||
default_base_image,
|
||||
@@ -150,6 +152,148 @@ def test_validate_config():
|
||||
)
|
||||
|
||||
|
||||
def test_validate_config_http_apps():
|
||||
"""Test validation of http.apps field."""
|
||||
# Valid http.apps config
|
||||
config = validate_config(
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": ["."],
|
||||
"graphs": {"agent": "./agent.py:graph"},
|
||||
"http": {
|
||||
"apps": {
|
||||
"/dashboard": "./dashboard.py:app",
|
||||
"/ext": "./extension.js:app",
|
||||
"/conduit": "some-npm-package:app",
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
assert config["http"]["apps"] == {
|
||||
"/dashboard": "./dashboard.py:app",
|
||||
"/ext": "./extension.js:app",
|
||||
"/conduit": "some-npm-package:app",
|
||||
}
|
||||
|
||||
# Valid with both app and apps
|
||||
config = validate_config(
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": ["."],
|
||||
"graphs": {"agent": "./agent.py:graph"},
|
||||
"http": {
|
||||
"app": "./custom_routes.py:app",
|
||||
"apps": {
|
||||
"/dashboard": "./dashboard.py:app",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
assert config["http"]["app"] == "./custom_routes.py:app"
|
||||
assert config["http"]["apps"] == {"/dashboard": "./dashboard.py:app"}
|
||||
|
||||
# Invalid prefix (doesn't start with /)
|
||||
with pytest.raises(ValueError, match="Invalid http.apps prefix"):
|
||||
validate_config(
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": ["."],
|
||||
"graphs": {"agent": "./agent.py:graph"},
|
||||
"http": {"apps": {"dashboard": "./dashboard.py:app"}},
|
||||
}
|
||||
)
|
||||
|
||||
# Invalid app path (no colon separator)
|
||||
with pytest.raises(ValueError, match="Invalid http.apps format"):
|
||||
validate_config(
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": ["."],
|
||||
"graphs": {"agent": "./agent.py:graph"},
|
||||
"http": {"apps": {"/dashboard": "./dashboard.py"}},
|
||||
}
|
||||
)
|
||||
|
||||
# Empty apps dict is fine (no-op)
|
||||
config = validate_config(
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": ["."],
|
||||
"graphs": {"agent": "./agent.py:graph"},
|
||||
"http": {"apps": {}},
|
||||
}
|
||||
)
|
||||
assert config["http"]["apps"] == {}
|
||||
|
||||
# None apps is fine
|
||||
config = validate_config(
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": ["."],
|
||||
"graphs": {"agent": "./agent.py:graph"},
|
||||
"http": {"apps": None},
|
||||
}
|
||||
)
|
||||
assert config["http"]["apps"] is None
|
||||
|
||||
|
||||
def test_is_js_http_app():
|
||||
"""Test _is_js_http_app detection logic."""
|
||||
# Python files are not JS
|
||||
assert not _is_js_http_app("./dashboard.py:app")
|
||||
assert not _is_js_http_app("./app.pyx:app")
|
||||
|
||||
# JS/TS files are JS
|
||||
assert _is_js_http_app("./extension.js:app")
|
||||
assert _is_js_http_app("./extension.ts:app")
|
||||
assert _is_js_http_app("./extension.mts:app")
|
||||
assert _is_js_http_app("./extension.mjs:app")
|
||||
assert _is_js_http_app("./extension.cts:app")
|
||||
assert _is_js_http_app("./extension.cjs:app")
|
||||
|
||||
# npm package references (bare module names) are JS
|
||||
assert _is_js_http_app("some-npm-package:app")
|
||||
assert _is_js_http_app("@scope/package:app")
|
||||
|
||||
# File paths without extensions that start with . or / are not JS
|
||||
assert not _is_js_http_app("./mymodule:app")
|
||||
|
||||
|
||||
def test_has_js_http_apps():
|
||||
"""Test _has_js_http_apps config detection."""
|
||||
# No http config
|
||||
assert not _has_js_http_apps({})
|
||||
|
||||
# No apps field
|
||||
assert not _has_js_http_apps({"http": {}})
|
||||
|
||||
# Empty apps
|
||||
assert not _has_js_http_apps({"http": {"apps": {}}})
|
||||
|
||||
# Python-only apps
|
||||
assert not _has_js_http_apps(
|
||||
{"http": {"apps": {"/dashboard": "./dashboard.py:app"}}}
|
||||
)
|
||||
|
||||
# JS apps
|
||||
assert _has_js_http_apps({"http": {"apps": {"/ext": "./extension.js:app"}}})
|
||||
|
||||
# npm package
|
||||
assert _has_js_http_apps({"http": {"apps": {"/conduit": "some-npm-package:app"}}})
|
||||
|
||||
# Mixed
|
||||
assert _has_js_http_apps(
|
||||
{
|
||||
"http": {
|
||||
"apps": {
|
||||
"/dashboard": "./dashboard.py:app",
|
||||
"/ext": "./extension.js:app",
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_validate_config_image_distro():
|
||||
"""Test validation of image_distro field."""
|
||||
# Valid image_distro values should work
|
||||
@@ -491,6 +635,111 @@ WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
}
|
||||
|
||||
|
||||
def test_config_to_docker_with_http_apps_python():
|
||||
"""Test Docker generation with http.apps containing Python apps."""
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
actual_docker_stdin, additional_contexts = config_to_docker(
|
||||
PATH_TO_CONFIG,
|
||||
validate_config(
|
||||
{
|
||||
"dependencies": [".", "../../examples"],
|
||||
"graphs": graphs,
|
||||
"http": {
|
||||
"apps": {
|
||||
"/dashboard": "../../examples/my_app.py:app",
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
base_image="langchain/langgraph-api",
|
||||
)
|
||||
# Python http.apps paths should be rewritten to container paths
|
||||
assert "LANGGRAPH_HTTP" in actual_docker_stdin
|
||||
assert "/deps/examples/my_app.py:app" in actual_docker_stdin
|
||||
assert "/dashboard" in actual_docker_stdin
|
||||
# No Node.js installation for Python-only apps
|
||||
assert "install-node" not in actual_docker_stdin
|
||||
|
||||
|
||||
def test_config_to_docker_with_http_apps_js():
|
||||
"""Test Docker generation with http.apps containing JS apps triggers Node install."""
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
# Use npm package references to avoid needing actual JS files on disk
|
||||
actual_docker_stdin, _ = config_to_docker(
|
||||
PATH_TO_CONFIG,
|
||||
validate_config(
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": graphs,
|
||||
"http": {
|
||||
"apps": {
|
||||
"/ext": "my-js-package:app",
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
base_image="langchain/langgraph-api",
|
||||
)
|
||||
# JS http.apps should trigger Node.js installation
|
||||
assert "install-node" in actual_docker_stdin
|
||||
assert "NODE_VERSION=20" in actual_docker_stdin
|
||||
assert "LANGGRAPH_HTTP" in actual_docker_stdin
|
||||
|
||||
|
||||
def test_config_to_docker_with_http_apps_npm_package():
|
||||
"""Test Docker generation with http.apps containing npm package triggers Node install."""
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
actual_docker_stdin, _ = config_to_docker(
|
||||
PATH_TO_CONFIG,
|
||||
validate_config(
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": graphs,
|
||||
"http": {
|
||||
"apps": {
|
||||
"/conduit": "some-npm-package:app",
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
base_image="langchain/langgraph-api",
|
||||
)
|
||||
# npm package should trigger Node.js installation
|
||||
assert "install-node" in actual_docker_stdin
|
||||
assert "NODE_VERSION=20" in actual_docker_stdin
|
||||
assert "LANGGRAPH_HTTP" in actual_docker_stdin
|
||||
# npm package path should not be rewritten
|
||||
assert "some-npm-package:app" in actual_docker_stdin
|
||||
|
||||
|
||||
def test_config_to_docker_with_http_apps_mixed():
|
||||
"""Test Docker generation with mixed Python and JS http.apps."""
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
actual_docker_stdin, _ = config_to_docker(
|
||||
PATH_TO_CONFIG,
|
||||
validate_config(
|
||||
{
|
||||
"dependencies": [".", "../../examples"],
|
||||
"graphs": graphs,
|
||||
"http": {
|
||||
"app": "../../examples/my_app.py:app",
|
||||
"apps": {
|
||||
"/dashboard": "../../examples/my_app.py:app",
|
||||
"/conduit": "some-npm-package:app",
|
||||
},
|
||||
},
|
||||
}
|
||||
),
|
||||
base_image="langchain/langgraph-api",
|
||||
)
|
||||
# Should have both Python path rewriting and Node.js installation
|
||||
assert "install-node" in actual_docker_stdin
|
||||
assert "NODE_VERSION=20" in actual_docker_stdin
|
||||
assert "LANGGRAPH_HTTP" in actual_docker_stdin
|
||||
# Python paths should be rewritten
|
||||
assert "/deps/examples/my_app.py:app" in actual_docker_stdin
|
||||
|
||||
|
||||
def test_config_to_docker_outside_path():
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
actual_docker_stdin, additional_contexts = config_to_docker(
|
||||
|
||||
Reference in New Issue
Block a user