mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-07-27 00:06:37 +00:00
* Support for async execution functions This commit adds support for node execution functions defined as async. When a node's execution function is defined as async, we can continue executing other nodes while it is processing. Standard uses of `await` should "just work", but people will still have to be careful if they spawn actual threads. Because torch doesn't really have async/await versions of functions, this won't particularly help with most locally-executing nodes, but it does work for e.g. web requests to other machines. In addition to the execute function, the `VALIDATE_INPUTS` and `check_lazy_status` functions can also be defined as async, though we'll only resolve one node at a time right now for those. * Add the execution model tests to CI * Add a missing file It looks like this got caught by .gitignore? There's probably a better place to put it, but I'm not sure what that is. * Add the websocket library for automated tests * Add additional tests for async error cases Also fixes one bug that was found when an async function throws an error after being scheduled on a task. * Add a feature flags message to reduce bandwidth We now only send 1 preview message of the latest type the client can support. We'll add a console warning when the client fails to send a feature flags message at some point in the future. * Add async tests to CI * Don't actually add new tests in this PR Will do it in a separate PR * Resolve unit test in GPU-less runner * Just remove the tests that GHA can't handle * Change line endings to UNIX-style * Avoid loading model_management.py so early Because model_management.py has a top-level `logging.info`, we have to be careful not to import that file before we call `setup_logging`. If we do, we end up having the default logging handler registered in addition to our custom one.
99 lines
3.9 KiB
Python
99 lines
3.9 KiB
Python
"""Tests for feature flags functionality."""
|
|
|
|
from comfy_api.feature_flags import (
|
|
get_connection_feature,
|
|
supports_feature,
|
|
get_server_features,
|
|
SERVER_FEATURE_FLAGS,
|
|
)
|
|
|
|
|
|
class TestFeatureFlags:
|
|
"""Test suite for feature flags functions."""
|
|
|
|
def test_get_server_features_returns_copy(self):
|
|
"""Test that get_server_features returns a copy of the server flags."""
|
|
features = get_server_features()
|
|
# Verify it's a copy by modifying it
|
|
features["test_flag"] = True
|
|
# Original should be unchanged
|
|
assert "test_flag" not in SERVER_FEATURE_FLAGS
|
|
|
|
def test_get_server_features_contains_expected_flags(self):
|
|
"""Test that server features contain expected flags."""
|
|
features = get_server_features()
|
|
assert "supports_preview_metadata" in features
|
|
assert features["supports_preview_metadata"] is True
|
|
assert "max_upload_size" in features
|
|
assert isinstance(features["max_upload_size"], (int, float))
|
|
|
|
def test_get_connection_feature_with_missing_sid(self):
|
|
"""Test getting feature for non-existent session ID."""
|
|
sockets_metadata = {}
|
|
result = get_connection_feature(sockets_metadata, "missing_sid", "some_feature")
|
|
assert result is False # Default value
|
|
|
|
def test_get_connection_feature_with_custom_default(self):
|
|
"""Test getting feature with custom default value."""
|
|
sockets_metadata = {}
|
|
result = get_connection_feature(
|
|
sockets_metadata, "missing_sid", "some_feature", default="custom_default"
|
|
)
|
|
assert result == "custom_default"
|
|
|
|
def test_get_connection_feature_with_feature_flags(self):
|
|
"""Test getting feature from connection with feature flags."""
|
|
sockets_metadata = {
|
|
"sid1": {
|
|
"feature_flags": {
|
|
"supports_preview_metadata": True,
|
|
"custom_feature": "value",
|
|
},
|
|
}
|
|
}
|
|
result = get_connection_feature(sockets_metadata, "sid1", "supports_preview_metadata")
|
|
assert result is True
|
|
|
|
result = get_connection_feature(sockets_metadata, "sid1", "custom_feature")
|
|
assert result == "value"
|
|
|
|
def test_get_connection_feature_missing_feature(self):
|
|
"""Test getting non-existent feature from connection."""
|
|
sockets_metadata = {
|
|
"sid1": {"feature_flags": {"existing_feature": True}}
|
|
}
|
|
result = get_connection_feature(sockets_metadata, "sid1", "missing_feature")
|
|
assert result is False
|
|
|
|
def test_supports_feature_returns_boolean(self):
|
|
"""Test that supports_feature always returns boolean."""
|
|
sockets_metadata = {
|
|
"sid1": {
|
|
"feature_flags": {
|
|
"bool_feature": True,
|
|
"string_feature": "value",
|
|
"none_feature": None,
|
|
},
|
|
}
|
|
}
|
|
|
|
# True boolean feature
|
|
assert supports_feature(sockets_metadata, "sid1", "bool_feature") is True
|
|
|
|
# Non-boolean values should return False
|
|
assert supports_feature(sockets_metadata, "sid1", "string_feature") is False
|
|
assert supports_feature(sockets_metadata, "sid1", "none_feature") is False
|
|
assert supports_feature(sockets_metadata, "sid1", "missing_feature") is False
|
|
|
|
def test_supports_feature_with_missing_connection(self):
|
|
"""Test supports_feature with missing connection."""
|
|
sockets_metadata = {}
|
|
assert supports_feature(sockets_metadata, "missing_sid", "any_feature") is False
|
|
|
|
def test_empty_feature_flags_dict(self):
|
|
"""Test connection with empty feature flags dictionary."""
|
|
sockets_metadata = {"sid1": {"feature_flags": {}}}
|
|
result = get_connection_feature(sockets_metadata, "sid1", "any_feature")
|
|
assert result is False
|
|
assert supports_feature(sockets_metadata, "sid1", "any_feature") is False
|