mirror of
https://git.datalinker.icu/ltdrdata/ComfyUI-Manager
synced 2025-12-08 21:54:26 +08:00
Fix sequential scenario test that was failing in Env 9 due to improper state management between scenarios. Root Cause: In scenario "CNR enabled + Nightly disabled": 1. Install Nightly → auto-disables CNR 2. Disable Nightly → both packages now disabled 3. Test expects: CNR enabled, Nightly disabled 4. Actual state: both disabled (CNR not re-enabled) Fix: Added enable operation after disabling Nightly to restore CNR to enabled state. This ensures the scenario accurately represents "CNR enabled + Nightly disabled" instead of leaving both packages disabled. Changes: - Added enable CNR operation after disabling Nightly in scenario 2 - Updated ui_id to be more descriptive (disable_nightly, enable_cnr) - Ensures proper state transition: both enabled → Nightly disabled → CNR re-enabled Test Results: - Before: 9/10 environments passing (90%) - After: 10/10 environments passing (100%) - All 63 tests passing across all environments Session Progress: - Session start: 7/10 environments (60/63 tests) - After parameter fix: 9/10 environments (62/63 tests) - Final: 10/10 environments (63/63 tests ✅) - Total improvement: +3 environments, +3 tests (+42.9%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
503 lines
18 KiB
Python
503 lines
18 KiB
Python
"""
|
|
Test that /v2/customnode/installed API priority rules work correctly.
|
|
|
|
This test verifies that the `/v2/customnode/installed` API follows two priority rules:
|
|
|
|
Rule 1 (Enabled-Priority):
|
|
- When both enabled and disabled versions exist → Show ONLY enabled version
|
|
- Prevents frontend confusion from duplicate package entries
|
|
|
|
Rule 2 (CNR-Priority for disabled packages):
|
|
- When both CNR and Nightly are disabled → Show ONLY CNR version
|
|
- CNR stable releases take priority over development Nightly builds
|
|
|
|
Additional behaviors:
|
|
1. Only returns the enabled version when both enabled and disabled versions exist
|
|
2. Does not return duplicate entries for the same package
|
|
3. Returns disabled version only when no enabled version exists
|
|
4. When both are disabled, CNR version takes priority over Nightly
|
|
"""
|
|
|
|
import pytest
|
|
import requests
|
|
import time
|
|
from pathlib import Path
|
|
|
|
TEST_PACKAGE_ID = "ComfyUI_SigmoidOffsetScheduler"
|
|
WAIT_TIME_SHORT = 10
|
|
WAIT_TIME_MEDIUM = 30
|
|
|
|
|
|
@pytest.fixture
|
|
def setup_cnr_enabled_nightly_disabled(api_client, custom_nodes_path):
|
|
"""
|
|
Setup fixture: CNR v1.0.1 enabled, Nightly disabled.
|
|
|
|
This creates the scenario where both versions exist but in different states:
|
|
- custom_nodes/ComfyUI_SigmoidOffsetScheduler/ (CNR v1.0.1, enabled)
|
|
- .disabled/comfyui_sigmoidoffsetscheduler@nightly/ (Nightly, disabled)
|
|
"""
|
|
import shutil
|
|
|
|
# Clean up any existing package (session fixture may have restored CNR)
|
|
enabled_path = custom_nodes_path / TEST_PACKAGE_ID
|
|
disabled_path = custom_nodes_path / ".disabled"
|
|
|
|
if enabled_path.exists():
|
|
shutil.rmtree(enabled_path)
|
|
|
|
if disabled_path.exists():
|
|
for item in disabled_path.iterdir():
|
|
if 'sigmoid' in item.name.lower() and item.is_dir():
|
|
shutil.rmtree(item)
|
|
|
|
# Install Nightly version first
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id="setup_nightly_install",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "nightly",
|
|
"selected_version": "nightly",
|
|
},
|
|
)
|
|
assert response.status_code == 200, f"Failed to queue Nightly install: {response.text}"
|
|
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201], f"Failed to start queue: {response.text}"
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Verify Nightly is installed and enabled
|
|
enabled_path = custom_nodes_path / TEST_PACKAGE_ID
|
|
assert enabled_path.exists(), "Nightly should be enabled"
|
|
assert (enabled_path / ".git").exists(), "Nightly should have .git directory"
|
|
|
|
# Install CNR version (this will disable Nightly and enable CNR)
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id="setup_cnr_install",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "1.0.1",
|
|
"selected_version": "latest",
|
|
},
|
|
)
|
|
assert response.status_code == 200, f"Failed to queue CNR install: {response.text}"
|
|
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201], f"Failed to start queue: {response.text}"
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Verify final state: CNR enabled, Nightly disabled
|
|
assert enabled_path.exists(), "CNR should be enabled"
|
|
assert (enabled_path / ".tracking").exists(), "CNR should have .tracking marker"
|
|
|
|
disabled_path = custom_nodes_path / ".disabled"
|
|
disabled_nightly = [
|
|
item for item in disabled_path.iterdir()
|
|
if 'sigmoid' in item.name.lower() and (item / ".git").exists()
|
|
]
|
|
assert len(disabled_nightly) == 1, "Should have one disabled Nightly package"
|
|
|
|
yield
|
|
|
|
# Cleanup
|
|
# (cleanup handled by conftest.py session fixture)
|
|
|
|
|
|
def test_installed_api_shows_only_enabled_when_both_exist(
|
|
api_client,
|
|
server_url,
|
|
custom_nodes_path,
|
|
setup_cnr_enabled_nightly_disabled
|
|
):
|
|
"""
|
|
Test that /installed API only shows enabled package when both versions exist.
|
|
|
|
Setup:
|
|
- CNR v1.0.1 enabled in custom_nodes/ComfyUI_SigmoidOffsetScheduler/
|
|
- Nightly disabled in .disabled/comfyui_sigmoidoffsetscheduler@nightly/
|
|
|
|
Expected:
|
|
- /v2/customnode/installed returns ONLY the enabled CNR package
|
|
- No duplicate entry for the disabled Nightly version
|
|
- enabled: True for the CNR package
|
|
|
|
This prevents frontend confusion from seeing two entries for the same package.
|
|
"""
|
|
# Verify setup state on filesystem
|
|
enabled_path = custom_nodes_path / TEST_PACKAGE_ID
|
|
assert enabled_path.exists(), "CNR should be enabled"
|
|
|
|
disabled_path = custom_nodes_path / ".disabled"
|
|
disabled_packages = [
|
|
item for item in disabled_path.iterdir()
|
|
if 'sigmoid' in item.name.lower() and item.is_dir()
|
|
]
|
|
assert len(disabled_packages) > 0, "Should have at least one disabled package"
|
|
|
|
# Call /v2/customnode/installed API
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200, f"API call failed: {response.text}"
|
|
|
|
installed = response.json()
|
|
|
|
# Find all entries for our test package
|
|
sigmoid_entries = [
|
|
(key, info) for key, info in installed.items()
|
|
if 'sigmoid' in key.lower() or 'sigmoid' in info.get('cnr_id', '').lower()
|
|
]
|
|
|
|
# Critical assertion: Should have EXACTLY ONE entry, not two
|
|
assert len(sigmoid_entries) == 1, (
|
|
f"Expected exactly 1 entry in /installed API, but found {len(sigmoid_entries)}. "
|
|
f"This causes frontend confusion. Entries: {sigmoid_entries}"
|
|
)
|
|
|
|
# Verify the single entry is the enabled one
|
|
package_key, package_info = sigmoid_entries[0]
|
|
assert package_info['enabled'] is True, (
|
|
f"The single entry should be enabled=True, got: {package_info}"
|
|
)
|
|
|
|
# Verify it's the CNR version (has version number)
|
|
assert package_info['ver'].count('.') >= 2, (
|
|
f"Should be CNR version with semantic version, got: {package_info['ver']}"
|
|
)
|
|
|
|
|
|
def test_installed_api_shows_disabled_when_no_enabled_exists(
|
|
api_client,
|
|
server_url,
|
|
custom_nodes_path
|
|
):
|
|
"""
|
|
Test that /installed API shows disabled package when no enabled version exists.
|
|
|
|
Setup:
|
|
- Install and then disable a package (no other version exists)
|
|
|
|
Expected:
|
|
- /v2/customnode/installed returns the disabled package
|
|
- enabled: False
|
|
- Only one entry for the package
|
|
|
|
This verifies that disabled packages are still visible when they're the only version.
|
|
"""
|
|
# Install CNR version
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id="test_disabled_only_install",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "1.0.1",
|
|
"selected_version": "latest",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Disable it
|
|
response = api_client.queue_task(
|
|
kind="disable",
|
|
ui_id="test_disabled_only_disable",
|
|
params={"node_name": TEST_PACKAGE_ID},
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Verify it's disabled on filesystem
|
|
enabled_path = custom_nodes_path / TEST_PACKAGE_ID
|
|
assert not enabled_path.exists(), "Package should be disabled"
|
|
|
|
disabled_path = custom_nodes_path / ".disabled"
|
|
disabled_packages = [
|
|
item for item in disabled_path.iterdir()
|
|
if 'sigmoid' in item.name.lower() and item.is_dir()
|
|
]
|
|
assert len(disabled_packages) > 0, "Should have disabled package"
|
|
|
|
# Call /v2/customnode/installed API
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200
|
|
|
|
installed = response.json()
|
|
|
|
# Find entry for our test package
|
|
sigmoid_entries = [
|
|
(key, info) for key, info in installed.items()
|
|
if 'sigmoid' in key.lower() or 'sigmoid' in info.get('cnr_id', '').lower()
|
|
]
|
|
|
|
# Should have exactly one entry (the disabled one)
|
|
assert len(sigmoid_entries) == 1, (
|
|
f"Expected exactly 1 entry for disabled-only package, found {len(sigmoid_entries)}"
|
|
)
|
|
|
|
# Verify it's marked as disabled
|
|
package_key, package_info = sigmoid_entries[0]
|
|
assert package_info['enabled'] is False, (
|
|
f"Package should be disabled, got: {package_info}"
|
|
)
|
|
|
|
# Cleanup: Re-enable package for other tests
|
|
response = api_client.queue_task(
|
|
kind="enable",
|
|
ui_id="test_disabled_only_cleanup",
|
|
params={"cnr_id": TEST_PACKAGE_ID},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_SHORT)
|
|
|
|
|
|
def test_installed_api_no_duplicates_across_scenarios(
|
|
api_client,
|
|
server_url,
|
|
custom_nodes_path
|
|
):
|
|
"""
|
|
Test that /installed API never returns duplicate entries regardless of scenario.
|
|
|
|
This test cycles through multiple scenarios:
|
|
1. CNR enabled only
|
|
2. CNR enabled + Nightly disabled
|
|
3. Nightly enabled + CNR disabled
|
|
4. Both disabled
|
|
|
|
In all cases, the API should return at most ONE entry per unique package.
|
|
"""
|
|
scenarios = [
|
|
("cnr_only", "CNR enabled only"),
|
|
("cnr_enabled_nightly_disabled", "CNR enabled + Nightly disabled"),
|
|
("nightly_enabled_cnr_disabled", "Nightly enabled + CNR disabled"),
|
|
]
|
|
|
|
for scenario_id, scenario_desc in scenarios:
|
|
# Setup scenario
|
|
if scenario_id == "cnr_only":
|
|
# Install CNR only
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id=f"test_{scenario_id}_install",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "1.0.1",
|
|
"selected_version": "latest",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
elif scenario_id == "cnr_enabled_nightly_disabled":
|
|
# Install Nightly (this auto-disables CNR), then disable Nightly, then enable CNR
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id=f"test_{scenario_id}_nightly",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "nightly",
|
|
"selected_version": "nightly",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Disable Nightly
|
|
response = api_client.queue_task(
|
|
kind="disable",
|
|
ui_id=f"test_{scenario_id}_disable_nightly",
|
|
params={"node_name": TEST_PACKAGE_ID},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Re-enable CNR (which was auto-disabled when Nightly was installed)
|
|
response = api_client.queue_task(
|
|
kind="enable",
|
|
ui_id=f"test_{scenario_id}_enable_cnr",
|
|
params={"cnr_id": TEST_PACKAGE_ID},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
elif scenario_id == "nightly_enabled_cnr_disabled":
|
|
# CNR should already be disabled from previous scenario
|
|
# Enable Nightly (install if not exists)
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id=f"test_{scenario_id}_nightly",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "nightly",
|
|
"selected_version": "nightly",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Call API and verify no duplicates
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200, f"API call failed for {scenario_desc}"
|
|
|
|
installed = response.json()
|
|
|
|
sigmoid_entries = [
|
|
(key, info) for key, info in installed.items()
|
|
if 'sigmoid' in key.lower() or 'sigmoid' in info.get('cnr_id', '').lower()
|
|
]
|
|
|
|
# Critical: Should never have more than one entry
|
|
assert len(sigmoid_entries) <= 1, (
|
|
f"Scenario '{scenario_desc}': Expected at most 1 entry, found {len(sigmoid_entries)}. "
|
|
f"Entries: {sigmoid_entries}"
|
|
)
|
|
|
|
if len(sigmoid_entries) == 1:
|
|
package_key, package_info = sigmoid_entries[0]
|
|
# If entry exists, it should be enabled=True
|
|
# (disabled-only case is covered in separate test)
|
|
if scenario_id != "all_disabled":
|
|
assert package_info['enabled'] is True, (
|
|
f"Scenario '{scenario_desc}': Entry should be enabled=True, got: {package_info}"
|
|
)
|
|
|
|
|
|
def test_installed_api_cnr_priority_when_both_disabled(
|
|
api_client,
|
|
server_url,
|
|
custom_nodes_path
|
|
):
|
|
"""
|
|
Test Rule 2 (CNR-Priority): When both CNR and Nightly are disabled, show ONLY CNR.
|
|
|
|
Setup:
|
|
- Install CNR v1.0.1 and disable it
|
|
- Install Nightly and disable it
|
|
- Both versions exist in .disabled/ directory
|
|
|
|
Expected:
|
|
- /v2/customnode/installed returns ONLY the CNR version
|
|
- CNR version has enabled: False
|
|
- Nightly version is NOT in the response
|
|
- This prevents confusion and prioritizes stable releases over dev builds
|
|
|
|
Rationale:
|
|
CNR versions are stable releases and should be preferred over development
|
|
Nightly builds when both are inactive. This gives users clear indication
|
|
of which version would be activated if they choose to enable.
|
|
"""
|
|
# Install CNR version first
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id="test_cnr_priority_cnr_install",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "1.0.1",
|
|
"selected_version": "latest",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Install Nightly (this will disable CNR)
|
|
response = api_client.queue_task(
|
|
kind="install",
|
|
ui_id="test_cnr_priority_nightly_install",
|
|
params={
|
|
"id": TEST_PACKAGE_ID,
|
|
"version": "nightly",
|
|
"selected_version": "nightly",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Disable Nightly (now both are disabled)
|
|
response = api_client.queue_task(
|
|
kind="disable",
|
|
ui_id="test_cnr_priority_nightly_disable",
|
|
params={"node_name": TEST_PACKAGE_ID},
|
|
)
|
|
assert response.status_code == 200
|
|
response = api_client.start_queue()
|
|
assert response.status_code in [200, 201]
|
|
time.sleep(WAIT_TIME_MEDIUM)
|
|
|
|
# Verify filesystem state: both should be in .disabled/
|
|
disabled_path = custom_nodes_path / ".disabled"
|
|
disabled_packages = [
|
|
item for item in disabled_path.iterdir()
|
|
if 'sigmoid' in item.name.lower() and item.is_dir()
|
|
]
|
|
|
|
# Should have both CNR and Nightly in .disabled/
|
|
cnr_disabled = [p for p in disabled_packages if (p / ".tracking").exists()]
|
|
nightly_disabled = [p for p in disabled_packages if (p / ".git").exists()]
|
|
|
|
assert len(cnr_disabled) >= 1, f"Should have disabled CNR package, found: {[p.name for p in disabled_packages]}"
|
|
assert len(nightly_disabled) >= 1, f"Should have disabled Nightly package, found: {[p.name for p in disabled_packages]}"
|
|
|
|
# Call /v2/customnode/installed API
|
|
response = requests.get(f"{server_url}/v2/customnode/installed")
|
|
assert response.status_code == 200
|
|
|
|
installed = response.json()
|
|
|
|
# Find all entries for our test package
|
|
sigmoid_entries = [
|
|
(key, info) for key, info in installed.items()
|
|
if 'sigmoid' in key.lower() or 'sigmoid' in info.get('cnr_id', '').lower()
|
|
]
|
|
|
|
# Critical assertion: Should have EXACTLY ONE entry (CNR), not two
|
|
assert len(sigmoid_entries) == 1, (
|
|
f"Rule 2 (CNR-Priority) violated: Expected exactly 1 entry (CNR only), "
|
|
f"but found {len(sigmoid_entries)}. Entries: {sigmoid_entries}"
|
|
)
|
|
|
|
# Verify the single entry is the CNR version
|
|
package_key, package_info = sigmoid_entries[0]
|
|
|
|
# Should be disabled
|
|
assert package_info['enabled'] is False, (
|
|
f"Package should be disabled, got: {package_info}"
|
|
)
|
|
|
|
# Should have cnr_id (CNR packages have cnr_id, Nightly has empty cnr_id)
|
|
assert package_info.get('cnr_id'), (
|
|
f"Should be CNR package with cnr_id, got: {package_info}"
|
|
)
|
|
|
|
# Should have null aux_id (CNR packages have aux_id=null, Nightly has aux_id set)
|
|
assert package_info.get('aux_id') is None, (
|
|
f"Should be CNR package with aux_id=null, got: {package_info}"
|
|
)
|
|
|
|
# Should have semantic version (CNR uses semver, Nightly uses git hash)
|
|
ver = package_info['ver']
|
|
assert ver.count('.') >= 2 or ver[0].isdigit(), (
|
|
f"Should be CNR with semantic version, got: {ver}"
|
|
)
|