forked from contextforge-org/contextforge-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_plugins.py
More file actions
154 lines (130 loc) · 7.14 KB
/
Copy pathtest_plugins.py
File metadata and controls
154 lines (130 loc) · 7.14 KB
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
# -*- coding: utf-8 -*-
"""Location: ./tests/commands/resources/test_plugins.py
Copyright 2025
SPDX-License-Identifier: Apache-2.0
Authors: Matthew Grigsby
Tests for the plugins commands.
"""
# Third-Party
import pytest
import typer
# First-Party
from cforge.commands.resources.plugins import PluginMode, plugins_get, plugins_list, plugins_stats
from cforge.common import AuthenticationError, CLIError
from tests.conftest import invoke_typer_command, patch_functions
class TestPluginCommands:
"""Tests for plugins commands."""
def test_plugin_mode_enum_is_case_insensitive(self) -> None:
"""Typer Enum choices should accept case-insensitive values."""
assert PluginMode("EnFoRcE") == PluginMode.ENFORCE
def test_plugin_mode_enum_missing_non_string(self) -> None:
"""Non-string values should not be coerced into Enum members."""
assert PluginMode._missing_(123) is None
def test_plugin_mode_enum_missing_unknown_value(self) -> None:
"""Unknown strings should not be coerced into Enum members."""
assert PluginMode._missing_("nope") is None
def test_plugins_list_success(self, mock_console) -> None:
"""Test plugins list command with table output."""
mock_response = {
"plugins": [
{"name": "pii_filter", "version": "1.0.0", "author": "ContextForge", "mode": "enforce", "status": "enabled", "priority": 10, "hooks": ["tool_pre_invoke"], "tags": ["security"]}
],
"total": 1,
"enabled_count": 1,
"disabled_count": 0,
}
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"return_value": mock_response},
print_table=None,
) as mocks:
invoke_typer_command(plugins_list)
mocks.print_table.assert_called_once()
def test_plugins_list_json_output(self, mock_console) -> None:
"""Test plugins list with JSON output."""
mock_response = {"plugins": [], "total": 0, "enabled_count": 0, "disabled_count": 0}
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"return_value": mock_response},
print_json=None,
) as mocks:
invoke_typer_command(plugins_list, json_output=True)
mocks.print_json.assert_called_once()
def test_plugins_list_no_results(self, mock_console) -> None:
"""Test plugins list with no results."""
mock_response = {"plugins": [], "total": 0, "enabled_count": 0, "disabled_count": 0}
with patch_functions("cforge.commands.resources.plugins", get_console=mock_console, make_authenticated_request={"return_value": mock_response}):
invoke_typer_command(plugins_list)
assert any("No plugins found" in str(call) for call in mock_console.print.call_args_list)
def test_plugins_list_with_filters(self, mock_console) -> None:
"""Test plugins list with all filters."""
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"return_value": {"plugins": [], "total": 0, "enabled_count": 0, "disabled_count": 0}},
print_table=None,
) as mocks:
invoke_typer_command(plugins_list, search="pii", mode=PluginMode.ENFORCE, hook="tool_pre_invoke", tag="security")
call_args = mocks.make_authenticated_request.call_args
assert call_args[0][0] == "GET"
assert call_args[0][1] == "/admin/plugins"
assert call_args[1]["params"] == {"search": "pii", "mode": "enforce", "hook": "tool_pre_invoke", "tag": "security"}
def test_plugins_list_error(self, mock_console) -> None:
"""Test plugins list error handling."""
with patch_functions("cforge.commands.resources.plugins", get_console=mock_console, make_authenticated_request={"side_effect": Exception("API error")}):
with pytest.raises(typer.Exit):
invoke_typer_command(plugins_list)
def test_plugins_get_success(self, mock_console) -> None:
"""Test plugins get command."""
mock_plugin = {"name": "pii_filter", "version": "1.0.0"}
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"return_value": mock_plugin},
print_json=None,
) as mocks:
invoke_typer_command(plugins_get, name="pii_filter")
mocks.print_json.assert_called_once()
def test_plugins_get_error(self, mock_console) -> None:
"""Test plugins get error handling."""
with patch_functions("cforge.commands.resources.plugins", get_console=mock_console, make_authenticated_request={"side_effect": Exception("API error")}):
with pytest.raises(typer.Exit):
invoke_typer_command(plugins_get, name="pii_filter")
def test_plugins_stats_success(self, mock_console) -> None:
"""Test plugins stats command."""
mock_stats = {"total_plugins": 4, "enabled_plugins": 3, "disabled_plugins": 1, "plugins_by_hook": {"tool_pre_invoke": 3}, "plugins_by_mode": {"enforce": 3, "disabled": 1}}
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"return_value": mock_stats},
print_json=None,
) as mocks:
invoke_typer_command(plugins_stats)
mocks.print_json.assert_called_once()
def test_plugins_stats_error(self, mock_console) -> None:
"""Test plugins stats error handling."""
with patch_functions("cforge.commands.resources.plugins", get_console=mock_console, make_authenticated_request={"side_effect": Exception("API error")}):
with pytest.raises(typer.Exit):
invoke_typer_command(plugins_stats)
def test_plugins_list_forbidden_shows_permission_hint(self, mock_console) -> None:
"""Test plugins list shows a targeted hint on forbidden/admin failures."""
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"side_effect": AuthenticationError("Authentication required but not configured")},
):
with pytest.raises(typer.Exit):
invoke_typer_command(plugins_list)
assert any("Requires admin.plugins permission" in str(call) for call in mock_console.print.call_args_list)
def test_plugins_list_not_found_shows_admin_api_hint(self, mock_console) -> None:
"""Test plugins list shows an admin-api hint on 404."""
with patch_functions(
"cforge.commands.resources.plugins",
get_console=mock_console,
make_authenticated_request={"side_effect": CLIError("API request failed (404): Not Found")},
):
with pytest.raises(typer.Exit):
invoke_typer_command(plugins_list)
assert any("Admin plugin API unavailable" in str(call) for call in mock_console.print.call_args_list)