splunk-soar-sdk 3.2.3__tar.gz → 3.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splunk_soar_sdk-3.3.0/.github/scripts/generate_test_summary.py +173 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/workflows/code_quality.yml +1 -1
- splunk_soar_sdk-3.3.0/.github/workflows/integration_tests.yml +195 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/PKG-INFO +1 -1
- splunk_soar_sdk-3.3.0/mcp_server/README.md +135 -0
- splunk_soar_sdk-3.3.0/mcp_server/install.sh +58 -0
- splunk_soar_sdk-3.3.0/mcp_server/mcp_config.json +12 -0
- splunk_soar_sdk-3.3.0/mcp_server/pyproject.toml +24 -0
- splunk_soar_sdk-3.3.0/mcp_server/pytest.ini +2 -0
- splunk_soar_sdk-3.3.0/mcp_server/src/soar_test_assistant/__init__.py +3 -0
- splunk_soar_sdk-3.3.0/mcp_server/src/soar_test_assistant/server.py +372 -0
- splunk_soar_sdk-3.3.0/mcp_server/tests/test_analyzer.py +23 -0
- splunk_soar_sdk-3.3.0/mcp_server/uv.lock +1009 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/pyproject.toml +10 -3
- splunk_soar_sdk-3.3.0/release_notes.txt +77 -0
- splunk_soar_sdk-3.3.0/release_version.txt +1 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/abstract.py +4 -4
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/actions_manager.py +13 -37
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app.py +15 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_client.py +2 -1
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/asset.py +27 -0
- splunk_soar_sdk-3.3.0/src/soar_sdk/asset_state.py +54 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/cli.py +2 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/manifests/processors.py +5 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/package/cli.py +10 -4
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/package/utils.py +16 -15
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/path_utils.py +0 -5
- splunk_soar_sdk-3.3.0/src/soar_sdk/cli/test/cli.py +296 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/webhook.py +3 -2
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/exceptions.py +9 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/base_connector.py +7 -2
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/encryption_helper.py +6 -4
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_package_cli.py +24 -5
- splunk_soar_sdk-3.3.0/tests/cli/test_test_cli.py +208 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/conftest.py +12 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/app.json +116 -30
- splunk_soar_sdk-3.3.0/tests/example_app/example_asset.json +9 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/pyproject.toml +4 -1
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/src/app.py +14 -0
- splunk_soar_sdk-3.3.0/tests/example_app/uv.lock +1201 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app_with_webhook/app.json +77 -76
- splunk_soar_sdk-3.3.0/tests/example_app_with_webhook/example_asset.json +7 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app_with_webhook/pyproject.toml +2 -2
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app_with_webhook/src/app.py +5 -3
- {splunk_soar_sdk-3.2.3/tests/example_app → splunk_soar_sdk-3.3.0/tests/example_app_with_webhook}/uv.lock +118 -113
- splunk_soar_sdk-3.3.0/tests/integration/conftest.py +115 -0
- splunk_soar_sdk-3.3.0/tests/integration/phantom_constants.py +29 -0
- splunk_soar_sdk-3.3.0/tests/integration/phantom_instance.py +421 -0
- splunk_soar_sdk-3.3.0/tests/integration/soar_client.py +250 -0
- splunk_soar_sdk-3.3.0/tests/integration/test_example_app.py +60 -0
- splunk_soar_sdk-3.3.0/tests/integration/test_example_app_with_webhook.py +19 -0
- splunk_soar_sdk-3.3.0/tests/interfaces/__init__.py +0 -0
- splunk_soar_sdk-3.3.0/tests/meta/__init__.py +0 -0
- splunk_soar_sdk-3.3.0/tests/mocks/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_actions_manager.py +7 -90
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_app.py +47 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_asset.py +10 -0
- splunk_soar_sdk-3.3.0/tests/test_asset_state.py +68 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/uv.lock +18 -1
- splunk_soar_sdk-3.2.3/release_notes.txt +0 -21
- splunk_soar_sdk-3.2.3/release_version.txt +0 -1
- splunk_soar_sdk-3.2.3/tests/example_app/example_asset.json +0 -4
- splunk_soar_sdk-3.2.3/tests/example_app_with_webhook/uv.lock +0 -1022
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/pull_request_template.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/utils/github.js +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/utils/update_version.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/workflows/commit_hygiene.yml +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/workflows/generate_docs.yml +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.github/workflows/semantic_release.yml +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.gitignore +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.pre-commit-config.yaml +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/.releaserc +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/LICENSE +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/README.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/commitlint.config.js +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/api_reference.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/app_structure/index.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/app_structure/pre-commit-config.yaml.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/app_structure/pyproject.toml.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/app_structure/src_app.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/changelog.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/cli_reference.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/conf.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/custom_views/index.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/custom_views/reusable_components.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/custom_views/templates.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/custom_views/view_handlers.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/getting_started/defining_asset.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/getting_started/first_action.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/getting_started/index.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/getting_started/init_app.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/getting_started/installation.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/getting_started/testing_and_building.rst +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/docs/index.rst +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/apis → splunk_soar_sdk-3.3.0/mcp_server/tests}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/action_results.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/app_templates/basic_app/src → splunk_soar_sdk-3.3.0/src/soar_sdk/apis}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/apis/artifact.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/apis/container.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/apis/utils.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/apis/vault.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_cli_runner.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_templates/basic_app/.gitignore +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_templates/basic_app/.pre-commit-config.yaml +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_templates/basic_app/logo.svg +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_templates/basic_app/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/cli → splunk_soar_sdk-3.3.0/src/soar_sdk/app_templates/basic_app/src}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/app_templates/basic_app/uv.lock +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/async_utils.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/cli/init → splunk_soar_sdk-3.3.0/src/soar_sdk/cli}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/cli/manifests → splunk_soar_sdk-3.3.0/src/soar_sdk/cli/init}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/init/cli.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/code_renderers → splunk_soar_sdk-3.3.0/src/soar_sdk/cli/manifests}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/manifests/cli.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/manifests/deserializers.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/manifests/serializers.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/meta → splunk_soar_sdk-3.3.0/src/soar_sdk/cli/test}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/cli/utils.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/views → splunk_soar_sdk-3.3.0/src/soar_sdk/code_renderers}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/code_renderers/action_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/code_renderers/app_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/code_renderers/asset_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/code_renderers/renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/code_renderers/templates/pyproject.toml.jinja +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/code_renderers/toml_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/colors.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/compat.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/crypto.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/action.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/make_request.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/on_es_poll.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/on_poll.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/test_connectivity.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/decorators/view_handler.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/field_utils.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/input_spec.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/logging.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/views/components → splunk_soar_sdk-3.3.0/src/soar_sdk/meta}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/meta/actions.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/meta/adapters.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/meta/app.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/meta/datatypes.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/meta/dependencies.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/meta/webhooks.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/artifact.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/attachment_input.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/container.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/finding.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/vault_attachment.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/models/view.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/params.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/paths.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/py.typed +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/action_result.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/app.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/connector_result.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/consts.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/install_info.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/json_keys.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/ph_ipc.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom/vault.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom_common/app_interface/app_interface.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/shims/phantom_common/encryption/encryption_manager_factory.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/base/base_template.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/base/error.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/base/header.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/base/logo_header.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/components/pie_chart.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/widgets/widget_resize_snippet.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/templates/widgets/widget_template.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/types.py +0 -0
- {splunk_soar_sdk-3.2.3/src/soar_sdk/webhooks → splunk_soar_sdk-3.3.0/src/soar_sdk/views}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/views/component_registry.py +0 -0
- {splunk_soar_sdk-3.2.3/tests → splunk_soar_sdk-3.3.0/src/soar_sdk/views/components}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/views/components/pie_chart.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/views/template_filters.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/views/template_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/views/view_parser.py +0 -0
- {splunk_soar_sdk-3.2.3/tests/cli → splunk_soar_sdk-3.3.0/src/soar_sdk/webhooks}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/webhooks/models.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/src/soar_sdk/webhooks/routing.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/test.png +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/test.txt +0 -0
- {splunk_soar_sdk-3.2.3/tests/cli/manifests → splunk_soar_sdk-3.3.0/tests}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3/tests/example_app/src/actions → splunk_soar_sdk-3.3.0/tests/cli}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/datapath_parse.py +0 -0
- {splunk_soar_sdk-3.2.3/tests/interfaces → splunk_soar_sdk-3.3.0/tests/cli/manifests}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/manifests/test_processors.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/manifests/test_python_version_resolution.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_assets/converted_app/actions.py.txt +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_cli.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_convert_cli.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_deserializers.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_init_cli.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_manifests_cli.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_serializers.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/cli/test_utils.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/code_renderers/test_action_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/logo.svg +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/release_notes/v1.md +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/src/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3/tests/meta → splunk_soar_sdk-3.3.0/tests/example_app/src/actions}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/src/actions/async_action.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/src/actions/generate_category.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/src/actions/reverse_string.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/src/ignoreme.txt +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app/templates/reverse_string.html +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app_with_webhook/logo.svg +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app_with_webhook/logo_dark.svg +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/example_app_with_webhook/src/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3/tests/mocks → splunk_soar_sdk-3.3.0/tests/integration}/__init__.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/interfaces/test_artifact_interface.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/interfaces/test_container_interface.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/interfaces/test_vault_interface.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/meta/test_actions.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/meta/test_adapters.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/meta/test_datatypes.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/meta/test_dependencies.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/meta/test_webhooks.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/mocks/dynamic_mocks.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/mocks/importable_action.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/stubs.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_action_results.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_app_action.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_app_action_params.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_app_action_results.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_app_client.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_app_runner.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_assets/splunk-sdk-2.1.0.tar.gz +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_async_integration.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_async_utils.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_attachment_input.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_code_renderers.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_compat.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_container.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_custom_views.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_encryption.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_es_on_poll.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_field_utils.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_finding.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_input_spec.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_logging.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_make_request_action.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_on_poll.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_params.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_template_filters.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_template_renderer.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_test_connectivity.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/test_view_parser.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/webhooks/test_models.py +0 -0
- {splunk_soar_sdk-3.2.3 → splunk_soar_sdk-3.3.0}/tests/webhooks/test_routing.py +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate integration test summary from JUnit XML reports."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class TestResult:
|
|
12
|
+
"""Test result summary for a single version."""
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
passed: int
|
|
16
|
+
failed: int
|
|
17
|
+
skipped: int
|
|
18
|
+
errors: int
|
|
19
|
+
duration: float
|
|
20
|
+
failures: list[dict[str, str]]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def parse_junit_xml(xml_path: Path) -> TestResult:
|
|
24
|
+
"""Parse JUnit XML file and extract test results."""
|
|
25
|
+
tree = ET.parse(xml_path) # noqa: S314
|
|
26
|
+
root = tree.getroot()
|
|
27
|
+
|
|
28
|
+
version = xml_path.stem.replace("junit-", "")
|
|
29
|
+
passed = 0
|
|
30
|
+
failed = 0
|
|
31
|
+
skipped = 0
|
|
32
|
+
errors = 0
|
|
33
|
+
duration = 0.0
|
|
34
|
+
failures = []
|
|
35
|
+
|
|
36
|
+
for testsuite in root.findall(".//testsuite"):
|
|
37
|
+
duration += float(testsuite.get("time", 0))
|
|
38
|
+
passed += (
|
|
39
|
+
int(testsuite.get("tests", 0))
|
|
40
|
+
- int(testsuite.get("failures", 0))
|
|
41
|
+
- int(testsuite.get("skipped", 0))
|
|
42
|
+
- int(testsuite.get("errors", 0))
|
|
43
|
+
)
|
|
44
|
+
failed += int(testsuite.get("failures", 0))
|
|
45
|
+
skipped += int(testsuite.get("skipped", 0))
|
|
46
|
+
errors += int(testsuite.get("errors", 0))
|
|
47
|
+
|
|
48
|
+
for testcase in root.findall(".//testcase"):
|
|
49
|
+
failure = testcase.find("failure")
|
|
50
|
+
error = testcase.find("error")
|
|
51
|
+
|
|
52
|
+
if failure is not None:
|
|
53
|
+
failures.append(
|
|
54
|
+
{
|
|
55
|
+
"name": f"{testcase.get('classname')}.{testcase.get('name')}",
|
|
56
|
+
"message": failure.get("message", "No message"),
|
|
57
|
+
"type": failure.get("type", "AssertionError"),
|
|
58
|
+
"output": (failure.text or "")[:500],
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
elif error is not None:
|
|
62
|
+
failures.append(
|
|
63
|
+
{
|
|
64
|
+
"name": f"{testcase.get('classname')}.{testcase.get('name')}",
|
|
65
|
+
"message": error.get("message", "No message"),
|
|
66
|
+
"type": error.get("type", "Error"),
|
|
67
|
+
"output": (error.text or "")[:500],
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return TestResult(
|
|
72
|
+
version=version,
|
|
73
|
+
passed=passed,
|
|
74
|
+
failed=failed,
|
|
75
|
+
skipped=skipped,
|
|
76
|
+
errors=errors,
|
|
77
|
+
duration=duration,
|
|
78
|
+
failures=failures,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def format_duration(seconds: float) -> str:
|
|
83
|
+
"""Format duration in human-readable format."""
|
|
84
|
+
minutes = int(seconds // 60)
|
|
85
|
+
secs = int(seconds % 60)
|
|
86
|
+
return f"{minutes}m {secs}s"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def generate_markdown_summary(
|
|
90
|
+
results: list[TestResult], run_url: str | None = None
|
|
91
|
+
) -> str:
|
|
92
|
+
"""Generate markdown summary from test results."""
|
|
93
|
+
total_passed = sum(r.passed for r in results)
|
|
94
|
+
total_failed = sum(r.failed for r in results)
|
|
95
|
+
total_errors = sum(r.errors for r in results)
|
|
96
|
+
|
|
97
|
+
md = "# Integration Test Results\n"
|
|
98
|
+
|
|
99
|
+
if total_failed + total_errors == 0:
|
|
100
|
+
md += f"## ✅ {total_passed} tests passed!\n"
|
|
101
|
+
else:
|
|
102
|
+
md += "## ⚠️ Some tests failed.\n"
|
|
103
|
+
|
|
104
|
+
md += "| Version | ✅ Passed | ❌ Failed | ⏭️ Skipped | Duration |\n"
|
|
105
|
+
md += "| ------- | --------- | --------- | ---------- | -------- |\n"
|
|
106
|
+
|
|
107
|
+
for result in sorted(results, key=lambda r: r.version):
|
|
108
|
+
md += f"| {result.version} | {result.passed} | {result.failed + result.errors} | {result.skipped} | {format_duration(result.duration)} |\n"
|
|
109
|
+
|
|
110
|
+
has_failures = any(r.failures for r in results)
|
|
111
|
+
if has_failures:
|
|
112
|
+
md += "## Failed Tests\n"
|
|
113
|
+
md += "| Version | Name | Message | Type |\n"
|
|
114
|
+
md += "| ------- | ---- | ------- | ---- |\n"
|
|
115
|
+
for result in results:
|
|
116
|
+
if result.failures:
|
|
117
|
+
for failure in result.failures:
|
|
118
|
+
message = failure["message"]
|
|
119
|
+
if len(message) > 200:
|
|
120
|
+
message = message[:200].strip() + "…"
|
|
121
|
+
md += f"| {result.version} | {failure['name']} | {message} | {failure['type']} |\n"
|
|
122
|
+
|
|
123
|
+
if run_url:
|
|
124
|
+
md += "---\n[View full logs and artifacts](" + run_url + ")\n"
|
|
125
|
+
|
|
126
|
+
return md
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main() -> int:
|
|
130
|
+
"""Main entry point."""
|
|
131
|
+
if len(sys.argv) < 2:
|
|
132
|
+
print(
|
|
133
|
+
"Usage: generate_test_summary.py <test-results-dir> [output-file] [run-url]"
|
|
134
|
+
)
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
test_results_dir = Path(sys.argv[1])
|
|
138
|
+
if not test_results_dir.exists():
|
|
139
|
+
print(f"Error: Directory {test_results_dir} does not exist")
|
|
140
|
+
return 1
|
|
141
|
+
|
|
142
|
+
junit_files = list(test_results_dir.glob("**/junit-*.xml"))
|
|
143
|
+
if not junit_files:
|
|
144
|
+
print(f"Error: No JUnit XML files found in {test_results_dir}")
|
|
145
|
+
return 1
|
|
146
|
+
|
|
147
|
+
results = []
|
|
148
|
+
for junit_file in junit_files:
|
|
149
|
+
try:
|
|
150
|
+
result = parse_junit_xml(junit_file)
|
|
151
|
+
results.append(result)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f"Error parsing {junit_file}: {e}")
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
if not results:
|
|
157
|
+
print("Error: No test results could be parsed")
|
|
158
|
+
return 1
|
|
159
|
+
|
|
160
|
+
run_url = sys.argv[3] if len(sys.argv) > 3 else None
|
|
161
|
+
summary = generate_markdown_summary(results, run_url)
|
|
162
|
+
print(summary)
|
|
163
|
+
|
|
164
|
+
summary_file = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("test-summary.md")
|
|
165
|
+
summary_file.write_text(summary)
|
|
166
|
+
print(f"\nSummary written to {summary_file}", file=sys.stderr)
|
|
167
|
+
|
|
168
|
+
has_failures = any(r.failed + r.errors > 0 for r in results)
|
|
169
|
+
return 1 if has_failures else 0
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
sys.exit(main())
|
|
@@ -56,7 +56,7 @@ jobs:
|
|
|
56
56
|
enable-cache: true
|
|
57
57
|
python-version: ${{ matrix.python-version }}
|
|
58
58
|
- name: Pytest + Coverage - ${{ matrix.python-version }}
|
|
59
|
-
run: uv run --locked pytest -n auto
|
|
59
|
+
run: uv run --locked pytest -n auto -m "not integration"
|
|
60
60
|
|
|
61
61
|
generate_docs:
|
|
62
62
|
runs-on: ubuntu-latest
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
name: Integration Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- next
|
|
8
|
+
- beta
|
|
9
|
+
pull_request:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build-example-apps:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
app:
|
|
17
|
+
- example_app
|
|
18
|
+
- example_app_with_webhook
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout SDK repository
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v5
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
python-version: 3.13
|
|
28
|
+
|
|
29
|
+
- name: Build ${{ matrix.app }}
|
|
30
|
+
run: |
|
|
31
|
+
echo "Building SOAR SDK from branch"
|
|
32
|
+
uv build
|
|
33
|
+
echo "Building ${{ matrix.app }} using soarapps CLI"
|
|
34
|
+
uv run soarapps package build tests/${{ matrix.app }} --output-file /tmp/${{ matrix.app }}.tgz --with-sdk-wheel-from $(ls -t dist/splunk_soar_sdk-*.whl | head -n 1)
|
|
35
|
+
echo "App build completed successfully"
|
|
36
|
+
|
|
37
|
+
- name: Upload app tar file as artifact
|
|
38
|
+
uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: ${{ matrix.app }}-tar
|
|
41
|
+
path: /tmp/${{ matrix.app }}.tgz
|
|
42
|
+
retention-days: 1
|
|
43
|
+
|
|
44
|
+
integration-test:
|
|
45
|
+
runs-on:
|
|
46
|
+
- codebuild-github-splunk-soar-sdk-integration-tests-${{ github.run_id }}-${{ github.run_attempt }}
|
|
47
|
+
- image:custom-linux-875003031410.dkr.ecr.us-west-1.amazonaws.com/soar-connectors/pytest:3ea28df603962918dc8e72ead393c40b3ce6e20e
|
|
48
|
+
needs: build-example-apps
|
|
49
|
+
strategy:
|
|
50
|
+
fail-fast: false
|
|
51
|
+
matrix:
|
|
52
|
+
include:
|
|
53
|
+
- version: "previous"
|
|
54
|
+
ip: ${{ vars.PHANTOM_INSTANCE_PREVIOUS_VERSION_IP }}
|
|
55
|
+
- version: "current"
|
|
56
|
+
ip: ${{ vars.PHANTOM_INSTANCE_CURRENT_VERSION_IP }}
|
|
57
|
+
- version: "next"
|
|
58
|
+
ip: ${{ vars.PHANTOM_INSTANCE_NEXT_VERSION_IP }}
|
|
59
|
+
env:
|
|
60
|
+
PHANTOM_USERNAME: ${{ vars.PHANTOM_USERNAME }}
|
|
61
|
+
PHANTOM_PASSWORD: ${{ secrets.PHANTOM_PASSWORD }}
|
|
62
|
+
NUM_TEST_RETRIES: ${{ vars.NUM_TEST_RETRIES || '2' }}
|
|
63
|
+
PHANTOM_VERSION: ${{ matrix.version }}
|
|
64
|
+
AUTOMATION_BROKER_NAME: ${{ vars.AUTOMATION_BROKER_NAME }}
|
|
65
|
+
FORCE_AUTOMATION_BROKER: ${{ vars.FORCE_AUTOMATION_BROKER }}
|
|
66
|
+
UV_LOCKED: 1
|
|
67
|
+
steps:
|
|
68
|
+
- name: Checkout SDK repository
|
|
69
|
+
uses: actions/checkout@v4
|
|
70
|
+
|
|
71
|
+
- name: Install uv
|
|
72
|
+
uses: astral-sh/setup-uv@v5
|
|
73
|
+
with:
|
|
74
|
+
enable-cache: true
|
|
75
|
+
python-version: 3.13
|
|
76
|
+
|
|
77
|
+
- name: Download example_app tar file
|
|
78
|
+
uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: example_app-tar
|
|
81
|
+
path: /tmp/
|
|
82
|
+
|
|
83
|
+
- name: Download example_app_with_webhook tar file
|
|
84
|
+
uses: actions/download-artifact@v4
|
|
85
|
+
with:
|
|
86
|
+
name: example_app_with_webhook-tar
|
|
87
|
+
path: /tmp/
|
|
88
|
+
|
|
89
|
+
- name: Clear uv cache and sync SDK
|
|
90
|
+
run: |
|
|
91
|
+
uv cache clean
|
|
92
|
+
uv sync
|
|
93
|
+
|
|
94
|
+
- name: Check connectivity to SOAR host
|
|
95
|
+
run: |
|
|
96
|
+
apt update -y
|
|
97
|
+
apt install -y netcat-openbsd
|
|
98
|
+
phantom_ip=${{ matrix.ip }}
|
|
99
|
+
nc -zv "$phantom_ip" 443
|
|
100
|
+
nc -zv "$phantom_ip" 3500
|
|
101
|
+
|
|
102
|
+
- name: Install example_app on Phantom instance
|
|
103
|
+
env:
|
|
104
|
+
PHANTOM_PASSWORD: ${{ secrets.PHANTOM_PASSWORD }}
|
|
105
|
+
run: |
|
|
106
|
+
phantom_ip=${{ matrix.ip }}
|
|
107
|
+
echo "Installing example_app on https://$phantom_ip"
|
|
108
|
+
|
|
109
|
+
uv run soarapps package install \
|
|
110
|
+
/tmp/example_app.tgz \
|
|
111
|
+
"$phantom_ip" \
|
|
112
|
+
--username "${{ vars.PHANTOM_USERNAME }}"
|
|
113
|
+
|
|
114
|
+
- name: Install example_app_with_webhook on Phantom instance
|
|
115
|
+
env:
|
|
116
|
+
PHANTOM_PASSWORD: ${{ secrets.PHANTOM_PASSWORD }}
|
|
117
|
+
run: |
|
|
118
|
+
phantom_ip=${{ matrix.ip }}
|
|
119
|
+
echo "Installing example_app_with_webhook on https://$phantom_ip"
|
|
120
|
+
|
|
121
|
+
uv run soarapps package install \
|
|
122
|
+
/tmp/example_app_with_webhook.tgz \
|
|
123
|
+
"$phantom_ip" \
|
|
124
|
+
--username "${{ vars.PHANTOM_USERNAME }}" \
|
|
125
|
+
--force
|
|
126
|
+
|
|
127
|
+
- name: Run integration tests
|
|
128
|
+
env:
|
|
129
|
+
PHANTOM_URL: "https://${{ matrix.ip }}"
|
|
130
|
+
run: |
|
|
131
|
+
echo "Running integration tests against $PHANTOM_URL"
|
|
132
|
+
|
|
133
|
+
uv sync --group dev
|
|
134
|
+
|
|
135
|
+
# Run pytest with integration tests only (no coverage)
|
|
136
|
+
uv run pytest tests/integration/ \
|
|
137
|
+
-v \
|
|
138
|
+
--reruns=$NUM_TEST_RETRIES \
|
|
139
|
+
--tb=short \
|
|
140
|
+
--color=yes \
|
|
141
|
+
--no-cov \
|
|
142
|
+
-m "integration" \
|
|
143
|
+
--junitxml=test-results/junit-${{ matrix.version }}.xml \
|
|
144
|
+
|| exit_code=$?
|
|
145
|
+
|
|
146
|
+
echo "Integration tests completed"
|
|
147
|
+
exit ${exit_code:-0}
|
|
148
|
+
|
|
149
|
+
- name: Upload test results
|
|
150
|
+
uses: actions/upload-artifact@v4
|
|
151
|
+
if: always()
|
|
152
|
+
with:
|
|
153
|
+
name: integration-test-results-${{ matrix.version }}
|
|
154
|
+
path: |
|
|
155
|
+
pytest-logs/
|
|
156
|
+
test-results/
|
|
157
|
+
retention-days: 1
|
|
158
|
+
|
|
159
|
+
test-summary:
|
|
160
|
+
runs-on: ubuntu-latest
|
|
161
|
+
needs: integration-test
|
|
162
|
+
if: always()
|
|
163
|
+
steps:
|
|
164
|
+
- name: Checkout repository
|
|
165
|
+
uses: actions/checkout@v4
|
|
166
|
+
|
|
167
|
+
- name: Download all test results
|
|
168
|
+
uses: actions/download-artifact@v4
|
|
169
|
+
with:
|
|
170
|
+
pattern: integration-test-results-*
|
|
171
|
+
path: all-test-results/
|
|
172
|
+
merge-multiple: true
|
|
173
|
+
|
|
174
|
+
- name: Generate test summary
|
|
175
|
+
run: |
|
|
176
|
+
RUN_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
|
|
177
|
+
python3 .github/scripts/generate_test_summary.py all-test-results/ test-summary.md "$RUN_URL"
|
|
178
|
+
|
|
179
|
+
- name: Post summary to job
|
|
180
|
+
run: |
|
|
181
|
+
cat test-summary.md >> $GITHUB_STEP_SUMMARY
|
|
182
|
+
|
|
183
|
+
- name: Upload summary as artifact
|
|
184
|
+
uses: actions/upload-artifact@v4
|
|
185
|
+
with:
|
|
186
|
+
name: test-summary
|
|
187
|
+
path: test-summary.md
|
|
188
|
+
retention-days: 7
|
|
189
|
+
|
|
190
|
+
- name: Fail if tests failed
|
|
191
|
+
run: |
|
|
192
|
+
if grep -q "FAILED" test-summary.md; then
|
|
193
|
+
echo "Integration tests failed - check summary above"
|
|
194
|
+
exit 1
|
|
195
|
+
fi
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splunk-soar-sdk
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: The official framework for developing and testing Splunk SOAR Apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/phantomcyber/splunk-soar-sdk
|
|
6
6
|
Project-URL: Documentation, https://github.com/phantomcyber/splunk-soar-sdk
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# SOAR Test Assistant MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP server that provides automated test analysis and remediation capabilities for the SOAR SDK. This server orchestrates test execution, applies targeted fixes, and validates changes through an iterative workflow.
|
|
4
|
+
|
|
5
|
+
## Workflow
|
|
6
|
+
|
|
7
|
+
The server implements a two-phase analysis and remediation cycle:
|
|
8
|
+
|
|
9
|
+
### 1. analyze_tests - Test Execution and Failure Analysis
|
|
10
|
+
Executes the test suite and captures comprehensive diagnostic output including stack traces, assertion failures, and error context.
|
|
11
|
+
|
|
12
|
+
### 2. fix_and_run_tests - Automated Remediation and Validation
|
|
13
|
+
Applies proposed changes to source files, test fixtures, or dependencies, then re-executes the test suite to verify the fix.
|
|
14
|
+
|
|
15
|
+
The cycle repeats until all tests pass or unresolvable failures are identified.
|
|
16
|
+
|
|
17
|
+
## Tools
|
|
18
|
+
|
|
19
|
+
### `analyze_tests`
|
|
20
|
+
|
|
21
|
+
Executes the SDK test suite and returns detailed diagnostic output for failure analysis.
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- `test_type`: `"unit"` or `"integration"` (default: `"unit"`)
|
|
25
|
+
- `test_path`: Optional specific test file/directory
|
|
26
|
+
- `soar_instance`: Required for integration tests: `{"ip": "...", "username": "...", "password": "..."}`
|
|
27
|
+
|
|
28
|
+
**Returns:**
|
|
29
|
+
- `status`: `"success"` or `"failed"`
|
|
30
|
+
- `test_output`: Complete pytest output with stack traces and context
|
|
31
|
+
- `message`: Diagnostic guidance for common failure patterns
|
|
32
|
+
|
|
33
|
+
**Example:**
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"test_type": "unit",
|
|
37
|
+
"test_path": "tests/cli/manifests/test_processors.py"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `fix_and_run_tests`
|
|
42
|
+
|
|
43
|
+
Applies a set of proposed changes (file edits, command executions) and re-runs the test suite to validate the remediation.
|
|
44
|
+
|
|
45
|
+
**Parameters:**
|
|
46
|
+
- `test_type`: `"unit"` or `"integration"` (default: `"unit"`)
|
|
47
|
+
- `test_path`: Optional specific test file/directory
|
|
48
|
+
- `soar_instance`: Required for integration tests
|
|
49
|
+
- `changes`: Array of changes to apply
|
|
50
|
+
|
|
51
|
+
**Change Types:**
|
|
52
|
+
|
|
53
|
+
#### edit_file
|
|
54
|
+
Replace exact content in a file:
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"type": "edit_file",
|
|
58
|
+
"file": "tests/example_app/app.json",
|
|
59
|
+
"old_content": "\"version\": \"1.0\"",
|
|
60
|
+
"new_content": "\"version\": \"1.1\"",
|
|
61
|
+
"reasoning": "Update version to match new release"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### run_command
|
|
66
|
+
Execute a bash command:
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"type": "run_command",
|
|
70
|
+
"command": "cd tests/example_app && uv run python -c \"<regenerate fixture code>\"",
|
|
71
|
+
"reasoning": "Regenerate outdated test fixture"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Returns:**
|
|
76
|
+
- `status`: `"success"`, `"still_failing"`, or `"error"`
|
|
77
|
+
- `applied_changes`: List of changes that were applied
|
|
78
|
+
- `test_output`: New test results
|
|
79
|
+
- `errors`: Any errors encountered
|
|
80
|
+
|
|
81
|
+
## Example Session
|
|
82
|
+
|
|
83
|
+
Automated remediation workflow for a failing manifest processor test:
|
|
84
|
+
|
|
85
|
+
1. **Initial Analysis**: `analyze_tests` executes test suite
|
|
86
|
+
- Detects assertion failure: `app_meta != expected_meta`
|
|
87
|
+
- Identifies drift in test fixture `tests/example_app/app.json`
|
|
88
|
+
|
|
89
|
+
2. **Remediation Strategy**: Regenerate test fixture to match current processor output
|
|
90
|
+
- Command: Invoke `ManifestProcessor` to generate updated manifest
|
|
91
|
+
- Preserve existing `utctime_updated` field to maintain test stability
|
|
92
|
+
|
|
93
|
+
3. **Apply and Validate**: `fix_and_run_tests` executes regeneration command
|
|
94
|
+
- Fixture updated with current expected values
|
|
95
|
+
- Test suite re-executed
|
|
96
|
+
- Returns `status: "success"`
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
Run the installation script from the repository root:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
./install
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Or configure manually by adding to your MCP settings (`~/.config/claude-code/mcp_settings.json`):
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"mcpServers": {
|
|
111
|
+
"soar-test-assistant": {
|
|
112
|
+
"command": "uv",
|
|
113
|
+
"args": ["run", "soar-test-assistant"],
|
|
114
|
+
"cwd": "/path/to/your/splunk-soar-sdk/mcp_server"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Then restart Claude Code.
|
|
121
|
+
|
|
122
|
+
## SDK-Only Focus
|
|
123
|
+
|
|
124
|
+
This MCP server is **exclusively for SOAR SDK tests**:
|
|
125
|
+
- **Unit tests**: `tests/` (excluding integration)
|
|
126
|
+
- **Integration tests**: `tests/integration/`
|
|
127
|
+
|
|
128
|
+
All tests run from the SDK root directory, which is auto-detected.
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Test the MCP server
|
|
134
|
+
uv run pytest
|
|
135
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "Installing SOAR Test Assistant MCP Server..."
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
MCP_CONFIG_DIR="$HOME/.config/claude-code"
|
|
8
|
+
MCP_CONFIG_FILE="$MCP_CONFIG_DIR/mcp_settings.json"
|
|
9
|
+
|
|
10
|
+
echo "1. Installing dependencies..."
|
|
11
|
+
cd "$SCRIPT_DIR"
|
|
12
|
+
uv sync
|
|
13
|
+
|
|
14
|
+
echo "2. Setting up MCP configuration..."
|
|
15
|
+
mkdir -p "$MCP_CONFIG_DIR"
|
|
16
|
+
|
|
17
|
+
SERVER_NAME="soar-test-assistant"
|
|
18
|
+
SERVER_CONFIG=$(cat <<EOF
|
|
19
|
+
{
|
|
20
|
+
"command": "uv",
|
|
21
|
+
"args": [
|
|
22
|
+
"run",
|
|
23
|
+
"--directory",
|
|
24
|
+
"$SCRIPT_DIR",
|
|
25
|
+
"soar_test_assistant"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
EOF
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if [ -f "$MCP_CONFIG_FILE" ]; then
|
|
32
|
+
echo " MCP config file exists, backing up..."
|
|
33
|
+
cp "$MCP_CONFIG_FILE" "$MCP_CONFIG_FILE.backup.$(date +%s)"
|
|
34
|
+
|
|
35
|
+
if command -v jq &> /dev/null; then
|
|
36
|
+
echo " Adding server to existing config..."
|
|
37
|
+
jq ".mcpServers[\"$SERVER_NAME\"] = $SERVER_CONFIG" "$MCP_CONFIG_FILE" > "$MCP_CONFIG_FILE.tmp"
|
|
38
|
+
mv "$MCP_CONFIG_FILE.tmp" "$MCP_CONFIG_FILE"
|
|
39
|
+
else
|
|
40
|
+
echo " WARNING: jq not found. Please manually add the server to $MCP_CONFIG_FILE"
|
|
41
|
+
echo " Server configuration:"
|
|
42
|
+
echo "$SERVER_CONFIG"
|
|
43
|
+
fi
|
|
44
|
+
else
|
|
45
|
+
echo " Creating new MCP config file..."
|
|
46
|
+
cat > "$MCP_CONFIG_FILE" <<EOF
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"$SERVER_NAME": $SERVER_CONFIG
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
EOF
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
echo ""
|
|
56
|
+
echo "Installation complete!"
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Restart Claude Code to load the new MCP server."
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "soar-test-assistant"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.13"
|
|
5
|
+
|
|
6
|
+
dependencies = [
|
|
7
|
+
"mcp>=1.1.0,<2.0.0",
|
|
8
|
+
"splunk-soar-sdk>=3.0.0",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[tool.uv.sources]
|
|
12
|
+
splunk-soar-sdk = { path = "..", editable = true }
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
soar_test_assistant = "soar_test_assistant.server:cli"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest>=7.4.2",
|
|
24
|
+
]
|