Skip to content

Commit 2f50bd5

Browse files
djptekjsorianoandrewkrohebeahan
authored
[system testing] Implement assertion for expected number of events (#960)
* placeholder in docs * test-expected-event-count-config.yml in httpjson OK * add detail to system testing docs * clarify README.md * refactor runner * remove trailing space * global change SystemTest.ExpectedEventCount to Assert.EventsCount * Update docs/howto/system_testing.md Co-authored-by: Jaime Soriano Pastor <[email protected]> * add explanation to example * refactor assertion * assert hits, rather than events * simplify comparison to bool constant * WIP adding new test * rollback changes to generated.log * change input in test config * using multiinput as template * changed to tcp type * revert hits to tcp * set hit_count to desired 100 * Update internal/testrunner/runners/system/runner.go Thanks @andrewkroh Co-authored-by: Andrew Kroh <[email protected]> * move declaration inside if block * wait for expected number of events if hit_count is used * Apply suggestions from code review Co-authored-by: Jaime Soriano Pastor <[email protected]> * working through CI errors * adding missing fields --------- Co-authored-by: Jaime Soriano Pastor <[email protected]> Co-authored-by: Andrew Kroh <[email protected]> Co-authored-by: Eric Beahan <[email protected]>
1 parent ab4357c commit 2f50bd5

File tree

14 files changed

+356
-0
lines changed

14 files changed

+356
-0
lines changed

docs/howto/system_testing.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,55 @@ you can use the `input` option to select the stream to test. The first stream
395395
whose input type matches the `input` value will be tested. By default, the first
396396
stream declared in the manifest will be tested.
397397

398+
To add an assertion on the number of hits in a given system test, consider this example from the `httpjson/generic` data stream's `test-expected-hit-count-config.yml`, shown below.
399+
400+
```
401+
input: httpjson
402+
service: httpjson
403+
data_stream:
404+
vars:
405+
data_stream.dataset: httpjson.generic
406+
username: test
407+
password: test
408+
request_url: http://{{Hostname}}:{{Port}}/testexpectedhits/api
409+
response_split: |-
410+
target: body.hits
411+
type: array
412+
keep_parent: false
413+
assert:
414+
hit_count: 3
415+
```
416+
417+
The `data_stream.vars.request_url` corresponds to a test-stub path in the `_dev/deploy/docker/files/config.yml` file.
418+
419+
```
420+
- path: /testexpectedhits/api
421+
methods: ["GET"]
422+
request_headers:
423+
Authorization:
424+
- "Basic dGVzdDp0ZXN0"
425+
responses:
426+
- status_code: 200
427+
headers:
428+
Content-Type:
429+
- "application/json; charset=utf-8"
430+
body: |-
431+
{"total":3,"hits":[{"message": "success"},{"message": "success"},{"message": "success"}]}
432+
```
433+
434+
Handlebar syntax in `httpjson.yml.hbs`
435+
436+
```
437+
{{#if response_split}}
438+
response.split:
439+
{{response_split}}
440+
{{/if}}
441+
```
442+
443+
inserts the value of `response_split` from the test configuration into the integration, in this case, ensuring the `total.hits[]` array from the test-stub response yields 3 hits.
444+
445+
Returning to `test-expected-hit-count-config.yml`, when `assert.hit_count` is defined and `> 0` the test will assert that the number of hits in the array matches that value and fail when this is not true.
446+
398447
#### Placeholders
399448

400449
The `SERVICE_LOGS_DIR` placeholder is not the only one available for use in a data stream's `test-<test_name>-config.yml` file. The complete list of available placeholders is shown below.

internal/testrunner/runners/system/runner.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,11 @@ func (r *runner) runTest(config *testConfig, ctxt servicedeployer.ServiceContext
450450

451451
var err error
452452
docs, err = r.getDocs(dataStream)
453+
454+
if config.Assert.HitCount > 0 {
455+
return len(docs) >= config.Assert.HitCount, err
456+
}
457+
453458
return len(docs) > 0, err
454459
}, waitForDataTimeout)
455460
if err != nil {
@@ -486,6 +491,11 @@ func (r *runner) runTest(config *testConfig, ctxt servicedeployer.ServiceContext
486491
return result.WithError(err)
487492
}
488493

494+
// Check Hit Count within docs, if 0 then it has not been specified
495+
if assertionPass, message := assertHitCount(config.Assert.HitCount, docs); !assertionPass {
496+
result.FailureMsg = message
497+
}
498+
489499
return result.WithSuccess()
490500
}
491501

@@ -860,6 +870,17 @@ func validateFields(docs []common.MapStr, fieldsValidator *fields.Validator, dat
860870
return nil
861871
}
862872

873+
func assertHitCount(expected int, docs []common.MapStr) (pass bool, message string) {
874+
if expected != 0 {
875+
observed := len(docs)
876+
logger.Debugf("assert hit count expected %d, observed %d", expected, observed)
877+
if observed != expected {
878+
return false, fmt.Sprintf("observed hit count %d did not match expected hit count %d", observed, expected)
879+
}
880+
}
881+
return true, ""
882+
}
883+
863884
func (r *runner) selectVariants(variantsFile *servicedeployer.VariantsFile) []string {
864885
if variantsFile == nil || variantsFile.Variants == nil {
865886
return []string{""} // empty variants file switches to no-variant mode

internal/testrunner/runners/system/test_config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ type testConfig struct {
3838
Vars common.MapStr `config:"vars"`
3939
} `config:"data_stream"`
4040

41+
Assert struct {
42+
// Expected number of hits for a given test
43+
HitCount int `config:"hit_count"`
44+
} `config:"assert"`
45+
4146
// NumericKeywordFields holds a list of fields that have keyword
4247
// type but can be ingested as numeric type.
4348
NumericKeywordFields []string `config:"numeric_keyword_fields"`
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: '2.3'
2+
services:
3+
test-hits:
4+
image: akroh/stream:v0.0.1
5+
volumes:
6+
- ./logs:/logs:ro
7+
command: log --start-signal=SIGHUP --delay=5s --addr elastic-agent:9999 -p=tcp /logs/generated.log
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
ntpd[1001]: kernel time sync enabled utl
2+
restorecond: : Reset file context quasiarc: liqua
3+
auditd[5699]: Audit daemon rotating log files
4+
anacron[5066]: Normal exit ehend
5+
restorecond: : Reset file context vol: luptat
6+
heartbeat: : <<eumiu.medium> Processing command: accept
7+
restorecond: : Reset file context nci: ofdeFin
8+
auditd[6668]: Audit daemon rotating log files
9+
anacron[1613]: Normal exit mvolu
10+
ntpd[2959]: ntpd gelit-r tatno
11+
anacron[654]: Updated timestamp for job rmagni to sit
12+
dmd: : <<tenima.very-high> Health state for metric"seq3874.mail.domain" "quid" changed to "fug", reason: "success"
13+
auditd[2067]: Audit daemon rotating log files
14+
pm[5969]: <<tquovol.very-high> check_license_validity(), tae
15+
logrotate: : ALERT exited abnormally with temUten
16+
sshd: : <<dun.medium> error: Bind to port Duisau on psum failed: failure
17+
configd: : <<end.medium> itaut@rveli: command: accept
18+
authd: : <<luptat.low> authd_signal_handler(), quam
19+
xinetd[6547]: Started working: onproide available services
20+
logrotate: : ALERT exited abnormally with tfug
21+
heartbeat: : <<urE.medium> Processing command: deny
22+
rsyslogd: : Warning: rehe
23+
sshd: : <<stiae.medium> error: Bind to port erc on amqu failed: unknown
24+
ntpd[4515]: ntpd emp-r aperia
25+
restorecond: : Reset file context run: vol
26+
logrotate: : ALERT exited abnormally with mporain
27+
heartbeat: : <<mpori.very-high> connect: atu
28+
cmd: : <<texp.medium> cmd starting adeseru
29+
cli[7108]: <<-uam.low> tmo@::fficiade:10.2.53.125 : CLI launched
30+
pm[7061]: <<ihilmo.very-high> ntpd will start in tlabo
31+
poller[795]: <<oluptate.low> Querying content system for job results.
32+
runner[6134]: <<edo.very-high> Processing command: allow
33+
epmd: : epmd: epmd running orpor
34+
runner[602]: <<emvel.very-high> Failed to exec olup
35+
shutdown[2807]: shutting down non
36+
configd: : <<ugiatnu.high> sperna@sintocc: command: cancel
37+
auditd[2986]: Audit daemon rotating log files
38+
configd: : <<ccaecat.medium> CREATE onsequ
39+
auditd[1243]: Audit daemon rotating log files
40+
xinetd[6599]: Started working: naal available services
41+
xinetd[5850]: Started working: rQu available services
42+
heartbeat: : <<boree.low> queips: undefined symbol: ncidi
43+
authd: : <<olor.very-high> authd_close(): npr
44+
anacron[6373]: Anacron 1.3962 started on epre
45+
cli[3979]: <<-iduntu.medium> [email protected] : Processing command accept
46+
cmd: : <<amc.medium> cmd starting isiuta
47+
sshd[5227]: dutp(psaquaea:taevita): pam_putenv: ameiusm
48+
ccd: : <<olab.low> Device elitse6672.internal.localdomain: mquisno
49+
runner[1859]: <<tasnulap.high> Failed to exec umSe
50+
shutdown[6110]: shutting down itau
51+
sshd[2415]: PAM lorsita more authentication failure; dolore
52+
rsyslogd: : Warning: tio
53+
cli[802]: <<-gnaaliqu.very-high> velillu@::cteturad:10.18.204.87 : Processing a secure command...
54+
heartbeat: : <<reprehe.high> connect: inimveni
55+
authd: : <<litani.low> authd_close(): psumqu
56+
runner[2558]: <<icabo.high> Failed to exec edquiac
57+
anacron[4538]: Updated timestamp for job remips to uisaute
58+
auditd[6837]: Audit daemon rotating log files
59+
pm[1493]: <<etdolor.high> print_msg(), dic
60+
configd: : <<avolupt.low> Device "itation4168.api.domain" completed command(s) accept ;; CPL generated by Visual Policy Manager: isciv ;rroqu ; nofd ; dipisci
61+
epmd: : epmd: invalid packet size (mquae)
62+
runner[429]: <<corpori.very-high> File reading failed
63+
shutdown[7595]: shutting down emqu
64+
heartbeat: : <<leumiur.low> The HB command is accept
65+
authd: : <<est.very-high> authd_signal_handler(), isetquas
66+
authd: : <<psaqua.medium> authd_signal_handler(), gnaal
67+
logrotate: : ALERT exited abnormally with voluptas
68+
ntpd[627]: ntpd exiting on signal orin
69+
restorecond: : Reset file context ecillu: mmodoc
70+
cli[1140]: <<-abore.high> [email protected] : Processing command: deny
71+
sshd: : bad username mquisn
72+
ntpd[1313]: ntpd derit-r orese
73+
ccd: : <<leumiur.medium> Device Communication Daemon online
74+
rsyslogd: : Warning: moles
75+
restorecond: : Reset file context olup: aco
76+
shutdown[609]: shutting down ser
77+
ntpd[2991]: ntpd orinrep-r quiavol
78+
dmd: : <<quin.medium> inserted device id = sBonor2001.www5.example and serial number = amc into DB
79+
ccd: : <<ame.very-high> ccd_handle_read_failure(), uid
80+
cmd: : <<scivel.high> cmd starting lmolesti
81+
dmd: : <<emaperia.high> inserted device id = ersp6625.internal.domain and serial number = seq into DB
82+
cmd: : <<tanimid.medium> cmd starting uipexe
83+
heartbeat: : <<ore.low> The HB command is cancel
84+
anacron[7360]: Normal exit tperspic
85+
dmd: : <<ict.very-high> Filter on (tetura) things. riosamni
86+
ccd: : <<umetMa.low> Device eleumiu2454.api.local: tat
87+
schedulerd: : <<lumqu.very-high> System time changed, recomputing job run times.
88+
xinetd[3450]: Started working: aconsequ available services
89+
authd: : <<sequat.high> handle_authd unknown message =utemvel
90+
rsyslogd: : Warning: iusm
91+
ntpd[16]: time reset stquido
92+
ccd: : <<aaliq.high> Device olu5333.www.domain: orumSe
93+
anacron[80]: Normal exit ici
94+
ntpd[7612]: kernel time sync enabled nturmag
95+
cli[7128]: eseruntm(lpaquiof:oloreeu): pam_putenv: olor
96+
schedulerd: : <<ici.very-high> Executing Job "tquo" execution iatnu
97+
logrotate: : ALERT exited abnormally with ntut
98+
poller[7151]: <<ess.high> Querying content system for job results.
99+
ntpd[2314]: ntpd litanim-r rQuisaut
100+
heartbeat: : <<metco.high> Processing command: block
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# newer versions go on top
2+
- version: "999.999.999"
3+
changes:
4+
- description: initial release
5+
type: enhancement # can be one of: enhancement, bugfix, breaking-change
6+
link: https://github.com/elastic/elastic-package/pull/960
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
input: tcp
2+
service: test-hits
3+
service_notify_signal: SIGHUP
4+
vars: ~
5+
data_stream:
6+
vars:
7+
tcp_host: 0.0.0.0
8+
tcp_port: 9999
9+
assert:
10+
hit_count: 100
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
host: "{{tcp_host}}:{{tcp_port}}"
2+
tags:
3+
{{#each tags as |tag i|}}
4+
- {{tag}}
5+
{{/each}}
6+
fields_under_root: true
7+
fields:
8+
observer:
9+
vendor: Test
10+
product: test
11+
type: test
12+
{{#contains tags "forwarded"}}
13+
publisher_pipeline.disable_host: true
14+
{{/contains}}
15+
16+
processors:
17+
- add_locale: ~
18+
- add_fields:
19+
target: ''
20+
fields:
21+
ecs.version: 1.7.0
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
paths:
2+
{{#each paths as |path i|}}
3+
- {{path}}
4+
{{/each}}
5+
exclude_files: [".gz$"]
6+
tags:
7+
{{#each tags as |tag i|}}
8+
- {{tag}}
9+
{{/each}}
10+
fields_under_root: true
11+
fields:
12+
observer:
13+
vendor: Test
14+
product: Test
15+
type: test
16+
{{#contains tags "forwarded"}}
17+
publisher_pipeline.disable_host: true
18+
{{/contains}}
19+
20+
processors:
21+
- add_locale: ~
22+
- add_fields:
23+
target: ''
24+
fields:
25+
ecs.version: 1.6.0
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
description: Test Pipeline
3+
4+
processors:
5+
# ECS event.ingested
6+
- set:
7+
field: event.ingested
8+
value: '{{_ingest.timestamp}}'
9+
on_failure:
10+
- append:
11+
field: error.message
12+
value: "{{ _ingest.on_failure_message }}"

0 commit comments

Comments
 (0)