File size: 11,194 Bytes
1fa3c6c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | # Copyright 2020-2026 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import pytest
from trl.skills import install_skill
from trl.skills.cli import add_skills_subcommands, cmd_install, cmd_list, cmd_uninstall
class TestCLICommands:
"""Tests for CLI command handlers."""
def test_cmd_list_without_target(self, capsys):
"""Test cmd_list without target (lists TRL skills)."""
args = argparse.Namespace(target=None, scope="project")
result = cmd_list(args)
captured = capsys.readouterr()
assert result == 0
assert "TRL (available for installation)" in captured.out
assert "trl-training" in captured.out
assert "Use 'trl skills install" in captured.out
def test_cmd_list_with_target(self, tmp_path, capsys):
"""Test cmd_list with target (lists installed skills)."""
# Install a skill
install_skill("trl-training", target=tmp_path)
args = argparse.Namespace(target=str(tmp_path), scope="project")
result = cmd_list(args)
captured = capsys.readouterr()
assert result == 0
assert "trl-training" in captured.out
assert str(tmp_path) in captured.out
def test_cmd_list_empty_target(self, tmp_path, capsys):
"""Test cmd_list with empty target directory."""
args = argparse.Namespace(target=str(tmp_path), scope="project")
result = cmd_list(args)
captured = capsys.readouterr()
assert result == 0
assert "No skills installed" in captured.out
def test_cmd_install_single_skill(self, tmp_path, capsys):
"""Test cmd_install with single skill."""
args = argparse.Namespace(skill="trl-training", all=False, target=str(tmp_path), scope="project", force=False)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 0
assert "β" in captured.out
assert "1/1 skills installed" in captured.out
assert (tmp_path / "trl-training").exists()
def test_cmd_install_all_skills(self, tmp_path, capsys):
"""Test cmd_install with --all flag."""
args = argparse.Namespace(skill=None, all=True, target=str(tmp_path), scope="project", force=False)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 0
assert "β" in captured.out
assert "installed successfully" in captured.out
assert (tmp_path / "trl-training").exists()
def test_cmd_install_no_skill_or_all(self, capsys):
"""Test cmd_install without skill name or --all flag."""
args = argparse.Namespace(skill=None, all=False, target="/tmp/test", scope="project", force=False)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 1
assert "Error: Either provide a skill name or use --all" in captured.out
def test_cmd_install_both_skill_and_all(self, capsys):
"""Test cmd_install with both skill name and --all (error)."""
args = argparse.Namespace(skill="trl-training", all=True, target="/tmp/test", scope="project", force=False)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 1
assert "Cannot specify both" in captured.out
def test_cmd_install_nonexistent_skill(self, tmp_path, capsys):
"""Test cmd_install with non-existent skill."""
args = argparse.Namespace(skill="nonexistent", all=False, target=str(tmp_path), scope="project", force=False)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 1
assert "β" in captured.out
assert "0/1 skills installed" in captured.out
def test_cmd_install_already_exists(self, tmp_path, capsys):
"""Test cmd_install when skill already exists without force."""
# Install once
install_skill("trl-training", target=tmp_path)
args = argparse.Namespace(skill="trl-training", all=False, target=str(tmp_path), scope="project", force=False)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 1
assert "β" in captured.out
assert "Use --force to overwrite" in captured.out
def test_cmd_install_with_force(self, tmp_path, capsys):
"""Test cmd_install with --force to overwrite."""
# Install once
install_skill("trl-training", target=tmp_path)
args = argparse.Namespace(skill="trl-training", all=False, target=str(tmp_path), scope="project", force=True)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 0
assert "β" in captured.out
assert "1/1 skills installed" in captured.out
def test_cmd_uninstall_success(self, tmp_path, capsys):
"""Test cmd_uninstall with installed skill."""
# Install first
install_skill("trl-training", target=tmp_path)
args = argparse.Namespace(skill="trl-training", target=str(tmp_path), scope="project")
result = cmd_uninstall(args)
captured = capsys.readouterr()
assert result == 0
assert "β" in captured.out
assert "has been removed" in captured.out
assert not (tmp_path / "trl-training").exists()
def test_cmd_uninstall_not_installed(self, tmp_path, capsys):
"""Test cmd_uninstall when skill is not installed."""
args = argparse.Namespace(skill="nonexistent", target=str(tmp_path), scope="project")
result = cmd_uninstall(args)
captured = capsys.readouterr()
assert result == 1
assert "β" in captured.out
assert "Error:" in captured.out
def test_cmd_install_creates_target_directory(self, tmp_path, capsys):
"""Test cmd_install creates target directory if it doesn't exist."""
# Custom path that doesn't exist yet
target_path = tmp_path / "new_directory"
assert not target_path.exists()
args = argparse.Namespace(
skill="trl-training", all=False, target=str(target_path), scope="project", force=False
)
result = cmd_install(args)
captured = capsys.readouterr()
assert result == 0
assert "β" in captured.out
assert target_path.exists()
def test_cmd_uninstall_invalid_target(self, capsys):
"""Test cmd_uninstall with non-existent path."""
args = argparse.Namespace(skill="trl-training", target="/nonexistent/invalid/path", scope="project")
result = cmd_uninstall(args)
captured = capsys.readouterr()
assert result == 1
assert "β" in captured.out
class TestCLIArgumentParsing:
"""Tests for CLI argument parsing setup."""
def test_add_skills_subcommands_creates_parsers(self):
"""Test that add_skills_subcommands creates the expected subparsers."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
# Test that we can parse expected commands
args = parser.parse_args(["list"])
assert args.command == "list"
assert hasattr(args, "func")
args = parser.parse_args(["install", "trl-training", "--target", "claude"])
assert args.command == "install"
assert args.skill == "trl-training"
assert args.target == "claude"
args = parser.parse_args(["uninstall", "trl-training", "--target", "claude"])
assert args.command == "uninstall"
assert args.skill == "trl-training"
def test_list_command_optional_target(self):
"""Test that list command has optional target."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
# Should work without target
args = parser.parse_args(["list"])
assert args.target is None
# Should work with target
args = parser.parse_args(["list", "--target", "claude"])
assert args.target == "claude"
def test_install_command_requires_target(self):
"""Test that install command requires target."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
# Should fail without target
with pytest.raises(SystemExit):
parser.parse_args(["install", "trl-training"])
def test_scope_choices(self):
"""Test that scope parameter accepts valid choices."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
# Valid scopes
args = parser.parse_args(["install", "trl-training", "--target", "claude", "--scope", "project"])
assert args.scope == "project"
args = parser.parse_args(["install", "trl-training", "--target", "claude", "--scope", "global"])
assert args.scope == "global"
# Invalid scope should fail
with pytest.raises(SystemExit):
parser.parse_args(["install", "trl-training", "--target", "claude", "--scope", "invalid"])
def test_install_all_flag(self):
"""Test install --all flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
args = parser.parse_args(["install", "--all", "--target", "claude"])
assert args.all is True
assert args.skill is None
def test_install_force_flag(self):
"""Test install --force flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
args = parser.parse_args(["install", "trl-training", "--target", "claude", "--force"])
assert args.force is True
def test_default_scope_is_project(self):
"""Test that default scope is 'project'."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
add_skills_subcommands(subparsers)
args = parser.parse_args(["install", "trl-training", "--target", "claude"])
assert args.scope == "project"
|