1+ """Unit tests for parallel execution."""
2+
3+ import pytest
4+ pytestmark = [pytest .mark .parallel ]
5+
6+ import os
7+ import sys
8+ import time
9+
10+ # Make the package importable in tests run in this environment
11+ sys .path .insert (0 , os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." )))
12+
13+ from remote_machine .parallel import ParallelExecutor , ParallelResult
14+ from remote_machine .models .command_result import CommandResult
15+ from remote_machine .models .remote_state import RemoteState
16+
17+
18+ class FakeRemoteMachine :
19+ def __init__ (self , host : str , responses : dict [str , str ] = None , should_fail : bool = False ):
20+ self .host = host
21+ self .responses = responses or {}
22+ self .should_fail = should_fail
23+
24+ def run (self , command : str ) -> str :
25+ if self .should_fail :
26+ raise Exception ("Simulated failure" )
27+ return self .responses .get (command , f"executed { command } on { self .host } " )
28+
29+
30+ def test_parallel_executor_success ():
31+ """Test successful parallel execution."""
32+ clients = [
33+ FakeRemoteMachine ("host1" ),
34+ FakeRemoteMachine ("host2" ),
35+ FakeRemoteMachine ("host3" ),
36+ ]
37+
38+ executor = ParallelExecutor (max_workers = 2 )
39+ results = executor .run (clients , "uptime" )
40+
41+ assert len (results ) == 3
42+ for result in results :
43+ assert result .success
44+ assert result .error is None
45+ assert result .duration_ms >= 0
46+ assert "uptime" in result .output
47+ assert result .host in ["host1" , "host2" , "host3" ]
48+
49+
50+ def test_parallel_executor_failure ():
51+ """Test parallel execution with failures."""
52+ clients = [
53+ FakeRemoteMachine ("host1" ),
54+ FakeRemoteMachine ("host2" , should_fail = True ),
55+ FakeRemoteMachine ("host3" ),
56+ ]
57+
58+ executor = ParallelExecutor (max_workers = 2 )
59+ results = executor .run (clients , "uptime" )
60+
61+ assert len (results ) == 3
62+
63+ success_results = [r for r in results if r .success ]
64+ failure_results = [r for r in results if not r .success ]
65+
66+ assert len (success_results ) == 2
67+ assert len (failure_results ) == 1
68+
69+ failure = failure_results [0 ]
70+ assert failure .host == "host2"
71+ assert failure .output is None
72+ assert isinstance (failure .error , Exception )
73+ assert str (failure .error ) == "Simulated failure"
74+
75+
76+ def test_parallel_executor_max_workers ():
77+ """Test that max_workers limits concurrency."""
78+ # This is hard to test directly, but we can check the parameter validation
79+ with pytest .raises (ValueError ):
80+ ParallelExecutor (max_workers = 0 )
81+
82+ with pytest .raises (ValueError ):
83+ ParallelExecutor (max_workers = - 1 )
84+
85+ # Valid
86+ executor = ParallelExecutor (max_workers = 5 )
87+ assert executor .max_workers == 5
88+
89+
90+ def test_parallel_result_structure ():
91+ """Test ParallelResult dataclass structure."""
92+ result = ParallelResult (
93+ host = "testhost" ,
94+ success = True ,
95+ output = "output" ,
96+ error = None ,
97+ duration_ms = 123.45
98+ )
99+
100+ assert result .host == "testhost"
101+ assert result .success is True
102+ assert result .output == "output"
103+ assert result .error is None
104+ assert result .duration_ms == 123.45
0 commit comments