name: Tests permissions: contents: read on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Cache pip dependencies uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} restore-keys: | ${{ runner.os }}-pip- - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements-dev.txt - name: Run core functionality tests run: | python -c " import sys import os sys.path.insert(0, os.getcwd()) # Test imports from kikotools.base import ComfyAssetsBaseNode from kikotools.tools.resolution_calculator.logic import extract_dimensions, calculate_scaled_dimensions from kikotools.tools.resolution_calculator.node import ResolutionCalculatorNode import torch print('✓ All imports successful') # Test base node assert 'ComfyAssets' in ComfyAssetsBaseNode.CATEGORY print('✓ Base node tests passed') # Test dimension extraction mock_image = torch.randn(1, 1216, 832, 3) width, height = extract_dimensions(image=mock_image) assert width == 832 and height == 1216 print('✓ Dimension extraction tests passed') # Test scaling new_width, new_height = calculate_scaled_dimensions(832, 1216, 1.5) assert new_width % 8 == 0 and new_height % 8 == 0 print('✓ Scaling tests passed') # Test node interface node = ResolutionCalculatorNode() result_width, result_height = node.calculate_resolution(1.5, image=mock_image) assert isinstance(result_width, int) and isinstance(result_height, int) assert result_width % 8 == 0 and result_height % 8 == 0 print('✓ Node interface tests passed') # Test user scenario width, height = node.calculate_resolution(1.53, image=mock_image) print(f'✓ User scenario test: 832×1216 → {width}×{height} (1.53x scale)') print('🎉 All tests passed!') " - name: Test Width Height Selector run: | python -c " import sys import os sys.path.insert(0, os.getcwd()) # Test Width Height Selector imports from kikotools.tools.width_height_selector.node import WidthHeightSelectorNode from kikotools.tools.width_height_selector.presets import PRESET_OPTIONS, PRESET_METADATA from kikotools.tools.width_height_selector.logic import get_preset_dimensions print('✓ Width Height Selector imports successful') # Test preset structure assert len(PRESET_OPTIONS) > 0 assert 'custom' in PRESET_OPTIONS assert len(PRESET_METADATA) > 0 print('✓ Preset structure tests passed') # Test node interface node = WidthHeightSelectorNode() input_types = node.INPUT_TYPES() assert 'required' in input_types assert 'preset' in input_types['required'] assert 'width' in input_types['required'] assert 'height' in input_types['required'] print('✓ Node interface tests passed') # Test formatted presets preset_options = input_types['required']['preset'][0] assert 'custom' in preset_options formatted_count = len([opt for opt in preset_options if ' - ' in opt and 'MP' in opt]) assert formatted_count > 0 print(f'✓ Found {formatted_count} formatted presets') # Test dimension calculation result = node.get_dimensions('1024×1024', 512, 512) assert result == (1024, 1024) print('✓ Dimension calculation tests passed') # Test formatted preset dimensions formatted_preset = '1024×1024 - 1:1 (1.1MP) - SDXL' result = node.get_dimensions(formatted_preset, 512, 512) assert result == (1024, 1024) print('✓ Formatted preset tests passed') # Test preset extraction extracted = node._extract_preset_name(formatted_preset) assert extracted == '1024×1024' print('✓ Preset extraction tests passed') print('🎉 All Width Height Selector tests passed!') " - name: Test Sampler Combo run: | python -c " import sys import os sys.path.insert(0, os.getcwd()) # Test Sampler Combo imports from kikotools.tools.sampler_combo.node import SamplerComboNode from kikotools.tools.sampler_combo.logic import ( get_sampler_combo, validate_sampler_settings, SAMPLERS, SCHEDULERS ) print('✓ Sampler Combo imports successful') # Test node interface node = SamplerComboNode() input_types = node.INPUT_TYPES() assert 'required' in input_types assert 'sampler_name' in input_types['required'] assert 'scheduler' in input_types['required'] assert 'steps' in input_types['required'] assert 'cfg' in input_types['required'] print('✓ Sampler Combo interface tests passed') # Test return types - Updated to match SAMPLERS list change assert node.RETURN_TYPES[0] == SAMPLERS # Now returns SAMPLERS list assert isinstance(node.RETURN_TYPES[1], list) # SCHEDULERS is a list assert node.RETURN_TYPES[2] == 'INT' assert node.RETURN_TYPES[3] == 'FLOAT' assert node.RETURN_NAMES == ('sampler_name', 'scheduler', 'steps', 'cfg') assert node.CATEGORY == '🫶 ComfyAssets/🌀 Samplers' print('✓ Sampler Combo return types tests passed') # Test sampler combo functionality result = node.get_sampler_combo('euler', 'normal', 20, 7.0) assert result == ('euler', 'normal', 20, 7.0) print('✓ Sampler combo functionality tests passed') # Test validation assert validate_sampler_settings('euler', 'normal', 20, 7.0) == True print('✓ Sampler validation tests passed') # Test available samplers and schedulers samplers = node.get_available_samplers() schedulers = node.get_available_schedulers() assert len(samplers) > 0 assert len(schedulers) > 0 assert 'euler' in samplers assert 'normal' in schedulers print(f'✓ Found {len(samplers)} samplers and {len(schedulers)} schedulers') print('🎉 All Sampler Combo tests passed!') " - name: Test Seed History run: | python -c " import sys import os sys.path.insert(0, os.getcwd()) # Test Seed History imports from kikotools.tools.seed_history.node import SeedHistoryNode from kikotools.tools.seed_history.logic import ( generate_random_seed, validate_seed_value, sanitize_seed_value ) print('✓ Seed History imports successful') # Test node interface node = SeedHistoryNode() input_types = node.INPUT_TYPES() assert 'required' in input_types assert 'seed' in input_types['required'] print('✓ Seed History interface tests passed') # Test return types assert node.RETURN_TYPES == ('INT',) assert node.RETURN_NAMES == ('seed',) assert node.CATEGORY == '🫶 ComfyAssets/🌱 Seeds' print('✓ Seed History return types tests passed') # Test seed output functionality result = node.output_seed(12345) assert result == (12345,) print('✓ Seed output functionality tests passed') # Test seed validation assert validate_seed_value(12345) == True assert validate_seed_value(-1) == False print('✓ Seed validation tests passed') # Test seed generation new_seed = generate_random_seed() assert isinstance(new_seed, int) assert validate_seed_value(new_seed) == True print('✓ Seed generation tests passed') # Test seed sanitization clean_seed = sanitize_seed_value(12345) assert clean_seed == 12345 print('✓ Seed sanitization tests passed') # Test node helper methods assert node.is_seed_in_range(12345) == True assert node.is_seed_in_range(-1) == False assert node.get_default_seed() == 12345 print('✓ Seed helper methods tests passed') print('🎉 All Seed History tests passed!') " - name: Test error handling for all tools run: | python -c " import sys import os sys.path.insert(0, os.getcwd()) print('=== Testing Error Handling for All Tools ===') # Test Resolution Calculator error handling from kikotools.tools.resolution_calculator.node import ResolutionCalculatorNode res_node = ResolutionCalculatorNode() try: res_node.calculate_resolution(2.0) # No input provided assert False, 'Should have raised ValueError' except ValueError: print('✓ Resolution Calculator error handling test passed') try: res_node.calculate_resolution(0.0) # Invalid scale assert False, 'Should have raised ValueError' except ValueError: print('✓ Resolution Calculator scale factor validation test passed') # Test Width Height Selector error handling from kikotools.tools.width_height_selector.node import WidthHeightSelectorNode wh_node = WidthHeightSelectorNode() # Test invalid preset fallback result = wh_node.get_dimensions('invalid_preset', 800, 600) assert result == (800, 600) # Should fallback to custom dimensions print('✓ Width Height Selector invalid preset handling test passed') # Test Sampler Combo error handling from kikotools.tools.sampler_combo.node import SamplerComboNode sampler_node = SamplerComboNode() # Test with invalid sampler (should use safe defaults) result = sampler_node.get_sampler_combo('invalid_sampler', 'normal', 20, 7.0) assert result == ('euler', 'normal', 20, 7.0) # Safe defaults print('✓ Sampler Combo invalid input handling test passed') # Test Seed History error handling from kikotools.tools.seed_history.node import SeedHistoryNode seed_node = SeedHistoryNode() # Test invalid seed value (should use fallback) result = seed_node.output_seed(-1) # Invalid negative seed assert result == (12345,) # Fallback seed print('✓ Seed History invalid seed handling test passed') print('🎉 All error handling tests passed for all tools!') " - name: Test ComfyUI integration readiness for all tools run: | python -c " import sys import os sys.path.insert(0, os.getcwd()) print('=== Testing ComfyUI Integration for All Tools ===') # Test Resolution Calculator from kikotools.tools.resolution_calculator.node import ResolutionCalculatorNode res_class = ResolutionCalculatorNode assert hasattr(res_class, 'INPUT_TYPES') assert hasattr(res_class, 'RETURN_TYPES') assert hasattr(res_class, 'RETURN_NAMES') assert hasattr(res_class, 'FUNCTION') assert hasattr(res_class, 'CATEGORY') input_types = res_class.INPUT_TYPES() assert 'required' in input_types assert 'optional' in input_types assert 'scale_factor' in input_types['required'] assert 'image' in input_types['optional'] assert 'latent' in input_types['optional'] assert res_class.RETURN_TYPES == ('INT', 'INT') assert res_class.RETURN_NAMES == ('width', 'height') assert 'ComfyAssets/' in res_class.CATEGORY print('✓ Resolution Calculator ComfyUI integration passed') # Test Width Height Selector from kikotools.tools.width_height_selector.node import WidthHeightSelectorNode wh_class = WidthHeightSelectorNode assert hasattr(wh_class, 'INPUT_TYPES') assert hasattr(wh_class, 'RETURN_TYPES') assert hasattr(wh_class, 'RETURN_NAMES') assert hasattr(wh_class, 'FUNCTION') assert hasattr(wh_class, 'CATEGORY') input_types = wh_class.INPUT_TYPES() assert 'required' in input_types assert 'preset' in input_types['required'] assert 'width' in input_types['required'] assert 'height' in input_types['required'] assert wh_class.RETURN_TYPES == ('INT', 'INT') assert wh_class.RETURN_NAMES == ('width', 'height') assert 'ComfyAssets/' in wh_class.CATEGORY print('✓ Width Height Selector ComfyUI integration passed') # Test Sampler Combo from kikotools.tools.sampler_combo.node import SamplerComboNode sampler_class = SamplerComboNode assert hasattr(sampler_class, 'INPUT_TYPES') assert hasattr(sampler_class, 'RETURN_TYPES') assert hasattr(sampler_class, 'RETURN_NAMES') assert hasattr(sampler_class, 'FUNCTION') assert hasattr(sampler_class, 'CATEGORY') input_types = sampler_class.INPUT_TYPES() assert 'required' in input_types assert 'sampler_name' in input_types['required'] assert 'scheduler' in input_types['required'] assert 'steps' in input_types['required'] assert 'cfg' in input_types['required'] assert 'ComfyAssets/' in sampler_class.CATEGORY print('✓ Sampler Combo ComfyUI integration passed') # Test Seed History from kikotools.tools.seed_history.node import SeedHistoryNode seed_class = SeedHistoryNode assert hasattr(seed_class, 'INPUT_TYPES') assert hasattr(seed_class, 'RETURN_TYPES') assert hasattr(seed_class, 'RETURN_NAMES') assert hasattr(seed_class, 'FUNCTION') assert hasattr(seed_class, 'CATEGORY') input_types = seed_class.INPUT_TYPES() assert 'required' in input_types assert 'seed' in input_types['required'] assert seed_class.RETURN_TYPES == ('INT',) assert seed_class.RETURN_NAMES == ('seed',) assert 'ComfyAssets/' in seed_class.CATEGORY print('✓ Seed History ComfyUI integration passed') print('🎉 All tools ComfyUI integration readiness tests passed!') " test-package-structure: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Set up Python 3.10 uses: actions/setup-python@v6 with: python-version: "3.10" - name: Test package structure run: | # Check required files exist test -f README.md || (echo "README.md missing" && exit 1) test -f LICENSE || (echo "LICENSE missing" && exit 1) test -f requirements-dev.txt || (echo "requirements-dev.txt missing" && exit 1) # Check directory structure test -d kikotools || (echo "kikotools directory missing" && exit 1) test -d kikotools/base || (echo "kikotools/base directory missing" && exit 1) test -d kikotools/tools || (echo "kikotools/tools directory missing" && exit 1) test -d kikotools/tools/resolution_calculator || (echo "resolution_calculator directory missing" && exit 1) test -d kikotools/tools/width_height_selector || (echo "width_height_selector directory missing" && exit 1) test -d kikotools/tools/sampler_combo || (echo "sampler_combo directory missing" && exit 1) test -d kikotools/tools/seed_history || (echo "seed_history directory missing" && exit 1) test -d tests || (echo "tests directory missing" && exit 1) test -d examples || (echo "examples directory missing" && exit 1) test -d web || (echo "web directory missing" && exit 1) # Check key files test -f kikotools/__init__.py || (echo "kikotools/__init__.py missing" && exit 1) test -f kikotools/base/base_node.py || (echo "base_node.py missing" && exit 1) # Resolution Calculator files test -f kikotools/tools/resolution_calculator/node.py || (echo "resolution_calculator node.py missing" && exit 1) test -f kikotools/tools/resolution_calculator/logic.py || (echo "resolution_calculator logic.py missing" && exit 1) # Width Height Selector files test -f kikotools/tools/width_height_selector/node.py || (echo "width_height_selector node.py missing" && exit 1) test -f kikotools/tools/width_height_selector/logic.py || (echo "width_height_selector logic.py missing" && exit 1) test -f kikotools/tools/width_height_selector/presets.py || (echo "width_height_selector presets.py missing" && exit 1) # Sampler Combo files test -f kikotools/tools/sampler_combo/node.py || (echo "sampler_combo node.py missing" && exit 1) test -f kikotools/tools/sampler_combo/logic.py || (echo "sampler_combo logic.py missing" && exit 1) # Seed History files test -f kikotools/tools/seed_history/node.py || (echo "seed_history node.py missing" && exit 1) test -f kikotools/tools/seed_history/logic.py || (echo "seed_history logic.py missing" && exit 1) # Model Downloader files test -f kikotools/tools/model_downloader/node.py || (echo "model_downloader node.py missing" && exit 1) test -f kikotools/tools/model_downloader/base.py || (echo "model_downloader base.py missing" && exit 1) test -f kikotools/tools/model_downloader/detector.py || (echo "model_downloader detector.py missing" && exit 1) # Text Input files test -f kikotools/tools/text_input/node.py || (echo "text_input node.py missing" && exit 1) # Web files test -f web/width_height_swap.js || (echo "width_height_swap.js missing" && exit 1) test -f web/seed_history_ui.js || (echo "seed_history_ui.js missing" && exit 1) echo "✓ Package structure tests passed" test-documentation: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Test documentation completeness run: | # Check documentation files for all tools test -f examples/documentation/resolution_calculator.md || (echo "Resolution calculator docs missing" && exit 1) test -f examples/workflows/resolution_calculator_example.json || (echo "Resolution calculator workflow missing" && exit 1) test -f examples/documentation/width_height_selector.md || (echo "Width height selector docs missing" && exit 1) test -f examples/workflows/width_height_selector_example.json || (echo "Width height selector workflow missing" && exit 1) test -f examples/documentation/sampler_combo.md || (echo "Sampler combo docs missing" && exit 1) test -f examples/workflows/sampler_combo_example.json || (echo "Sampler combo workflow missing" && exit 1) test -f examples/documentation/seed_history.md || (echo "Seed history docs missing" && exit 1) test -f examples/workflows/seed_history_example.json || (echo "Seed history workflow missing" && exit 1) # Check README has key sections grep -q "Installation" README.md || (echo "README missing Installation section" && exit 1) grep -q "Quick Start" README.md || (echo "README missing Quick Start section" && exit 1) grep -q "Development" README.md || (echo "README missing Development section" && exit 1) grep -q "Contributing" README.md || (echo "README missing Contributing section" && exit 1) echo "✓ Documentation tests passed"