spl2-testing-framework 1.5.1__tar.gz → 1.7.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.
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/PKG-INFO +120 -2
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/README.md +118 -1
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/pyproject.toml +2 -1
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/conftest.py +93 -4
- spl2_testing_framework-1.7.0/spl2_testing_framework/spl2_test_config.json +18 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/search_clients/cli_search_client.py +7 -2
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/spl2test_runner.py +90 -28
- spl2_testing_framework-1.7.0/spl2_testing_framework/tools/splunk_integration/__init__.py +13 -0
- spl2_testing_framework-1.7.0/spl2_testing_framework/tools/splunk_integration/splunk_handler.py +119 -0
- spl2_testing_framework-1.7.0/spl2_testing_framework/tools/test_discovery.py +413 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/test_types.py +3 -0
- spl2_testing_framework-1.7.0/spl2_testing_framework/tools/utils.py +114 -0
- spl2_testing_framework-1.5.1/spl2_testing_framework/spl2_test_config.json +0 -9
- spl2_testing_framework-1.5.1/spl2_testing_framework/tools/test_discovery.py +0 -235
- spl2_testing_framework-1.5.1/spl2_testing_framework/tools/utils.py +0 -51
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/LICENSE +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/__init__.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/launch_remotely_spl2.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/logger_manager.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/pytest.ini.sample +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/single_spl2_file_runner.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/spl2_utils/assertions.spl2 +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/spl2_utils/logs_to_metrics.spl2 +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/test_runner.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/__init__.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/create_diff_sheet.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/jobs/__init__.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/jobs/cli_job.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/jobs/cloud_job.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/jobs/job.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/jobs/splunk_job.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/jobs/test_cloud_job.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/performance.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/results.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/search_clients/__init__.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/search_clients/cloud_search_client.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/search_clients/search_client.py +0 -0
- {spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/tools/search_clients/splunk_search_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spl2-testing-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: SPL2 Testing Framework
|
|
5
5
|
Home-page: https://github.com/splunk/spl2-testing-framework
|
|
6
6
|
Keywords: SPL2 testing framework
|
|
@@ -25,6 +25,7 @@ Requires-Dist: pytest (==7.4.4)
|
|
|
25
25
|
Requires-Dist: pytest-subtests (==0.13.1)
|
|
26
26
|
Requires-Dist: pytest-xdist (==3.5.0)
|
|
27
27
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
28
|
+
Requires-Dist: splunk-sdk (>=2.1.1,<3.0.0)
|
|
28
29
|
Requires-Dist: tabulate (==0.9.0)
|
|
29
30
|
Requires-Dist: urllib3 (>=1.26.19,<2.0.0)
|
|
30
31
|
Project-URL: Repository, https://github.com/splunk/spl2-testing-framework
|
|
@@ -130,12 +131,15 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
130
131
|
|
|
131
132
|
Additionally, the following options are supported:
|
|
132
133
|
|
|
134
|
+
* `--test_dir` - directory where test files (`.test.json`, `.test.spl2`, `module.json`) are located. Defaults to the current working directory. Discovery is recursive.
|
|
135
|
+
* `--code_dir` - directory where SPL2 code modules (`.spl2`) are located. Defaults to the current working directory. Falls back to `--test_dir` if the module is not found. See [Separate code and test directories](#separate-code-and-test-directories) for details.
|
|
133
136
|
* `--ignore_empty_strings` - to ignore empty strings in the results
|
|
134
137
|
* `--ignore_additional_fields_in_actual` - to ignore fields present in actual results but not in expected results (useful when actual results contain extra fields that should not affect comparison)
|
|
135
138
|
* `--create_comparison_sheet` - to create a comparison sheet in `comparison_box_test` folder using actual and expected outputs (works only when running box tests)
|
|
136
139
|
* `--cli_bench` - when running tests with CLI, this option enables benchmarking mode by adding `-b` and `-n` flags to the spl2-processor-cli command. The value specifies the number of events to test (e.g., `--cli_bench=1000` will add `-b -n 1000` to the CLI command). **In bench mode, all tests always succeed and CLI output is printed to logs** - this is useful for performance testing and benchmarking. This is only applicable when using `--type cli`.
|
|
140
|
+
* `--check_splunk_results` - option to validate box test output by sending events to a Splunk instance (using HEC) and querying them back, with support for **CIM** (Common Information Model) and **TA** (Technology Add-on) checks.
|
|
137
141
|
|
|
138
|
-
Note: The `pytest.ini.sample` file allows you to define command parameters. Just update the configurations, rename the
|
|
142
|
+
* Note: The `pytest.ini.sample` file allows you to define command parameters. Just update the configurations, rename the
|
|
139
143
|
file by removing the `.sample` extension, and execute the command.
|
|
140
144
|
|
|
141
145
|
### Run tests in IDE [PyCharm]
|
|
@@ -150,10 +154,53 @@ Sample configuration which may be used:
|
|
|
150
154
|
- Parameters:
|
|
151
155
|
`--type [cli | splunk | cloud] --test_dir /tests/resources -o log_cli=true --log-cli-level=INFO --verbose`
|
|
152
156
|
- If test dir is not specified, current working directory will be used
|
|
157
|
+
- Use `--code_dir` if SPL2 code modules are in a separate directory from tests
|
|
153
158
|
- If necessary another pytest options can be added
|
|
154
159
|
|
|
155
160
|
Note: It's necessary to set "pytest" as default test runner in PyCharm settings
|
|
156
161
|
|
|
162
|
+
### Separate code and test directories
|
|
163
|
+
|
|
164
|
+
By default, the framework expects SPL2 code modules (`.spl2` files) to live alongside their test files. The `--code_dir` option allows you to keep code and tests in separate directory trees.
|
|
165
|
+
|
|
166
|
+
#### How it works
|
|
167
|
+
|
|
168
|
+
| What is searched | Primary directory | Fallback directory |
|
|
169
|
+
|------------------|-------------------|--------------------|
|
|
170
|
+
| **Code modules** (`.spl2`) | `--code_dir` | `--test_dir` |
|
|
171
|
+
| **Test files** (`.test.json`, `.test.spl2`) | `--test_dir` | `--code_dir` |
|
|
172
|
+
|
|
173
|
+
Both searches are recursive. If the primary directory has no results, the framework falls back to the other directory and logs the action.
|
|
174
|
+
|
|
175
|
+
When multiple files with the same name are found within a directory, a warning is logged and the lexicographically first match is used.
|
|
176
|
+
|
|
177
|
+
#### Example project layout
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
my-project/
|
|
181
|
+
├── src/ # --code_dir
|
|
182
|
+
│ ├── network_traffic.spl2
|
|
183
|
+
│ └── dns_lookup.spl2
|
|
184
|
+
└── tests/ # --test_dir
|
|
185
|
+
├── network_traffic.test.json
|
|
186
|
+
├── dns_lookup.test.json
|
|
187
|
+
└── dns_lookup.test.spl2
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Run with:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
spl2_tests_run cli --test_dir tests --code_dir src
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Backward compatibility
|
|
197
|
+
|
|
198
|
+
When `--code_dir` is not specified (or both flags point to the same directory), the framework behaves exactly as before — code modules are found next to their test files via the same recursive search.
|
|
199
|
+
|
|
200
|
+
#### Applies to all test types
|
|
201
|
+
|
|
202
|
+
The `--code_dir` option works for box tests, unit tests, and single SPL2 file execution.
|
|
203
|
+
|
|
157
204
|
## Executing a spl2 file
|
|
158
205
|
|
|
159
206
|
This framework also supports executing a single spl2 file and prints the results in command line as well as a log file.
|
|
@@ -177,6 +224,7 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
177
224
|
|
|
178
225
|
* `--test_dir` - Path in which the template file and module.json are available. If not provided, it will look for the
|
|
179
226
|
current directory for the template file and module.json file
|
|
227
|
+
* `--code_dir` - Path where SPL2 code modules are located, if different from `--test_dir`. Falls back to `--test_dir` when not set. See [Separate code and test directories](#separate-code-and-test-directories).
|
|
180
228
|
* `--template_file` - The spl2 template file to execute
|
|
181
229
|
* `--sample_file` - A file containing all the samples required for the template file. If not provided, it will look for
|
|
182
230
|
the samples in module.json file of the corresponding template file
|
|
@@ -206,6 +254,76 @@ Content of this file will also be printed to stdout.
|
|
|
206
254
|
This checks can be applied only to box tests, as assertions which are used in unit tests may impact spl2 pipeline
|
|
207
255
|
performance.
|
|
208
256
|
|
|
257
|
+
### Check splunk results
|
|
258
|
+
|
|
259
|
+
When set (`--check_splunk_results=<option>`), box tests (after running the SPL2 pipeline) send pipeline output to the configured Splunk instance and validate results there instead of comparing only in-memory expected vs actual.
|
|
260
|
+
|
|
261
|
+
| Value | Behavior |
|
|
262
|
+
|-------|----------|
|
|
263
|
+
| **CIM** | For each output event, push to Splunk, query by host, then validate that the returned event has the expected CIM fields (from `expected_cim_fields.cim_fields` in the box test). Uses `validate_compatibility()`. Skips the test if the box test has no `cim_fields`. |
|
|
264
|
+
| **TA** | For each output event, push to Splunk, query by host, then assert that the returned event equals the expected output event for that index. |
|
|
265
|
+
|
|
266
|
+
For **TA** only, you can name top-level fields to drop on **both** the queried event and each `expected_destination_result` row by adding **`ignore_fields_in_splunk_check`**: an object mapping **field name → short reason**. Use the same key under `test` in each entry in `*.test.json` (same file as `expected_destination_result`):
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
"test": {
|
|
270
|
+
"source": "...",
|
|
271
|
+
"expected_destination_result": [ ... ],
|
|
272
|
+
"ignore_fields_in_splunk_check": {
|
|
273
|
+
"_raw": "Raw event differs after Splunk indexing",
|
|
274
|
+
"_time": "Timestamp may vary due to ingestion latency"
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
If the key is omitted, no fields are ignored. Used only when `--check_splunk_results=TA` (ignored for other modes and for the default
|
|
281
|
+
in-memory box test assert). If the file or key is missing, no fields are ignored.
|
|
282
|
+
|
|
283
|
+
#### Configuration:
|
|
284
|
+
|
|
285
|
+
`SPLUNK_INSTANCE` in `spl2_test_config.json`
|
|
286
|
+
|
|
287
|
+
New top-level key for the Splunk instance used by `--check_splunk_results`:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"SPLUNK_INSTANCE": {
|
|
292
|
+
"ip": "<hostname-or-ip>",
|
|
293
|
+
"port": 8088,
|
|
294
|
+
"api_port": 8089,
|
|
295
|
+
"username": "<splunk-user>",
|
|
296
|
+
"password": "<splunk-password>",
|
|
297
|
+
"index": "<index-name>",
|
|
298
|
+
"hec_token": "<hec-token>"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
- **ip** – Splunk server host (e.g. `localhost` or hostname).
|
|
304
|
+
- **port** – HEC port (e.g. 8088).
|
|
305
|
+
- **api_port** – Management/API port (e.g. 8089) for Search API and index creation.
|
|
306
|
+
- **username** / **password** – Used for Search API and index creation (HTTP Basic).
|
|
307
|
+
- **index** – Index where HEC events are sent and then searched.
|
|
308
|
+
- **hec_token** – Token for HEC (`Authorization: Splunk <token>`).
|
|
309
|
+
|
|
310
|
+
Do not commit real credentials. Prefer environment variables (below) or a local config that is not in version control.
|
|
311
|
+
|
|
312
|
+
#### Environment variables
|
|
313
|
+
|
|
314
|
+
Any `SPLUNK_INSTANCE` field can be set via the environment; config file values override these only when non-empty.
|
|
315
|
+
|
|
316
|
+
| Variable | Maps to |
|
|
317
|
+
|----------|--------|
|
|
318
|
+
| `SPL2_TF_SPLUNK_INSTANCE_IP` | `ip` |
|
|
319
|
+
| `SPL2_TF_SPLUNK_INSTANCE_PORT` | `port` |
|
|
320
|
+
| `SPL2_TF_SPLUNK_INSTANCE_API_PORT` | `api_port` |
|
|
321
|
+
| `SPL2_TF_SPLUNK_INSTANCE_USERNAME` | `username` |
|
|
322
|
+
| `SPL2_TF_SPLUNK_INSTANCE_PASSWORD` | `password` |
|
|
323
|
+
| `SPL2_TF_SPLUNK_INSTANCE_INDEX` | `index` |
|
|
324
|
+
| `SPL2_TF_SPLUNK_INSTANCE_HEC_TOKEN` | `hec_token` |
|
|
325
|
+
|
|
326
|
+
|
|
209
327
|
## Format all files
|
|
210
328
|
To format all files run:
|
|
211
329
|
|
|
@@ -98,12 +98,15 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
98
98
|
|
|
99
99
|
Additionally, the following options are supported:
|
|
100
100
|
|
|
101
|
+
* `--test_dir` - directory where test files (`.test.json`, `.test.spl2`, `module.json`) are located. Defaults to the current working directory. Discovery is recursive.
|
|
102
|
+
* `--code_dir` - directory where SPL2 code modules (`.spl2`) are located. Defaults to the current working directory. Falls back to `--test_dir` if the module is not found. See [Separate code and test directories](#separate-code-and-test-directories) for details.
|
|
101
103
|
* `--ignore_empty_strings` - to ignore empty strings in the results
|
|
102
104
|
* `--ignore_additional_fields_in_actual` - to ignore fields present in actual results but not in expected results (useful when actual results contain extra fields that should not affect comparison)
|
|
103
105
|
* `--create_comparison_sheet` - to create a comparison sheet in `comparison_box_test` folder using actual and expected outputs (works only when running box tests)
|
|
104
106
|
* `--cli_bench` - when running tests with CLI, this option enables benchmarking mode by adding `-b` and `-n` flags to the spl2-processor-cli command. The value specifies the number of events to test (e.g., `--cli_bench=1000` will add `-b -n 1000` to the CLI command). **In bench mode, all tests always succeed and CLI output is printed to logs** - this is useful for performance testing and benchmarking. This is only applicable when using `--type cli`.
|
|
107
|
+
* `--check_splunk_results` - option to validate box test output by sending events to a Splunk instance (using HEC) and querying them back, with support for **CIM** (Common Information Model) and **TA** (Technology Add-on) checks.
|
|
105
108
|
|
|
106
|
-
Note: The `pytest.ini.sample` file allows you to define command parameters. Just update the configurations, rename the
|
|
109
|
+
* Note: The `pytest.ini.sample` file allows you to define command parameters. Just update the configurations, rename the
|
|
107
110
|
file by removing the `.sample` extension, and execute the command.
|
|
108
111
|
|
|
109
112
|
### Run tests in IDE [PyCharm]
|
|
@@ -118,10 +121,53 @@ Sample configuration which may be used:
|
|
|
118
121
|
- Parameters:
|
|
119
122
|
`--type [cli | splunk | cloud] --test_dir /tests/resources -o log_cli=true --log-cli-level=INFO --verbose`
|
|
120
123
|
- If test dir is not specified, current working directory will be used
|
|
124
|
+
- Use `--code_dir` if SPL2 code modules are in a separate directory from tests
|
|
121
125
|
- If necessary another pytest options can be added
|
|
122
126
|
|
|
123
127
|
Note: It's necessary to set "pytest" as default test runner in PyCharm settings
|
|
124
128
|
|
|
129
|
+
### Separate code and test directories
|
|
130
|
+
|
|
131
|
+
By default, the framework expects SPL2 code modules (`.spl2` files) to live alongside their test files. The `--code_dir` option allows you to keep code and tests in separate directory trees.
|
|
132
|
+
|
|
133
|
+
#### How it works
|
|
134
|
+
|
|
135
|
+
| What is searched | Primary directory | Fallback directory |
|
|
136
|
+
|------------------|-------------------|--------------------|
|
|
137
|
+
| **Code modules** (`.spl2`) | `--code_dir` | `--test_dir` |
|
|
138
|
+
| **Test files** (`.test.json`, `.test.spl2`) | `--test_dir` | `--code_dir` |
|
|
139
|
+
|
|
140
|
+
Both searches are recursive. If the primary directory has no results, the framework falls back to the other directory and logs the action.
|
|
141
|
+
|
|
142
|
+
When multiple files with the same name are found within a directory, a warning is logged and the lexicographically first match is used.
|
|
143
|
+
|
|
144
|
+
#### Example project layout
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
my-project/
|
|
148
|
+
├── src/ # --code_dir
|
|
149
|
+
│ ├── network_traffic.spl2
|
|
150
|
+
│ └── dns_lookup.spl2
|
|
151
|
+
└── tests/ # --test_dir
|
|
152
|
+
├── network_traffic.test.json
|
|
153
|
+
├── dns_lookup.test.json
|
|
154
|
+
└── dns_lookup.test.spl2
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Run with:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
spl2_tests_run cli --test_dir tests --code_dir src
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Backward compatibility
|
|
164
|
+
|
|
165
|
+
When `--code_dir` is not specified (or both flags point to the same directory), the framework behaves exactly as before — code modules are found next to their test files via the same recursive search.
|
|
166
|
+
|
|
167
|
+
#### Applies to all test types
|
|
168
|
+
|
|
169
|
+
The `--code_dir` option works for box tests, unit tests, and single SPL2 file execution.
|
|
170
|
+
|
|
125
171
|
## Executing a spl2 file
|
|
126
172
|
|
|
127
173
|
This framework also supports executing a single spl2 file and prints the results in command line as well as a log file.
|
|
@@ -145,6 +191,7 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
145
191
|
|
|
146
192
|
* `--test_dir` - Path in which the template file and module.json are available. If not provided, it will look for the
|
|
147
193
|
current directory for the template file and module.json file
|
|
194
|
+
* `--code_dir` - Path where SPL2 code modules are located, if different from `--test_dir`. Falls back to `--test_dir` when not set. See [Separate code and test directories](#separate-code-and-test-directories).
|
|
148
195
|
* `--template_file` - The spl2 template file to execute
|
|
149
196
|
* `--sample_file` - A file containing all the samples required for the template file. If not provided, it will look for
|
|
150
197
|
the samples in module.json file of the corresponding template file
|
|
@@ -174,6 +221,76 @@ Content of this file will also be printed to stdout.
|
|
|
174
221
|
This checks can be applied only to box tests, as assertions which are used in unit tests may impact spl2 pipeline
|
|
175
222
|
performance.
|
|
176
223
|
|
|
224
|
+
### Check splunk results
|
|
225
|
+
|
|
226
|
+
When set (`--check_splunk_results=<option>`), box tests (after running the SPL2 pipeline) send pipeline output to the configured Splunk instance and validate results there instead of comparing only in-memory expected vs actual.
|
|
227
|
+
|
|
228
|
+
| Value | Behavior |
|
|
229
|
+
|-------|----------|
|
|
230
|
+
| **CIM** | For each output event, push to Splunk, query by host, then validate that the returned event has the expected CIM fields (from `expected_cim_fields.cim_fields` in the box test). Uses `validate_compatibility()`. Skips the test if the box test has no `cim_fields`. |
|
|
231
|
+
| **TA** | For each output event, push to Splunk, query by host, then assert that the returned event equals the expected output event for that index. |
|
|
232
|
+
|
|
233
|
+
For **TA** only, you can name top-level fields to drop on **both** the queried event and each `expected_destination_result` row by adding **`ignore_fields_in_splunk_check`**: an object mapping **field name → short reason**. Use the same key under `test` in each entry in `*.test.json` (same file as `expected_destination_result`):
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
"test": {
|
|
237
|
+
"source": "...",
|
|
238
|
+
"expected_destination_result": [ ... ],
|
|
239
|
+
"ignore_fields_in_splunk_check": {
|
|
240
|
+
"_raw": "Raw event differs after Splunk indexing",
|
|
241
|
+
"_time": "Timestamp may vary due to ingestion latency"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
If the key is omitted, no fields are ignored. Used only when `--check_splunk_results=TA` (ignored for other modes and for the default
|
|
248
|
+
in-memory box test assert). If the file or key is missing, no fields are ignored.
|
|
249
|
+
|
|
250
|
+
#### Configuration:
|
|
251
|
+
|
|
252
|
+
`SPLUNK_INSTANCE` in `spl2_test_config.json`
|
|
253
|
+
|
|
254
|
+
New top-level key for the Splunk instance used by `--check_splunk_results`:
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"SPLUNK_INSTANCE": {
|
|
259
|
+
"ip": "<hostname-or-ip>",
|
|
260
|
+
"port": 8088,
|
|
261
|
+
"api_port": 8089,
|
|
262
|
+
"username": "<splunk-user>",
|
|
263
|
+
"password": "<splunk-password>",
|
|
264
|
+
"index": "<index-name>",
|
|
265
|
+
"hec_token": "<hec-token>"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- **ip** – Splunk server host (e.g. `localhost` or hostname).
|
|
271
|
+
- **port** – HEC port (e.g. 8088).
|
|
272
|
+
- **api_port** – Management/API port (e.g. 8089) for Search API and index creation.
|
|
273
|
+
- **username** / **password** – Used for Search API and index creation (HTTP Basic).
|
|
274
|
+
- **index** – Index where HEC events are sent and then searched.
|
|
275
|
+
- **hec_token** – Token for HEC (`Authorization: Splunk <token>`).
|
|
276
|
+
|
|
277
|
+
Do not commit real credentials. Prefer environment variables (below) or a local config that is not in version control.
|
|
278
|
+
|
|
279
|
+
#### Environment variables
|
|
280
|
+
|
|
281
|
+
Any `SPLUNK_INSTANCE` field can be set via the environment; config file values override these only when non-empty.
|
|
282
|
+
|
|
283
|
+
| Variable | Maps to |
|
|
284
|
+
|----------|--------|
|
|
285
|
+
| `SPL2_TF_SPLUNK_INSTANCE_IP` | `ip` |
|
|
286
|
+
| `SPL2_TF_SPLUNK_INSTANCE_PORT` | `port` |
|
|
287
|
+
| `SPL2_TF_SPLUNK_INSTANCE_API_PORT` | `api_port` |
|
|
288
|
+
| `SPL2_TF_SPLUNK_INSTANCE_USERNAME` | `username` |
|
|
289
|
+
| `SPL2_TF_SPLUNK_INSTANCE_PASSWORD` | `password` |
|
|
290
|
+
| `SPL2_TF_SPLUNK_INSTANCE_INDEX` | `index` |
|
|
291
|
+
| `SPL2_TF_SPLUNK_INSTANCE_HEC_TOKEN` | `hec_token` |
|
|
292
|
+
|
|
293
|
+
|
|
177
294
|
## Format all files
|
|
178
295
|
To format all files run:
|
|
179
296
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
[tool.poetry]
|
|
17
17
|
name = "spl2-testing-framework"
|
|
18
|
-
version = "1.
|
|
18
|
+
version = "1.7.0"
|
|
19
19
|
description = "SPL2 Testing Framework"
|
|
20
20
|
authors = ["mgazda-splunk <mgazda@splunk.com>"]
|
|
21
21
|
readme = "README.md"
|
|
@@ -52,6 +52,7 @@ numpy = [
|
|
|
52
52
|
{ version = ">=1.26,<2.0", python = ">=3.13" }
|
|
53
53
|
]
|
|
54
54
|
openpyxl = "^3.1.2"
|
|
55
|
+
splunk-sdk = "^2.1.1"
|
|
55
56
|
|
|
56
57
|
[tool.poetry.group.dev.dependencies]
|
|
57
58
|
pre-commit = "*"
|
{spl2_testing_framework-1.5.1 → spl2_testing_framework-1.7.0}/spl2_testing_framework/conftest.py
RENAMED
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
18
|
+
from collections import defaultdict
|
|
18
19
|
|
|
20
|
+
import pandas as pd
|
|
19
21
|
import pytest
|
|
20
22
|
|
|
21
23
|
from .tools.results import Results
|
|
24
|
+
from .tools.splunk_integration.splunk_handler import SplunkHandler
|
|
22
25
|
|
|
23
26
|
pytest.register_assert_rewrite("spl2_testing_framework")
|
|
24
27
|
|
|
@@ -48,9 +51,11 @@ SINGLE_SPL2_FILE = "single_spl2_file"
|
|
|
48
51
|
IGNORE_EMPTY_STRINGS = "ignore_empty_strings"
|
|
49
52
|
IGNORE_ADDITIONAL_FIELDS_IN_ACTUAL = "ignore_additional_fields_in_actual"
|
|
50
53
|
CREATE_COMPARISON_SHEET = "create_comparison_sheet"
|
|
54
|
+
CHECK_SPLUNK_RESULTS = "check_splunk_results"
|
|
51
55
|
PERFORMANCE_CHECK = "performance_check"
|
|
52
56
|
TEST_TYPE = "type"
|
|
53
57
|
TEST_DIR = "test_dir"
|
|
58
|
+
CODE_DIR = "code_dir"
|
|
54
59
|
LIMIT_TESTS = "limit_tests"
|
|
55
60
|
CLI_BENCH = "cli_bench"
|
|
56
61
|
|
|
@@ -66,6 +71,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
66
71
|
def pytest_addoption(parser):
|
|
67
72
|
parser.addoption(f"--{TEST_TYPE}", action="store", default="cli")
|
|
68
73
|
parser.addoption(f"--{TEST_DIR}", action="store", default=".")
|
|
74
|
+
parser.addoption(f"--{CODE_DIR}", action="store", default=".")
|
|
69
75
|
parser.addoption(
|
|
70
76
|
f"--{PERFORMANCE_CHECK}",
|
|
71
77
|
action="store",
|
|
@@ -75,6 +81,9 @@ def pytest_addoption(parser):
|
|
|
75
81
|
parser.addoption("--template_file", action="store", default=None)
|
|
76
82
|
parser.addoption("--sample_file", action="store", default=None)
|
|
77
83
|
parser.addoption("--sample_delimiter", action="store", default="\n")
|
|
84
|
+
parser.addoption(
|
|
85
|
+
f"--{CHECK_SPLUNK_RESULTS}", action="store", type=str, default=None
|
|
86
|
+
)
|
|
78
87
|
parser.addoption(f"--{CREATE_COMPARISON_SHEET}", action="store_true", default=False)
|
|
79
88
|
parser.addoption(f"--{IGNORE_EMPTY_STRINGS}", action="store_true")
|
|
80
89
|
parser.addoption(f"--{IGNORE_ADDITIONAL_FIELDS_IN_ACTUAL}", action="store_true")
|
|
@@ -86,13 +95,16 @@ def pytest_generate_tests(metafunc):
|
|
|
86
95
|
"""This function is used for dynamic generation of tests based on the test files in the directory specified by the
|
|
87
96
|
--test_dir option"""
|
|
88
97
|
test_dir = metafunc.config.getoption(TEST_DIR)
|
|
98
|
+
code_dir = metafunc.config.getoption(CODE_DIR)
|
|
89
99
|
performance_check = metafunc.config.getoption(PERFORMANCE_CHECK)
|
|
90
100
|
limit_tests = metafunc.config.getoption(LIMIT_TESTS)
|
|
91
101
|
_set_results_options(metafunc)
|
|
92
102
|
|
|
93
103
|
if BOX_TEST in metafunc.fixturenames:
|
|
94
104
|
_LOGGER.info("Collecting Box Tests")
|
|
95
|
-
module_tests = BoxTestDiscovery(
|
|
105
|
+
module_tests = BoxTestDiscovery(
|
|
106
|
+
test_dir, performance_check, limit_tests, code_dir=code_dir
|
|
107
|
+
)
|
|
96
108
|
module_tests.discover_tests()
|
|
97
109
|
metafunc.parametrize(
|
|
98
110
|
"%s" % BOX_TEST, module_tests.tests, ids=module_tests.tests.get_ids()
|
|
@@ -112,6 +124,7 @@ def pytest_generate_tests(metafunc):
|
|
|
112
124
|
sample_delimiter,
|
|
113
125
|
performance_check=performance_check,
|
|
114
126
|
limit_tests=limit_tests,
|
|
127
|
+
code_dir=code_dir,
|
|
115
128
|
)
|
|
116
129
|
try:
|
|
117
130
|
spl2_pipelines.discover_tests()
|
|
@@ -125,7 +138,7 @@ def pytest_generate_tests(metafunc):
|
|
|
125
138
|
|
|
126
139
|
if UNIT_TEST in metafunc.fixturenames:
|
|
127
140
|
_LOGGER.info("Collecting Unit Tests")
|
|
128
|
-
unit_tests = UTDiscovery(test_dir, limit_tests)
|
|
141
|
+
unit_tests = UTDiscovery(test_dir, limit_tests, code_dir=code_dir)
|
|
129
142
|
unit_tests.discover_tests()
|
|
130
143
|
metafunc.parametrize(
|
|
131
144
|
UNIT_TEST, unit_tests.tests, ids=unit_tests.tests.get_ids()
|
|
@@ -139,6 +152,8 @@ def spl2_test_runner(
|
|
|
139
152
|
basic_authentication,
|
|
140
153
|
bearer_token_authentication,
|
|
141
154
|
run_configurations,
|
|
155
|
+
splunk_handler,
|
|
156
|
+
stats,
|
|
142
157
|
):
|
|
143
158
|
"""This fixture is used to create a test runner object based on the test type specified by the --type option
|
|
144
159
|
3 types of tests are supported: 'splunk', 'cloud', 'cli'
|
|
@@ -160,7 +175,7 @@ def spl2_test_runner(
|
|
|
160
175
|
)
|
|
161
176
|
)
|
|
162
177
|
|
|
163
|
-
x = SPL2TestRunner(search_client, run_configurations)
|
|
178
|
+
x = SPL2TestRunner(search_client, run_configurations, splunk_handler, stats)
|
|
164
179
|
return x
|
|
165
180
|
|
|
166
181
|
|
|
@@ -172,11 +187,35 @@ def run_configurations(request):
|
|
|
172
187
|
f"--{CREATE_COMPARISON_SHEET}"
|
|
173
188
|
),
|
|
174
189
|
CLI_BENCH: request.config.getoption(f"--{CLI_BENCH}"),
|
|
190
|
+
CHECK_SPLUNK_RESULTS: request.config.getoption(f"--{CHECK_SPLUNK_RESULTS}"),
|
|
175
191
|
}
|
|
176
192
|
|
|
177
193
|
|
|
178
194
|
@pytest.fixture(scope="session")
|
|
179
|
-
def
|
|
195
|
+
def splunk_instance_config():
|
|
196
|
+
splunk_instance_ip = os.environ.get("SPL2_TF_SPLUNK_INSTANCE_IP", None)
|
|
197
|
+
splunk_instance_port = os.environ.get("SPL2_TF_SPLUNK_INSTANCE_PORT", None)
|
|
198
|
+
splunk_instance_api_port = os.environ.get("SPL2_TF_SPLUNK_INSTANCE_API_PORT", None)
|
|
199
|
+
splunk_instance_username = os.environ.get("SPL2_TF_SPLUNK_INSTANCE_USERNAME", None)
|
|
200
|
+
splunk_instance_password = os.environ.get("SPL2_TF_SPLUNK_INSTANCE_PASSWORD", None)
|
|
201
|
+
splunk_instance_index = os.environ.get("SPL2_TF_SPLUNK_INSTANCE_INDEX", None)
|
|
202
|
+
splunk_instance_hec_token = os.environ.get(
|
|
203
|
+
"SPL2_TF_SPLUNK_INSTANCE_HEC_TOKEN", None
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"ip": splunk_instance_ip,
|
|
208
|
+
"port": splunk_instance_port,
|
|
209
|
+
"api_port": splunk_instance_api_port,
|
|
210
|
+
"username": splunk_instance_username,
|
|
211
|
+
"password": splunk_instance_password,
|
|
212
|
+
"index": splunk_instance_index,
|
|
213
|
+
"hec_token": splunk_instance_hec_token,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@pytest.fixture(scope="session")
|
|
218
|
+
def config(splunk_instance_config):
|
|
180
219
|
"""This fixture is used to read the configuration from the environment variables or from the spl2_test_config.json
|
|
181
220
|
Configuration set using environment variables will be overwritten by settings from spl2_test_config.json file,
|
|
182
221
|
however empty values in spl2_test_config.json will be ignored.
|
|
@@ -219,6 +258,12 @@ def config():
|
|
|
219
258
|
if conf_file.get("cloud_instance", None):
|
|
220
259
|
cloud_instance = conf_file["cloud_instance"]
|
|
221
260
|
|
|
261
|
+
for k, v in splunk_instance_config.items():
|
|
262
|
+
if v is None:
|
|
263
|
+
splunk_instance_config[k] = conf_file.get("SPLUNK_INSTANCE", {}).get(
|
|
264
|
+
k, None
|
|
265
|
+
)
|
|
266
|
+
|
|
222
267
|
return {
|
|
223
268
|
"host": host,
|
|
224
269
|
"port": port,
|
|
@@ -227,6 +272,7 @@ def config():
|
|
|
227
272
|
"bearer_token": bearer_token,
|
|
228
273
|
"tenant": tenant,
|
|
229
274
|
"cloud_instance": cloud_instance,
|
|
275
|
+
"SPLUNK_INSTANCE": splunk_instance_config,
|
|
230
276
|
}
|
|
231
277
|
|
|
232
278
|
|
|
@@ -251,6 +297,49 @@ def bearer_token_authentication(config):
|
|
|
251
297
|
return HTTPBearerTokenAuth(token)
|
|
252
298
|
|
|
253
299
|
|
|
300
|
+
@pytest.fixture(scope="session")
|
|
301
|
+
def splunk_token_authentication(config):
|
|
302
|
+
class SplunkTokenAuth(AuthBase):
|
|
303
|
+
"""Attaches HTTP Basic Authentication to the given Request object."""
|
|
304
|
+
|
|
305
|
+
def __init__(self, token):
|
|
306
|
+
self.token = token
|
|
307
|
+
|
|
308
|
+
def __call__(self, r):
|
|
309
|
+
r.headers["Authorization"] = f"Splunk {self.token}"
|
|
310
|
+
return r
|
|
311
|
+
|
|
312
|
+
token = config["SPLUNK_INSTANCE"].get("hec_token", "")
|
|
313
|
+
splunk_auth = SplunkTokenAuth(token)
|
|
314
|
+
return splunk_auth
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@pytest.fixture(scope="session")
|
|
318
|
+
def splunk_handler(config, splunk_token_authentication):
|
|
319
|
+
config["SPLUNK_INSTANCE"][
|
|
320
|
+
"url"
|
|
321
|
+
] = f"https://{config['SPLUNK_INSTANCE']['ip']}:{config['SPLUNK_INSTANCE']['port']}/services/collector/raw"
|
|
322
|
+
config["SPLUNK_INSTANCE"][
|
|
323
|
+
"index_url"
|
|
324
|
+
] = f"https://{config['SPLUNK_INSTANCE']['ip']}:{config['SPLUNK_INSTANCE']['api_port']}/services/data/indexes"
|
|
325
|
+
|
|
326
|
+
sh = SplunkHandler(config, splunk_token_authentication)
|
|
327
|
+
return sh
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@pytest.fixture(scope="session")
|
|
331
|
+
def stats():
|
|
332
|
+
s = defaultdict(lambda: defaultdict(list))
|
|
333
|
+
yield s
|
|
334
|
+
|
|
335
|
+
for template, v in s.items():
|
|
336
|
+
x = pd.DataFrame(v)
|
|
337
|
+
_LOGGER.info(" *** *** ***")
|
|
338
|
+
_LOGGER.info("Collected statistics: ")
|
|
339
|
+
_LOGGER.info("Template name: %s ", template)
|
|
340
|
+
_LOGGER.info(x.describe())
|
|
341
|
+
|
|
342
|
+
|
|
254
343
|
def _set_results_options(metafunc):
|
|
255
344
|
ignore_empty_strings = metafunc.config.getoption(IGNORE_EMPTY_STRINGS)
|
|
256
345
|
Results._ignore_empty_strings = ignore_empty_strings
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"host": "",
|
|
3
|
+
"cloud_instance": "",
|
|
4
|
+
"tenant": "",
|
|
5
|
+
"bearer_token": "",
|
|
6
|
+
"port": "",
|
|
7
|
+
"user": "",
|
|
8
|
+
"password": "",
|
|
9
|
+
"SPLUNK_INSTANCE": {
|
|
10
|
+
"ip": "",
|
|
11
|
+
"port": "",
|
|
12
|
+
"api_port": "",
|
|
13
|
+
"username": "",
|
|
14
|
+
"password": "",
|
|
15
|
+
"index": "",
|
|
16
|
+
"hec_token": ""
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -75,8 +75,13 @@ class CLISearchClient(SearchClient):
|
|
|
75
75
|
# Normal mode - raise errors as before
|
|
76
76
|
if result.stderr:
|
|
77
77
|
raise RuntimeError(result.stderr)
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
try:
|
|
79
|
+
job.result = json.loads(result.stdout)
|
|
80
|
+
except json.decoder.JSONDecodeError:
|
|
81
|
+
_LOGGER.error(
|
|
82
|
+
"Can't decode output for %s. Details: %s ", job.test_name, result
|
|
83
|
+
)
|
|
84
|
+
raise
|
|
80
85
|
return
|
|
81
86
|
|
|
82
87
|
def _print_bench_results(self, job, result):
|