Skip to content

Commit 5cdf655

Browse files
committed
ridesx: add tests for fls integration
Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
1 parent 39b0356 commit 5cdf655

2 files changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import tempfile
2+
from unittest.mock import patch
3+
4+
import pytest
5+
from jumpstarter_driver_pyserial.driver import PySerial
6+
7+
from .driver import RideSXDriver
8+
from jumpstarter.common.utils import serve
9+
10+
11+
@pytest.fixture(scope="session")
12+
def temp_storage_dir():
13+
with tempfile.TemporaryDirectory() as temp_dir:
14+
yield temp_dir
15+
16+
17+
@pytest.fixture(scope="session")
18+
def ridesx_driver(temp_storage_dir):
19+
yield RideSXDriver(
20+
storage_dir=temp_storage_dir,
21+
children={
22+
"serial": PySerial(url="loop://"),
23+
},
24+
)
25+
26+
27+
@pytest.fixture
28+
def ridesx_client(ridesx_driver):
29+
"""Create a client instance for testing client-side methods"""
30+
with serve(ridesx_driver) as client:
31+
yield client
32+
33+
34+
# Validate Partition Mappings Tests
35+
36+
37+
def test_validate_partition_mappings(ridesx_client):
38+
"""Test partition mapping validation"""
39+
# None is valid (auto-detect mode)
40+
ridesx_client._validate_partition_mappings(None)
41+
42+
# Valid mapping
43+
ridesx_client._validate_partition_mappings({"boot": "/path/to/boot.img"})
44+
45+
# Empty path raises
46+
with pytest.raises(ValueError, match="has an empty file path"):
47+
ridesx_client._validate_partition_mappings({"boot": ""})
48+
49+
# Whitespace-only path raises
50+
with pytest.raises(ValueError, match="has an empty file path"):
51+
ridesx_client._validate_partition_mappings({"boot": " "})
52+
53+
54+
# Flash OCI Auto Tests
55+
56+
57+
def test_flash_oci_auto_success(ridesx_client):
58+
"""Test successful flash_oci_auto call"""
59+
with patch.object(ridesx_client, "call") as mock_call:
60+
mock_call.side_effect = [
61+
{"status": "device_found", "device_id": "ABC123"},
62+
{"status": "success"},
63+
]
64+
65+
result = ridesx_client.flash_oci_auto("oci://quay.io/org/image:tag")
66+
67+
assert result == {"status": "success"}
68+
# Verify flash_oci_image was called with the OCI URL
69+
flash_call = mock_call.call_args_list[1]
70+
assert flash_call[0][0] == "flash_oci_image"
71+
assert flash_call[0][1] == "oci://quay.io/org/image:tag"
72+
73+
74+
def test_flash_oci_auto_error_cases(ridesx_client):
75+
"""Test flash_oci_auto error handling"""
76+
# URL without oci:// scheme
77+
with pytest.raises(ValueError, match="Only oci:// URLs are supported"):
78+
ridesx_client.flash_oci_auto("docker://image:tag")
79+
80+
# Invalid URL format
81+
with pytest.raises(ValueError, match="Invalid OCI URL format"):
82+
ridesx_client.flash_oci_auto("invalid-url")
83+
84+
# No device found
85+
with patch.object(ridesx_client, "call") as mock_call:
86+
mock_call.return_value = {"status": "no_device_found", "device_id": None}
87+
88+
with pytest.raises(RuntimeError, match="No fastboot devices found"):
89+
ridesx_client.flash_oci_auto("oci://image:tag")

python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver_test.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,90 @@ def test_power_rescue(ridesx_power_driver):
452452
with pytest.raises(NotImplementedError, match="Rescue mode not available"):
453453
client.call("rescue")
454454

455+
456+
# Flash OCI Image Tests
457+
# Note: FLS download utilities are tested in jumpstarter.common.fls_test
458+
459+
460+
def test_flash_oci_image_success(temp_storage_dir, ridesx_driver):
461+
with serve(ridesx_driver) as client:
462+
with patch("jumpstarter_driver_ridesx.driver.get_fls_binary", return_value="/usr/local/bin/fls"):
463+
with patch("subprocess.run") as mock_subprocess:
464+
mock_result = MagicMock()
465+
mock_result.stdout = "Flashing complete"
466+
mock_result.stderr = ""
467+
mock_result.returncode = 0
468+
mock_subprocess.return_value = mock_result
469+
470+
result = client.call("flash_oci_image", "oci://quay.io/image:tag", None, None, None)
471+
472+
assert result["status"] == "success"
473+
mock_subprocess.assert_called_once()
474+
call_args = mock_subprocess.call_args[0][0]
475+
assert call_args[0] == "/usr/local/bin/fls"
476+
assert call_args[1] == "fastboot"
477+
assert call_args[2] == "oci://quay.io/image:tag"
478+
479+
480+
def test_flash_oci_image_with_partitions(temp_storage_dir, ridesx_driver):
481+
with serve(ridesx_driver) as client:
482+
with patch("jumpstarter_driver_ridesx.driver.get_fls_binary", return_value="fls"):
483+
with patch("subprocess.run") as mock_subprocess:
484+
mock_result = MagicMock()
485+
mock_result.stdout = "Flashing complete"
486+
mock_result.stderr = ""
487+
mock_result.returncode = 0
488+
mock_subprocess.return_value = mock_result
489+
490+
partitions = {"boot_a": "boot.img", "system_a": "rootfs.simg"}
491+
result = client.call("flash_oci_image", "oci://image:tag", partitions, None, None)
492+
493+
assert result["status"] == "success"
494+
call_args = mock_subprocess.call_args[0][0]
495+
# Check that -t flags are present for partitions
496+
assert "-t" in call_args
497+
assert "boot_a:boot.img" in call_args
498+
assert "system_a:rootfs.simg" in call_args
499+
500+
501+
def test_flash_oci_image_error_cases(temp_storage_dir, ridesx_driver):
502+
"""Test flash_oci_image error handling for various failure modes"""
503+
from jumpstarter.client.core import DriverError
504+
505+
with serve(ridesx_driver) as client:
506+
# Reject non-oci:// schemes
507+
with pytest.raises(DriverError, match="OCI URL must start with oci://"):
508+
client.call("flash_oci_image", "docker://image:tag", None, None, None)
509+
510+
with patch("jumpstarter_driver_ridesx.driver.get_fls_binary", return_value="fls"):
511+
with patch("subprocess.run") as mock_subprocess:
512+
# CalledProcessError
513+
error = subprocess.CalledProcessError(1, "fls")
514+
error.stdout = ""
515+
error.stderr = "Flash failed"
516+
mock_subprocess.side_effect = error
517+
518+
with pytest.raises(DriverError, match="FLS fastboot auto-detection failed"):
519+
client.call("flash_oci_image", "oci://image:tag", None, None, None)
520+
521+
# TimeoutExpired
522+
mock_subprocess.side_effect = subprocess.TimeoutExpired("fls", 1800)
523+
524+
with pytest.raises(DriverError, match="FLS fastboot auto-detection timeout"):
525+
client.call("flash_oci_image", "oci://image:tag", None, None, None)
526+
527+
# FileNotFoundError
528+
mock_subprocess.side_effect = FileNotFoundError("fls not found")
529+
530+
with pytest.raises(DriverError, match="FLS command not found"):
531+
client.call("flash_oci_image", "oci://image:tag", None, None, None)
532+
533+
534+
def test_flash_oci_image_requires_oci_scheme(temp_storage_dir, ridesx_driver):
535+
"""Test that only oci:// URLs are accepted"""
536+
from jumpstarter.client.core import DriverError
537+
538+
with serve(ridesx_driver) as client:
539+
# Bare registry URL should be rejected
540+
with pytest.raises(DriverError, match="OCI URL must start with oci://"):
541+
client.call("flash_oci_image", "quay.io/org/image:v1", None, None, None)

0 commit comments

Comments
 (0)