Coverage for tests / unit / cli / test_command.py: 100%
74 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 02:51 -0700
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 02:51 -0700
1from __future__ import annotations
3import os
4import subprocess
6import pytest
8from muutils.cli.command import Command
11def test_Command_init():
12 """Test Command initialization with list and string cmds."""
13 # Valid: list cmd with shell=False (default)
14 cmd1 = Command(cmd=["echo", "hello"])
15 assert cmd1.cmd == ["echo", "hello"]
16 assert cmd1.shell is False
18 # Valid: string cmd with shell=True
19 cmd2 = Command(cmd="echo hello", shell=True)
20 assert cmd2.cmd == "echo hello"
21 assert cmd2.shell is True
23 # Invalid: string cmd with shell=False should raise ValueError
24 with pytest.raises(
25 ValueError, match="cmd must be List\\[str\\] when shell is False"
26 ):
27 Command(cmd="echo hello", shell=False)
29 # Valid: list cmd with shell=True is allowed (will be joined)
30 cmd3 = Command(cmd=["echo", "hello"], shell=True)
31 assert cmd3.cmd == ["echo", "hello"]
32 assert cmd3.shell is True
35def test_Command_properties():
36 """Test cmd_joined and cmd_for_subprocess properties in both shell modes."""
37 # Test with shell=False (list cmd)
38 cmd_list = Command(cmd=["echo", "hello", "world"])
39 assert cmd_list.cmd_joined == "echo hello world"
40 assert cmd_list.cmd_for_subprocess == ["echo", "hello", "world"]
42 # Test with shell=True and string cmd
43 cmd_str = Command(cmd="echo hello world", shell=True)
44 assert cmd_str.cmd_joined == "echo hello world"
45 assert cmd_str.cmd_for_subprocess == "echo hello world"
47 # Test with shell=True and list cmd (should be joined for subprocess)
48 cmd_list_shell = Command(cmd=["echo", "hello", "world"], shell=True)
49 assert cmd_list_shell.cmd_joined == "echo hello world"
50 assert cmd_list_shell.cmd_for_subprocess == "echo hello world"
53def test_Command_script_line():
54 """Test script_line with env vars formatting."""
55 # No env vars
56 cmd1 = Command(cmd=["echo", "hello"])
57 assert cmd1.script_line() == "echo hello"
59 # With env vars
60 cmd2 = Command(cmd=["echo", "hello"], env={"FOO": "bar", "BAZ": "qux"})
61 script = cmd2.script_line()
62 # env vars can be in any order, so check both are present
63 assert "FOO=bar" in script
64 assert "BAZ=qux" in script
65 assert "echo hello" in script
66 # Verify format: env vars come before command
67 assert script.endswith("echo hello")
69 # With shell=True
70 cmd3 = Command(cmd="echo $FOO", shell=True, env={"FOO": "bar"})
71 assert cmd3.script_line() == "FOO=bar echo $FOO"
74def test_Command_env_final():
75 """Test env_final with inherit_env=True and inherit_env=False."""
76 # Set a test environment variable
77 os.environ["TEST_VAR_COMMAND"] = "original"
79 try:
80 # inherit_env=True (default) should merge with os.environ
81 cmd1 = Command(cmd=["echo", "test"], env={"FOO": "bar"})
82 env1 = cmd1.env_final
83 assert env1["FOO"] == "bar"
84 assert env1["TEST_VAR_COMMAND"] == "original"
86 # inherit_env=False should only include provided env
87 cmd2 = Command(cmd=["echo", "test"], env={"FOO": "bar"}, inherit_env=False)
88 env2 = cmd2.env_final
89 assert env2["FOO"] == "bar"
90 assert "TEST_VAR_COMMAND" not in env2
92 # Custom env should override inherited env
93 os.environ["OVERRIDE_TEST"] = "old"
94 cmd3 = Command(cmd=["echo", "test"], env={"OVERRIDE_TEST": "new"})
95 env3 = cmd3.env_final
96 assert env3["OVERRIDE_TEST"] == "new"
98 finally:
99 # Clean up test env vars
100 os.environ.pop("TEST_VAR_COMMAND", None)
101 os.environ.pop("OVERRIDE_TEST", None)
104def test_Command_run():
105 """Test running a simple command and capturing output."""
106 # Simple successful command
107 cmd = Command(cmd=["echo", "hello"])
108 result = cmd.run(capture_output=True, text=True)
109 assert result.returncode == 0
110 assert "hello" in result.stdout
112 # Command with env vars
113 cmd2 = Command(cmd=["sh", "-c", "echo $TEST_VAR"], env={"TEST_VAR": "test_value"})
114 result2 = cmd2.run(capture_output=True, text=True)
115 assert result2.returncode == 0
116 assert "test_value" in result2.stdout
118 # Shell command
119 cmd3 = Command(cmd="echo shell test", shell=True)
120 result3 = cmd3.run(capture_output=True, text=True)
121 assert result3.returncode == 0
122 assert "shell test" in result3.stdout
124 # Test that CalledProcessError is properly raised and handled
125 cmd4 = Command(cmd=["sh", "-c", "exit 1"])
126 result4 = cmd4.run(capture_output=True)
127 assert result4.returncode == 1 # Should not raise by default
129 # When check=True is passed, it should raise CalledProcessError
130 cmd5 = Command(cmd=["sh", "-c", "exit 1"])
131 with pytest.raises(subprocess.CalledProcessError):
132 cmd5.run(check=True, capture_output=True)