Skip to content

Commit fde0def

Browse files
Merge pull request #63 from amd/alex_functionaltests
Functional tests workflow
2 parents 09b6a97 + 1e535f8 commit fde0def

File tree

9 files changed

+424
-2
lines changed

9 files changed

+424
-2
lines changed

.github/workflows/code_quality_checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111
jobs:
1212
pre-commit:
1313
runs-on: [ self-hosted ]
14-
container: python:3.10
14+
container: python:3.9
1515

1616
steps:
1717
- uses: actions/checkout@v3
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Python Functional Tests
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
push:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
run_tests:
14+
runs-on: [ self-hosted ]
15+
container: python:3.9
16+
17+
steps:
18+
- uses: actions/checkout@v3
19+
20+
- name: Install xmllint
21+
run: |
22+
apt-get update
23+
apt-get install -y libxml2-utils bc
24+
25+
- name: Install package and run functional tests
26+
id: run_functional_tests
27+
shell: bash
28+
run: |
29+
source ./dev-setup.sh
30+
pytest test/functional -s --disable-warnings -v

.github/workflows/unit-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ permissions:
1212
jobs:
1313
run_tests:
1414
runs-on: [ self-hosted ]
15-
container: python:3.10
15+
container: python:3.9
1616

1717
steps:
1818
- uses: actions/checkout@v3

test/functional/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
"""Functional tests for node-scraper."""

test/functional/conftest.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
"""Shared fixtures for functional tests."""
27+
28+
import subprocess
29+
import sys
30+
from typing import List
31+
32+
import pytest
33+
34+
35+
@pytest.fixture
36+
def run_cli_command():
37+
"""Fixture that returns a function to run CLI commands."""
38+
39+
def _run_command(args: List[str], check: bool = False):
40+
"""Run a node-scraper CLI command.
41+
42+
Args:
43+
args: List of command-line arguments
44+
check: If True, raise CalledProcessError on non-zero exit
45+
46+
Returns:
47+
subprocess.CompletedProcess instance
48+
"""
49+
cmd = [sys.executable, "-m", "nodescraper.cli.cli"] + args
50+
return subprocess.run(
51+
cmd,
52+
capture_output=True,
53+
text=True,
54+
check=check,
55+
)
56+
57+
return _run_command
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
"""Functional tests for CLI describe command."""
27+
28+
29+
def test_describe_command_list_plugins(run_cli_command):
30+
"""Test that describe command can list all plugins."""
31+
result = run_cli_command(["describe", "plugin"])
32+
33+
assert result.returncode == 0
34+
assert len(result.stdout) > 0
35+
output = result.stdout.lower()
36+
assert "available plugins" in output or "biosplugin" in output or "kernelplugin" in output
37+
38+
39+
def test_describe_command_single_plugin(run_cli_command):
40+
"""Test that describe command can describe a single plugin."""
41+
result = run_cli_command(["describe", "plugin", "BiosPlugin"])
42+
43+
assert result.returncode == 0
44+
assert len(result.stdout) > 0
45+
output = result.stdout.lower()
46+
assert "bios" in output
47+
48+
49+
def test_describe_invalid_plugin(run_cli_command):
50+
"""Test that describe command handles invalid plugin gracefully."""
51+
result = run_cli_command(["describe", "plugin", "NonExistentPlugin"])
52+
53+
assert result.returncode != 0
54+
output = (result.stdout + result.stderr).lower()
55+
assert "error" in output or "not found" in output or "invalid" in output

test/functional/test_cli_help.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
"""Functional tests for node-scraper CLI help commands."""
27+
28+
import subprocess
29+
import sys
30+
31+
32+
def test_help_command():
33+
"""Test that node-scraper -h displays help information."""
34+
result = subprocess.run(
35+
[sys.executable, "-m", "nodescraper.cli.cli", "-h"],
36+
capture_output=True,
37+
text=True,
38+
)
39+
40+
assert result.returncode == 0
41+
assert "usage:" in result.stdout.lower()
42+
assert "node scraper" in result.stdout.lower()
43+
assert "-h" in result.stdout or "--help" in result.stdout
44+
45+
46+
def test_help_command_long_form():
47+
"""Test that node-scraper --help displays help information."""
48+
result = subprocess.run(
49+
[sys.executable, "-m", "nodescraper.cli.cli", "--help"],
50+
capture_output=True,
51+
text=True,
52+
)
53+
54+
assert result.returncode == 0
55+
assert "usage:" in result.stdout.lower()
56+
assert "node scraper" in result.stdout.lower()
57+
58+
59+
def test_no_arguments():
60+
"""Test that node-scraper with no arguments runs the default config."""
61+
result = subprocess.run(
62+
[sys.executable, "-m", "nodescraper.cli.cli"],
63+
capture_output=True,
64+
text=True,
65+
timeout=30,
66+
)
67+
68+
assert len(result.stdout) > 0 or len(result.stderr) > 0
69+
output = (result.stdout + result.stderr).lower()
70+
assert "plugin" in output or "nodescraper" in output
71+
72+
73+
def test_help_shows_subcommands():
74+
"""Test that help output includes available subcommands."""
75+
result = subprocess.run(
76+
[sys.executable, "-m", "nodescraper.cli.cli", "-h"],
77+
capture_output=True,
78+
text=True,
79+
)
80+
81+
assert result.returncode == 0
82+
output = result.stdout.lower()
83+
assert "run-plugins" in output or "commands:" in output or "positional arguments:" in output
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
"""Functional tests for plugin registry and plugin loading."""
27+
28+
import inspect
29+
30+
from nodescraper.pluginregistry import PluginRegistry
31+
32+
33+
def test_plugin_registry_loads_plugins():
34+
"""Test that PluginRegistry successfully loads built-in plugins."""
35+
registry = PluginRegistry()
36+
37+
assert len(registry.plugins) > 0
38+
plugin_names = [name.lower() for name in registry.plugins.keys()]
39+
expected_plugins = ["biosplugin", "kernelplugin", "osplugin"]
40+
41+
for expected in expected_plugins:
42+
assert expected in plugin_names
43+
44+
45+
def test_plugin_registry_has_connection_managers():
46+
"""Test that PluginRegistry loads connection managers."""
47+
registry = PluginRegistry()
48+
49+
assert len(registry.connection_managers) > 0
50+
conn_names = [name.lower() for name in registry.connection_managers.keys()]
51+
assert "inbandconnectionmanager" in conn_names
52+
53+
54+
def test_plugin_registry_list_plugins():
55+
"""Test that PluginRegistry stores plugins in a dictionary."""
56+
registry = PluginRegistry()
57+
plugin_dict = registry.plugins
58+
59+
assert isinstance(plugin_dict, dict)
60+
assert len(plugin_dict) > 0
61+
assert all(isinstance(name, str) for name in plugin_dict.keys())
62+
assert all(inspect.isclass(cls) for cls in plugin_dict.values())
63+
64+
65+
def test_plugin_registry_get_plugin():
66+
"""Test that PluginRegistry can retrieve a specific plugin."""
67+
registry = PluginRegistry()
68+
plugin_names = list(registry.plugins.keys())
69+
assert len(plugin_names) > 0
70+
71+
first_plugin_name = plugin_names[0]
72+
plugin = registry.plugins[first_plugin_name]
73+
74+
assert plugin is not None
75+
assert hasattr(plugin, "run")

0 commit comments

Comments
 (0)