mirror of
https://git.datalinker.icu/ltdrdata/ComfyUI-Manager
synced 2025-12-11 07:04:23 +08:00
Remove package-level caching in cnr_utils and node_package modules to enable proper dynamic custom node installation and version switching without ComfyUI server restarts. Key Changes: - Remove @lru_cache decorators from version-sensitive functions - Remove cached_property from NodePackage for dynamic state updates - Add comprehensive test suite with parallel execution support - Implement version switching tests (CNR ↔ Nightly) - Add case sensitivity integration tests - Improve error handling and logging API Priority Rules (manager_core.py:1801): - Enabled-Priority: Show only enabled version when both exist - CNR-Priority: Show only CNR when both CNR and Nightly are disabled - Prevents duplicate package entries in /v2/customnode/installed API - Cross-match using cnr_id and aux_id for CNR ↔ Nightly detection Test Infrastructure: - 8 test files with 59 comprehensive test cases - Parallel test execution across 5 isolated environments - Automated test scripts with environment setup - Configurable timeout (60 minutes default) - Support for both master and dr-support-pip-cm branches Bug Fixes: - Fix COMFYUI_CUSTOM_NODES_PATH environment variable export - Resolve test fixture regression with module-level variables - Fix import timing issues in test configuration - Register pytest integration marker to eliminate warnings - Fix POSIX compliance in shell scripts (((var++)) → $((var + 1))) Documentation: - CNR_VERSION_MANAGEMENT_DESIGN.md v1.0 → v1.1 with API priority rules - Add test guides and execution documentation (TESTING_PROMPT.md) - Add security-enhanced installation guide - Create CLI migration guides and references - Document package version management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
107 lines
4.2 KiB
Python
107 lines
4.2 KiB
Python
"""
|
|
Test that /installed API preserves original case in cnr_id.
|
|
|
|
This test verifies that the `/v2/customnode/installed` API:
|
|
1. Returns cnr_id with original case (e.g., "ComfyUI_SigmoidOffsetScheduler")
|
|
2. Does NOT include an "original_name" field
|
|
3. Maintains frontend compatibility with PyPI baseline
|
|
|
|
This matches the PyPI 4.0.3b1 baseline behavior.
|
|
"""
|
|
|
|
import requests
|
|
|
|
|
|
def test_installed_api_preserves_original_case(server_url):
|
|
"""Test that /installed API returns cnr_id with original case."""
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200
|
|
|
|
installed = response.json()
|
|
assert len(installed) > 0, "Should have at least one installed package"
|
|
|
|
# Check each installed package
|
|
for package_key, package_info in installed.items():
|
|
# Verify cnr_id field exists
|
|
assert 'cnr_id' in package_info, f"Package {package_key} should have cnr_id field"
|
|
|
|
cnr_id = package_info['cnr_id']
|
|
|
|
# Verify cnr_id preserves original case (contains uppercase letters)
|
|
# For ComfyUI_SigmoidOffsetScheduler, it should NOT be all lowercase
|
|
if 'comfyui' in cnr_id.lower():
|
|
# If it contains "comfyui", it should have uppercase letters
|
|
assert cnr_id != cnr_id.lower(), \
|
|
f"cnr_id '{cnr_id}' should preserve original case, not be normalized to lowercase"
|
|
|
|
# Verify no original_name field in response (PyPI baseline)
|
|
assert 'original_name' not in package_info, \
|
|
f"Package {package_key} should NOT have original_name field for frontend compatibility"
|
|
|
|
|
|
def test_cnr_package_original_case(server_url):
|
|
"""Test specifically that CNR packages preserve original case."""
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200
|
|
|
|
installed = response.json()
|
|
|
|
# Find a CNR package (has version like "1.0.1")
|
|
cnr_packages = {k: v for k, v in installed.items()
|
|
if v.get('ver', '').count('.') >= 2}
|
|
|
|
assert len(cnr_packages) > 0, "Should have at least one CNR package for testing"
|
|
|
|
for package_key, package_info in cnr_packages.items():
|
|
cnr_id = package_info['cnr_id']
|
|
|
|
# CNR packages should have original case preserved
|
|
# Example: "ComfyUI_SigmoidOffsetScheduler" not "comfyui_sigmoidoffsetscheduler"
|
|
assert any(c.isupper() for c in cnr_id), \
|
|
f"CNR package cnr_id '{cnr_id}' should contain uppercase letters"
|
|
|
|
|
|
def test_nightly_package_original_case(server_url):
|
|
"""Test specifically that Nightly packages preserve original case."""
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200
|
|
|
|
installed = response.json()
|
|
|
|
# Find a Nightly package (key contains "@nightly")
|
|
nightly_packages = {k: v for k, v in installed.items() if '@nightly' in k}
|
|
|
|
if len(nightly_packages) == 0:
|
|
# No nightly packages installed, skip test
|
|
return
|
|
|
|
for package_key, package_info in nightly_packages.items():
|
|
cnr_id = package_info['cnr_id']
|
|
|
|
# Nightly packages should also have original case preserved
|
|
# Example: "ComfyUI_SigmoidOffsetScheduler" not "comfyui_sigmoidoffsetscheduler"
|
|
assert any(c.isupper() for c in cnr_id), \
|
|
f"Nightly package cnr_id '{cnr_id}' should contain uppercase letters"
|
|
|
|
|
|
def test_api_response_structure_matches_pypi(server_url):
|
|
"""Test that API response structure matches PyPI 4.0.3b1 baseline."""
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200
|
|
|
|
installed = response.json()
|
|
|
|
# Skip test if no packages installed (may happen in parallel environments)
|
|
if len(installed) == 0:
|
|
pytest.skip("No packages installed - skipping structure validation test")
|
|
|
|
# Check first package structure
|
|
first_package = next(iter(installed.values()))
|
|
|
|
# Required fields from PyPI baseline
|
|
required_fields = {'ver', 'cnr_id', 'aux_id', 'enabled'}
|
|
actual_fields = set(first_package.keys())
|
|
|
|
assert required_fields == actual_fields, \
|
|
f"API response fields should match PyPI baseline: {required_fields}, got: {actual_fields}"
|