spl2-testing-framework 1.0.0__py3-none-any.whl
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.
- spl2_testing_framework/__init__.py +15 -0
- spl2_testing_framework/conftest.py +217 -0
- spl2_testing_framework/launch_remotely_spl2.py +85 -0
- spl2_testing_framework/logger_manager.py +26 -0
- spl2_testing_framework/pytest.ini.sample +16 -0
- spl2_testing_framework/single_spl2_file_runner.py +52 -0
- spl2_testing_framework/spl2_test_config.json +9 -0
- spl2_testing_framework/spl2_utils/assertions.spl2 +157 -0
- spl2_testing_framework/spl2_utils/logs_to_metrics.spl2 +36 -0
- spl2_testing_framework/test_runner.py +59 -0
- spl2_testing_framework/tools/__init__.py +15 -0
- spl2_testing_framework/tools/jobs/__init__.py +15 -0
- spl2_testing_framework/tools/jobs/cli_job.py +72 -0
- spl2_testing_framework/tools/jobs/cloud_job.py +107 -0
- spl2_testing_framework/tools/jobs/job.py +65 -0
- spl2_testing_framework/tools/jobs/splunk_job.py +112 -0
- spl2_testing_framework/tools/performance.py +252 -0
- spl2_testing_framework/tools/results.py +135 -0
- spl2_testing_framework/tools/search_clients/__init__.py +28 -0
- spl2_testing_framework/tools/search_clients/cli_search_client.py +87 -0
- spl2_testing_framework/tools/search_clients/cloud_search_client.py +84 -0
- spl2_testing_framework/tools/search_clients/search_client.py +150 -0
- spl2_testing_framework/tools/search_clients/splunk_search_client.py +81 -0
- spl2_testing_framework/tools/spl2test_runner.py +143 -0
- spl2_testing_framework/tools/test_discovery.py +220 -0
- spl2_testing_framework/tools/test_types.py +84 -0
- spl2_testing_framework/tools/utils.py +52 -0
- spl2_testing_framework-1.0.0.dist-info/LICENSE +201 -0
- spl2_testing_framework-1.0.0.dist-info/METADATA +190 -0
- spl2_testing_framework-1.0.0.dist-info/RECORD +32 -0
- spl2_testing_framework-1.0.0.dist-info/WHEEL +4 -0
- spl2_testing_framework-1.0.0.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
pytest.register_assert_rewrite("spl2_testing_framework")
|
|
23
|
+
|
|
24
|
+
from requests.auth import AuthBase, HTTPBasicAuth
|
|
25
|
+
|
|
26
|
+
from spl2_testing_framework.tools.search_clients.cli_search_client import (
|
|
27
|
+
CLISearchClient,
|
|
28
|
+
)
|
|
29
|
+
from spl2_testing_framework.tools.search_clients.cloud_search_client import (
|
|
30
|
+
CloudSearchClient,
|
|
31
|
+
)
|
|
32
|
+
from spl2_testing_framework.tools.search_clients.splunk_search_client import (
|
|
33
|
+
SplunkSearchClient,
|
|
34
|
+
)
|
|
35
|
+
from spl2_testing_framework.tools.spl2test_runner import SPL2TestRunner
|
|
36
|
+
from spl2_testing_framework.tools.test_discovery import (
|
|
37
|
+
BoxTestDiscovery,
|
|
38
|
+
SamplesNotFoundException,
|
|
39
|
+
SingleSPL2Discovery,
|
|
40
|
+
UTDiscovery,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
UNIT_TEST = "unit_test"
|
|
44
|
+
BOX_TEST = "box_test"
|
|
45
|
+
SINGLE_SPL2_FILE = "single_spl2_file"
|
|
46
|
+
|
|
47
|
+
import logging
|
|
48
|
+
|
|
49
|
+
from .logger_manager import setup_logging
|
|
50
|
+
|
|
51
|
+
setup_logging()
|
|
52
|
+
|
|
53
|
+
_LOGGER = logging.getLogger(__name__)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def pytest_addoption(parser):
|
|
57
|
+
parser.addoption("--type", action="store", default="local")
|
|
58
|
+
parser.addoption("--test_dir", action="store", default=".")
|
|
59
|
+
parser.addoption(
|
|
60
|
+
"--performance_check",
|
|
61
|
+
action="store",
|
|
62
|
+
default="no",
|
|
63
|
+
choices=["no", "time", "detailed_time"],
|
|
64
|
+
)
|
|
65
|
+
parser.addoption("--template_file", action="store", default=None)
|
|
66
|
+
parser.addoption("--sample_file", action="store", default=None)
|
|
67
|
+
parser.addoption("--sample_delimiter", action="store", default="\n")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def pytest_generate_tests(metafunc):
|
|
71
|
+
"""This function is used for dynamic generation of tests based on the test files in the directory specified by the
|
|
72
|
+
--test_dir option"""
|
|
73
|
+
test_dir = metafunc.config.getoption("test_dir")
|
|
74
|
+
performance_check = metafunc.config.getoption("performance_check")
|
|
75
|
+
|
|
76
|
+
if BOX_TEST in metafunc.fixturenames:
|
|
77
|
+
_LOGGER.info("Collecting Box Tests")
|
|
78
|
+
module_tests = BoxTestDiscovery(test_dir, performance_check)
|
|
79
|
+
module_tests.discover_tests()
|
|
80
|
+
metafunc.parametrize(
|
|
81
|
+
"%s" % BOX_TEST, module_tests.tests, ids=module_tests.tests.get_ids()
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if SINGLE_SPL2_FILE in metafunc.fixturenames:
|
|
85
|
+
_LOGGER.info("Collecting the single spl2 file")
|
|
86
|
+
template_file = metafunc.config.getoption("template_file")
|
|
87
|
+
if template_file is None:
|
|
88
|
+
pytest.skip("No template file found to execute. Exiting..")
|
|
89
|
+
sample_file = metafunc.config.getoption("sample_file")
|
|
90
|
+
sample_delimiter = metafunc.config.getoption("sample_delimiter")
|
|
91
|
+
spl2_pipelines = SingleSPL2Discovery(
|
|
92
|
+
test_dir,
|
|
93
|
+
template_file,
|
|
94
|
+
sample_file,
|
|
95
|
+
sample_delimiter,
|
|
96
|
+
performance_check=performance_check,
|
|
97
|
+
)
|
|
98
|
+
try:
|
|
99
|
+
spl2_pipelines.discover_tests()
|
|
100
|
+
except SamplesNotFoundException as err:
|
|
101
|
+
pytest.exit(err)
|
|
102
|
+
metafunc.parametrize(
|
|
103
|
+
"%s" % SINGLE_SPL2_FILE,
|
|
104
|
+
spl2_pipelines.tests,
|
|
105
|
+
ids=spl2_pipelines.tests.get_ids(),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if UNIT_TEST in metafunc.fixturenames:
|
|
109
|
+
_LOGGER.info("Collecting Unit Tests")
|
|
110
|
+
unit_tests = UTDiscovery(test_dir)
|
|
111
|
+
unit_tests.discover_tests()
|
|
112
|
+
metafunc.parametrize(
|
|
113
|
+
UNIT_TEST, unit_tests.tests, ids=unit_tests.tests.get_ids()
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.fixture()
|
|
118
|
+
def spl2_test_runner(
|
|
119
|
+
request, config, basic_authentication, bearer_token_authentication
|
|
120
|
+
):
|
|
121
|
+
"""This fixture is used to create a test runner object based on the test type specified by the --type option
|
|
122
|
+
3 types of tests are supported: 'splunk', 'cloud', 'cli'
|
|
123
|
+
"""
|
|
124
|
+
test_type = request.config.option.type.lower()
|
|
125
|
+
if test_type == "splunk":
|
|
126
|
+
url = f"{config['host']}:{config['port']}"
|
|
127
|
+
search_client = SplunkSearchClient(url=url, auth=basic_authentication)
|
|
128
|
+
elif test_type == "cli":
|
|
129
|
+
search_client = CLISearchClient()
|
|
130
|
+
elif test_type == "cloud":
|
|
131
|
+
url = f"https://{config['tenant']}.api.{config['cloud_instance']}/{config['tenant']}"
|
|
132
|
+
search_client = CloudSearchClient(url=url, auth=bearer_token_authentication)
|
|
133
|
+
else:
|
|
134
|
+
raise Exception(
|
|
135
|
+
"Unknown test type: {}. Supported tests types: 'splunk', 'cloud', 'cli'".format(
|
|
136
|
+
test_type
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
x = SPL2TestRunner(search_client)
|
|
141
|
+
return x
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@pytest.fixture(scope="session")
|
|
145
|
+
def config():
|
|
146
|
+
"""This fixture is used to read the configuration from the environment variables or from the spl2_test_config.json
|
|
147
|
+
Configuration set using environment variables will be overwritten by settings from spl2_test_config.json file,
|
|
148
|
+
however empty values in spl2_test_config.json will be ignored.
|
|
149
|
+
"""
|
|
150
|
+
host = os.environ.get("SPL2_TF_HOST", None)
|
|
151
|
+
port = os.environ.get("SPL2_TF_PORT", None)
|
|
152
|
+
user = os.environ.get("SPL2_TF_USER", None)
|
|
153
|
+
password = os.environ.get("SPL2_TF_PASSWORD", None)
|
|
154
|
+
bearer_token = os.environ.get("SPL2_TF_BEARER_TOKEN", None)
|
|
155
|
+
tenant = os.environ.get("SPL2_TF_TENANT", None)
|
|
156
|
+
cloud_instance = os.environ.get("SPL2_TF_CLOUD_INSTANCE", None)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
with open("spl2_test_config.json", "rb") as f:
|
|
160
|
+
conf_file = json.load(fp=f)
|
|
161
|
+
except FileNotFoundError:
|
|
162
|
+
from spl2_testing_framework.tools.utils import get_framework_root
|
|
163
|
+
|
|
164
|
+
with open(f"{get_framework_root()}/spl2_test_config.json", "rb") as f:
|
|
165
|
+
conf_file = json.load(fp=f)
|
|
166
|
+
|
|
167
|
+
if conf_file.get("user", None):
|
|
168
|
+
user = conf_file["user"]
|
|
169
|
+
|
|
170
|
+
if conf_file.get("host", None):
|
|
171
|
+
host = conf_file["host"]
|
|
172
|
+
|
|
173
|
+
if conf_file.get("port", None):
|
|
174
|
+
port = conf_file["port"]
|
|
175
|
+
|
|
176
|
+
if conf_file.get("password", None):
|
|
177
|
+
password = conf_file["password"]
|
|
178
|
+
|
|
179
|
+
if conf_file.get("bearer_token", None):
|
|
180
|
+
bearer_token = conf_file["bearer_token"]
|
|
181
|
+
|
|
182
|
+
if conf_file.get("tenant", None):
|
|
183
|
+
tenant = conf_file["tenant"]
|
|
184
|
+
|
|
185
|
+
if conf_file.get("cloud_instance", None):
|
|
186
|
+
cloud_instance = conf_file["cloud_instance"]
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
"host": host,
|
|
190
|
+
"port": port,
|
|
191
|
+
"user": user,
|
|
192
|
+
"password": password,
|
|
193
|
+
"bearer_token": bearer_token,
|
|
194
|
+
"tenant": tenant,
|
|
195
|
+
"cloud_instance": cloud_instance,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@pytest.fixture(scope="session")
|
|
200
|
+
def basic_authentication(config):
|
|
201
|
+
return HTTPBasicAuth(config.get("user", ""), config.get("password", ""))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@pytest.fixture(scope="session")
|
|
205
|
+
def bearer_token_authentication(config):
|
|
206
|
+
class HTTPBearerTokenAuth(AuthBase):
|
|
207
|
+
"""Attaches HTTP Basic Authentication to the given Request object."""
|
|
208
|
+
|
|
209
|
+
def __init__(self, token):
|
|
210
|
+
self.token = token
|
|
211
|
+
|
|
212
|
+
def __call__(self, r):
|
|
213
|
+
r.headers["Authorization"] = f"Bearer {self.token}"
|
|
214
|
+
return r
|
|
215
|
+
|
|
216
|
+
token = config.get("bearer_token", "")
|
|
217
|
+
return HTTPBearerTokenAuth(token)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
|
|
20
|
+
from spl2_testing_framework.tools.modules_reader import get_template_file
|
|
21
|
+
from spl2_testing_framework.tools.payload_functions import create_pipeline_job
|
|
22
|
+
from spl2_testing_framework.tools.cloud_search_client import CloudSearchClient
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Spl2Runner:
|
|
26
|
+
def _run_job_and_get_response(self, job):
|
|
27
|
+
response = self._search_client.run_job(job)
|
|
28
|
+
if response.status_code == 401:
|
|
29
|
+
print(
|
|
30
|
+
f"Unauthorised, may need to refresh token. Additional info: {response.text}"
|
|
31
|
+
)
|
|
32
|
+
exit(-1)
|
|
33
|
+
if response.status_code != 201:
|
|
34
|
+
print(f"Message: {response.text}. Status code: {response.status_code}.")
|
|
35
|
+
return response
|
|
36
|
+
|
|
37
|
+
def main(self):
|
|
38
|
+
parser = argparse.ArgumentParser("spl2_launcher")
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"spl2file",
|
|
41
|
+
help="Name of file to launch like: `pan_traffic_reduction.spl2`",
|
|
42
|
+
type=str,
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"-s",
|
|
46
|
+
"--source",
|
|
47
|
+
help="$source definition if it is not in the code.",
|
|
48
|
+
type=str,
|
|
49
|
+
default=None,
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"-e", "--events", help="Events definition txt file.", type=str, default=None
|
|
53
|
+
)
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
self._search_client = CloudSearchClient()
|
|
56
|
+
template_module = get_template_file(args.spl2file).read_text(encoding="utf-8")
|
|
57
|
+
if args.source is not None:
|
|
58
|
+
template_module = template_module + f"\n$source = from {args.source};"
|
|
59
|
+
if args.events is not None:
|
|
60
|
+
events = list(
|
|
61
|
+
filter(lambda ev: len(ev) > 0, open(args.events).read().split("\n"))
|
|
62
|
+
)
|
|
63
|
+
template_module = template_module + f"\n$source = from ["
|
|
64
|
+
for event in events:
|
|
65
|
+
template_module = template_module + f"{{_raw: {json.dumps(event)} }}"
|
|
66
|
+
if event != events[-1]:
|
|
67
|
+
template_module = template_module + ","
|
|
68
|
+
template_module = template_module + "];"
|
|
69
|
+
job = create_pipeline_job(
|
|
70
|
+
template_module, "ingestProcessor", False, None
|
|
71
|
+
) # allways launch in IP profile to use IP runner for tests
|
|
72
|
+
response = self._run_job_and_get_response(job)
|
|
73
|
+
# TODO check both destinations
|
|
74
|
+
destination_job_sid = response.json()["queryParameters"]["destination"]["sid"]
|
|
75
|
+
|
|
76
|
+
self._search_client.wait_for_job_status(destination_job_sid)
|
|
77
|
+
|
|
78
|
+
job_result = self._search_client.get_job_results(destination_job_sid)
|
|
79
|
+
|
|
80
|
+
print(json.dumps(job_result, indent=2))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
spl2runner = Spl2Runner()
|
|
85
|
+
spl2runner.main()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def setup_logging():
|
|
21
|
+
logging.basicConfig(
|
|
22
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
23
|
+
handlers=[
|
|
24
|
+
logging.FileHandler("spl2_testing_framework.log"),
|
|
25
|
+
],
|
|
26
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[pytest]
|
|
2
|
+
|
|
3
|
+
log_cli = true
|
|
4
|
+
log-cli-level=INFO
|
|
5
|
+
log_level = INFO
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
addopts =
|
|
9
|
+
-rA
|
|
10
|
+
--verbose
|
|
11
|
+
-k <single_spl2_file|unit_test|box_test> ; Used to run a specific kind of test
|
|
12
|
+
--type=<cloud|cli|local> ; Any preferable method can be used to run the tests
|
|
13
|
+
--test_dir=<test_directory> ; Directory in which your tests and spl2 templates are kept
|
|
14
|
+
--template_file=<template_file_name> ; Name of the .spl2 file for which you want to get the results. Only used when running single_spl2_test_runner.py
|
|
15
|
+
--sample_file=<sample_file_name> ; Path to the sample file. Only used when running single_spl2_test_runner.py
|
|
16
|
+
--sample_delimiter=<sample_file_delimiter> ; Delimiter to separate events in sample_file. Only used when running single_spl2_test_runner.py
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import logging
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
|
|
23
|
+
from spl2_testing_framework.tools.test_types import SingleSPL2
|
|
24
|
+
|
|
25
|
+
_LOGGER = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_single_spl2_file(spl2_test_runner, single_spl2_file: SingleSPL2):
|
|
29
|
+
_LOGGER.info(f"Executing single SPL2 File: '{single_spl2_file.name}'")
|
|
30
|
+
_LOGGER.debug(f"Single SPL2 File Details: {single_spl2_file}")
|
|
31
|
+
spl2_test_runner.run_single_spl2_file(single_spl2_file)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def run():
|
|
35
|
+
parser = argparse.ArgumentParser()
|
|
36
|
+
parser.add_argument("type")
|
|
37
|
+
|
|
38
|
+
arguments, other = parser.parse_known_args()
|
|
39
|
+
|
|
40
|
+
args = [__file__, "--type", arguments.type]
|
|
41
|
+
args.extend(other)
|
|
42
|
+
|
|
43
|
+
# INFO log level is required for result printing
|
|
44
|
+
if "--log-cli-level" not in args:
|
|
45
|
+
args.append("--log-cli-level")
|
|
46
|
+
args.append("INFO")
|
|
47
|
+
|
|
48
|
+
sys.exit(pytest.main(args))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
run()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
# SPDX-FileCopyrightText: 2021 Splunk, Inc. <sales@splunk.com>
|
|
3
|
+
# SPDX-License-Identifier: LicenseRef-Splunk-8-2021
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Utility function to compare a result with an expected value.
|
|
8
|
+
*
|
|
9
|
+
* @param $source events
|
|
10
|
+
* @param $expected_value value the test expects
|
|
11
|
+
* @param $test_field name of field that has the result to check
|
|
12
|
+
* @param $test_name optional, name the test to make it easier to parse results with multiple tests
|
|
13
|
+
* @param $expected_field optional, expected value is in this field
|
|
14
|
+
|
|
15
|
+
*/
|
|
16
|
+
export function expect($source: dataset, $expected_value: any="", $test_field: any=_raw, $test_name: string="test", $expected_field: any=expected): dataset {
|
|
17
|
+
return from $source
|
|
18
|
+
| eval _testExpectedField = if(isnotnull($expected_field), $expected_field, $expected_value)
|
|
19
|
+
| eval _testResult = if($test_field == _testExpectedField, "true", "false")
|
|
20
|
+
| _testAddResult $test_name _testResult _testExpectedField, $test_field
|
|
21
|
+
| fields - _testExpectedField, _testResult
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Test utility to ensure a given field exists on an event.
|
|
26
|
+
*
|
|
27
|
+
* @param $source events
|
|
28
|
+
* @param $field_name the field name to ensure exists
|
|
29
|
+
*/
|
|
30
|
+
export function expect_field($source: dataset, $field_name: any): dataset {
|
|
31
|
+
return | from $source
|
|
32
|
+
| eval _expect = if(isnotnull(_expect), _expect, "true")
|
|
33
|
+
| eval _original_event_json = tojson()
|
|
34
|
+
| eval _expectHasField=if(isnotnull(json_extract_exact(_original_event_json, $field_name)), "true", "false")
|
|
35
|
+
| eval _result=if(_expectHasField == _expect, "true", "false")
|
|
36
|
+
| _testAddResult name="Has field: ${$field_name}" result=_result expected=_expectHasField received=_expectHasField
|
|
37
|
+
| fields - _expectHasField, _original_event_json, _result
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Test utility to ensure given fields exists on an event.
|
|
42
|
+
*
|
|
43
|
+
* _expect field is not considered
|
|
44
|
+
*
|
|
45
|
+
* @param $source events
|
|
46
|
+
* @param $field_names the field name that are required
|
|
47
|
+
* @param $delimeter delimeter for the field names
|
|
48
|
+
*/
|
|
49
|
+
export function expect_fields($source: dataset, $field_names: string): dataset {
|
|
50
|
+
$delimiter = ","
|
|
51
|
+
return | from $source
|
|
52
|
+
| eval _expect = if(isnotnull(_expect),_expect, "false")
|
|
53
|
+
// Convert the original event into json
|
|
54
|
+
| eval _original_event_json = tojson()
|
|
55
|
+
// Remove the _expect field
|
|
56
|
+
| eval _original_event_json=json_delete(_original_event_json, "_expect")
|
|
57
|
+
// Set the expected fields
|
|
58
|
+
| eval _expected_fields = mv_to_json_array(split($field_names, $delimiter))
|
|
59
|
+
// Test all the expected fields
|
|
60
|
+
| eval _test_result = json_object("result", "true", "received", ""),
|
|
61
|
+
_test_result = reduce(
|
|
62
|
+
_expected_fields,
|
|
63
|
+
_test_result,
|
|
64
|
+
($results, $field) -> if(isnotnull(json_extract_exact(_original_event_json, $field)),
|
|
65
|
+
json_set($results, "", {"result": json_extract($results, "result"), "received": json_extract_exact($results, "received")+$field+": true\n"}),
|
|
66
|
+
json_set($results, "", {"result": "false", "received": json_extract($results, "received")+$field+": false\n"})
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
| eval _result=if(_test_result.result == _expect, "true", "false")
|
|
70
|
+
// Create a test result
|
|
71
|
+
| _testAddResult name="expect_fields" result=_result expected=_expect received=_test_result.result
|
|
72
|
+
| fields - _original_event_json, _expected_fields, _test_result, _result
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Utility to ensure a given field exists on an event.
|
|
79
|
+
*
|
|
80
|
+
* @param $source events
|
|
81
|
+
* @param $field_names comma seperated list of fields to validate
|
|
82
|
+
* @param $delimeter the delimeter to split the fields list
|
|
83
|
+
* @param $ignore_testResults ignore the _testResult field if it exists. Useful when chaining tests.
|
|
84
|
+
*/
|
|
85
|
+
export function expect_exact_fields($source: dataset, $field_names: string, $delimiter: string=",", $ignore_testResults: boolean=true): dataset {
|
|
86
|
+
return | from $source
|
|
87
|
+
| eval _expect = if(isnotnull(_expect),_expect, "false")
|
|
88
|
+
// Convert the original event into json
|
|
89
|
+
| eval _original_event_json = tojson()
|
|
90
|
+
// Remove the _expect field
|
|
91
|
+
| eval _original_event_json=json_delete(_original_event_json, "_expect")
|
|
92
|
+
// Get and sort the names of all the fields in the event into a MV field
|
|
93
|
+
| eval _original_event_fields = json_keys(_original_event_json)
|
|
94
|
+
// Get and sort all of the expected fields into a MV field
|
|
95
|
+
| eval _expected_fields = split($field_names, $delimiter)
|
|
96
|
+
|
|
97
|
+
// Match the expected fields against the event fields
|
|
98
|
+
| eval _test_result = json_object("fields", json_object()),
|
|
99
|
+
_test_result = reduce(
|
|
100
|
+
mv_to_json_array(_expected_fields),
|
|
101
|
+
_test_result,
|
|
102
|
+
($results, $field) ->
|
|
103
|
+
json_set($results, "fields."+$field,
|
|
104
|
+
if(isnotnull(json_extract_exact(_original_event_json, $field)), "present", "missing")
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
// Match event fields against expected fields to find unexpected fields
|
|
108
|
+
| eval _test_result = reduce(
|
|
109
|
+
_original_event_fields,
|
|
110
|
+
_test_result,
|
|
111
|
+
($results, $field) ->
|
|
112
|
+
json_set($results, "fields."+$field,
|
|
113
|
+
if(in($field, _expected_fields), "present", "not expected")
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
// Determine the test result
|
|
117
|
+
| eval _result = "true",
|
|
118
|
+
_result = reduce(
|
|
119
|
+
json_keys(_test_result.fields),
|
|
120
|
+
_result,
|
|
121
|
+
($results, $field) ->
|
|
122
|
+
if(json_extract_exact(_test_result.fields, $field) != "present", "false", _result)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
| eval _output=if(_result == _expect, "true", "false")
|
|
126
|
+
| _testAddResult name="expect_exact_fields", result=_output, expected=_expect, received=_result
|
|
127
|
+
| fields - _original_event_json, _original_event_fields, _expected_fields, _result, _test_result, _output
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
// Test result object
|
|
132
|
+
type testResult = {
|
|
133
|
+
test: string,
|
|
134
|
+
result: string,
|
|
135
|
+
expected: any,
|
|
136
|
+
received: any
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Utility function to add a test result object to the test result field.
|
|
141
|
+
*
|
|
142
|
+
* @param $source events
|
|
143
|
+
* @param $name test name
|
|
144
|
+
* @param $result test result
|
|
145
|
+
* @param $expected expected result of the test
|
|
146
|
+
* @param $received the received result of the test
|
|
147
|
+
*/
|
|
148
|
+
function _testAddResult($source: dataset, $name: string, $result: any, $expected: any, $received: any): dataset {
|
|
149
|
+
return from $source
|
|
150
|
+
// Create the test result object
|
|
151
|
+
| eval _testResultObject = json_object("result", $result, "test", $name, "expected", $expected, "received", $received)
|
|
152
|
+
| eval _testResults = if(isnotnull(_testResults),
|
|
153
|
+
json_append(_testResults, "results", json_extract(_testResultObject)),
|
|
154
|
+
json_object("results", json_array(json_extract(_testResultObject)))
|
|
155
|
+
)
|
|
156
|
+
| fields - _testResultObject
|
|
157
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
# SPDX-FileCopyrightText: 2021 Splunk, Inc. <sales@splunk.com>
|
|
3
|
+
# SPDX-License-Identifier: LicenseRef-Splunk-8-2021
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
type metrict=string where $value in (["gauge", "counter", "cumulative", "histogram"]);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This is a MOCK implementation of logs_to_metrics function. It is meant to be used just for testing.
|
|
10
|
+
*
|
|
11
|
+
* @param $source events
|
|
12
|
+
* @param $time time of the metric
|
|
13
|
+
* @param $name name of the metric
|
|
14
|
+
* @param $value value of the metric
|
|
15
|
+
* @param $metrictype type of the metric
|
|
16
|
+
* @param $dimensions dimensions of the metric
|
|
17
|
+
*/
|
|
18
|
+
function logs_to_metrics($source, $time:time, $name:string, $value:number, $metrictype:metrict = "gauge", $dimensions:object = {}): dataset {
|
|
19
|
+
return from $source
|
|
20
|
+
| eval _has_time=if(isnotnull($time), 1, 0)
|
|
21
|
+
| eval _time=if(_has_time=1, $time, time())
|
|
22
|
+
| eval _time=floor(_time / 10) * 10
|
|
23
|
+
| select _time as _time,
|
|
24
|
+
$name as metric_name,
|
|
25
|
+
$value as metric_value,
|
|
26
|
+
$metrictype as metric_type,
|
|
27
|
+
$dimensions as dimensions,
|
|
28
|
+
source as source,
|
|
29
|
+
sourcetype as sourcetype,
|
|
30
|
+
index as index,
|
|
31
|
+
host as host,
|
|
32
|
+
_has_time as _has_time
|
|
33
|
+
| fields - _raw
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export logs_to_metrics;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
from logging import getLogger
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
|
|
23
|
+
from spl2_testing_framework.tools.test_types import BoxTest
|
|
24
|
+
|
|
25
|
+
_LOGGER = getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_box_tests(spl2_test_runner, box_test: BoxTest):
|
|
29
|
+
_LOGGER.info("Running box test: %s from \n %s", box_test.name, box_test.module_path)
|
|
30
|
+
_LOGGER.debug("Test details: %s", box_test)
|
|
31
|
+
|
|
32
|
+
spl2_test_runner.run_box_test(box_test)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_spl2_unit_tests(spl2_test_runner, unit_test):
|
|
36
|
+
_LOGGER.info("Running unit test: %s", unit_test.name)
|
|
37
|
+
_LOGGER.debug("Test details: %s", unit_test)
|
|
38
|
+
|
|
39
|
+
spl2_test_runner.run_unit_test(unit_test)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run():
|
|
43
|
+
parser = argparse.ArgumentParser()
|
|
44
|
+
parser.add_argument("type")
|
|
45
|
+
|
|
46
|
+
arguments, other = parser.parse_known_args()
|
|
47
|
+
|
|
48
|
+
args = [__file__, "--type", arguments.type]
|
|
49
|
+
|
|
50
|
+
if "--log-cli-level" not in args:
|
|
51
|
+
args.append("--log-cli-level")
|
|
52
|
+
args.append("INFO")
|
|
53
|
+
|
|
54
|
+
args.extend(other)
|
|
55
|
+
sys.exit(pytest.main(args))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
run()
|