Files
klubhaus-doorbell/libraries/FastLED/ci/tests/test_package.py
2026-02-12 00:45:31 -08:00

221 lines
7.2 KiB
Python

"""
Comprehensive tests for enhanced Arduino package index implementation with Pydantic
Tests all Pydantic models, validation rules, parsing functionality, and error handling.
"""
import json
from pathlib import Path
from typing import Any, Dict
import pytest
from pydantic import ValidationError
# Import the enhanced implementation
from ci.compiler.packages import (
Board,
Help,
Package,
PackageIndex,
PackageIndexParser,
PackageManagerConfig,
PackageParsingError,
Platform,
SystemDownload,
Tool,
ToolDependency,
format_size,
)
class TestHelp:
"""Test Help model validation"""
def test_valid_help(self):
"""Test valid help creation"""
help_data = {"online": "https://github.com/espressif/arduino-esp32"}
help_obj = Help(**help_data) # type: ignore
assert str(help_obj.online) == "https://github.com/espressif/arduino-esp32"
def test_invalid_url(self):
"""Test invalid URL validation"""
with pytest.raises(ValidationError):
Help(online="not-a-valid-url") # type: ignore
class TestBoard:
"""Test Board model validation"""
def test_valid_board(self):
"""Test valid board creation"""
board_data = {
"name": "ESP32 Dev Module",
"properties": {
"upload.tool": "esptool_py",
"upload.maximum_size": "1310720",
},
}
board = Board(**board_data) # type: ignore
assert board.name == "ESP32 Dev Module"
assert board.properties["upload.tool"] == "esptool_py"
def test_empty_name_validation(self):
"""Test empty name validation"""
with pytest.raises(ValidationError):
Board(name="", properties={})
def test_name_trimming(self):
"""Test name trimming functionality"""
board = Board(name=" ESP32 Dev Module ", properties={})
assert board.name == "ESP32 Dev Module"
class TestSystemDownload:
"""Test SystemDownload model validation"""
def test_size_conversion_from_string(self):
"""Test size conversion from string bytes to MB"""
system = SystemDownload(
host="test-host",
url="https://example.com/file.tar.gz", # type: ignore
archiveFileName="file.tar.gz",
checksum="SHA-256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
size="52428800", # type: ignore - 50 MB in bytes, validator converts
)
assert abs(system.size_mb - 50.0) < 0.1 # Should be ~50 MB
def test_invalid_checksum_format(self):
"""Test invalid checksum format validation"""
with pytest.raises(ValidationError):
SystemDownload(
host="test-host",
url="https://example.com/file.tar.gz", # type: ignore
archiveFileName="file.tar.gz",
checksum="invalid-checksum",
size=50.0, # type: ignore
)
class TestPlatform:
"""Test Platform model validation"""
def test_valid_platform(self):
"""Test valid platform creation"""
platform_data: Dict[str, Any] = {
"name": "ESP32 Arduino",
"architecture": "esp32",
"version": "2.0.5",
"category": "ESP32",
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.5/esp32-2.0.5.zip",
"archiveFileName": "esp32-2.0.5.zip",
"checksum": "SHA-256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"size": "50000000",
"boards": [],
"toolsDependencies": [],
"help": {"online": "https://github.com/espressif/arduino-esp32"},
}
platform = Platform(**platform_data) # type: ignore
assert platform.name == "ESP32 Arduino"
assert platform.architecture == "esp32"
assert platform.size_mb == 50000000 / (1024 * 1024)
def test_invalid_archive_extension(self):
"""Test invalid archive extension validation"""
with pytest.raises(ValidationError):
Platform(
name="Test Platform",
architecture="test",
version="1.0.0",
category="Test",
url="https://example.com/file.txt", # type: ignore
archiveFileName="file.txt", # Invalid extension
checksum="SHA-256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
size=50.0, # type: ignore
boards=[],
toolsDependencies=[],
help=Help(online="https://example.com"), # type: ignore
)
class TestPackageIndexParser:
"""Test PackageIndexParser functionality"""
def test_parse_valid_json(self):
"""Test parsing valid package index JSON"""
valid_json: Dict[str, Any] = {
"packages": [
{
"name": "test",
"maintainer": "Test Maintainer",
"websiteURL": "https://example.com",
"email": "test@example.com",
"help": {"online": "https://example.com"},
"platforms": [],
"tools": [],
}
]
}
parser = PackageIndexParser()
package_index = parser.parse_package_index(json.dumps(valid_json))
assert len(package_index.packages) == 1
assert package_index.packages[0].name == "test"
def test_parse_invalid_json(self):
"""Test parsing invalid JSON"""
parser = PackageIndexParser()
with pytest.raises(PackageParsingError):
parser.parse_package_index("invalid json")
class TestUtilityFunctions:
"""Test utility functions"""
def test_format_size(self):
"""Test size formatting function"""
# Test KB
assert format_size(0.5) == "512.0 KB"
# Test MB
assert format_size(1.5) == "1.5 MB"
assert format_size(512.0) == "512.0 MB"
# Test GB
assert format_size(1536.0) == "1.5 GB"
assert format_size(2048.0) == "2.0 GB"
class TestRealDataParsing:
"""Test with real ESP32 package index data (if network available)"""
def test_esp32_package_parsing(self):
"""Test parsing real ESP32 package index"""
ESP32_URL = "https://espressif.github.io/arduino-esp32/package_esp32_index.json"
try:
parser = PackageIndexParser(timeout=10)
package_index = parser.parse_from_url(ESP32_URL)
# Basic validation
assert len(package_index.packages) > 0
esp32_package = package_index.packages[0]
assert esp32_package.name == "esp32"
assert len(esp32_package.platforms) > 0
assert len(esp32_package.tools) > 0
print(f"✅ Successfully parsed ESP32 package index:")
print(f" 📦 Packages: {len(package_index.packages)}")
print(f" 🛠️ Platforms: {len(esp32_package.platforms)}")
print(f" 🔧 Tools: {len(esp32_package.tools)}")
except Exception as e:
# Skip if network not available
pytest.skip(f"Network test skipped: {e}")
if __name__ == "__main__":
# Run tests
pytest.main([__file__, "-v"])