Skip to content

Commit ddf970a

Browse files
committed
tests: Initial Integration Tests
Heavily inspired by the already existing integration tests from Icinga DB, the icinga-testing project was extended to support Icinga Notifications[0]. Based on those changes, an initial integration test was implemented for Icinga Notifications, launching and configuring PostgreSQL, Icinga 2, and Icinga Notifications. Afterwards, a webhook channel is used to catch notifications. [0] Icinga/icinga-testing#27
1 parent 4e5716e commit ddf970a

File tree

7 files changed

+549
-0
lines changed

7 files changed

+549
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Integration Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- 'support/*'
8+
- 'init-integration-tests' # TODO: remove PR testing branch
9+
- 'x-integration-tests' # TODO: remove PR testing branch
10+
pull_request: {}
11+
schedule:
12+
- cron: '42 23 * * *'
13+
14+
jobs:
15+
integration-tests:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: actions/setup-go@v5
20+
with:
21+
go-version: stable
22+
- name: Run Integration Tests
23+
run: ./test.sh
24+
working-directory: tests/
25+
- name: Compress Debug Log
26+
if: ${{ always() }}
27+
run: xz -9 ./tests/tmp/debug.log
28+
- name: Upload Debug Log
29+
if: ${{ always() }}
30+
uses: actions/upload-artifact@v4
31+
with:
32+
name: debug.log.xz
33+
path: ./tests/tmp/debug.log.xz
34+
retention-days: 1

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ FROM docker.io/library/alpine
2020
COPY --from=build /src/icinga-notifications/bin/icinga-notifications-daemon /usr/bin/icinga-notifications-daemon
2121
COPY --from=build /src/icinga-notifications/bin/channel /usr/libexec/icinga-notifications/channel
2222

23+
RUN mkdir /etc/icinga-notifications/
24+
COPY config.example.yml /etc/icinga-notifications/config.yml
25+
2326
RUN apk add tzdata
2427

2528
ARG username=notifications

tests/go.mod

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module github.com/icinga/icinga-notifications/tests
2+
3+
go 1.21
4+
5+
replace (
6+
github.com/icinga/icinga-notifications => ../
7+
github.com/icinga/icinga-testing => github.com/icinga/icinga-testing v0.0.0-20240118120311-bf2ccd424307
8+
)
9+
10+
require (
11+
github.com/icinga/icinga-notifications v0.0.0-20240102102116-0d6f7271c116
12+
github.com/icinga/icinga-testing v0.0.0-20240112095229-18da8922599a
13+
github.com/jmoiron/sqlx v1.3.5
14+
github.com/stretchr/testify v1.8.4
15+
)
16+
17+
require (
18+
github.com/Icinga/go-libs v0.0.0-20220420130327-ef58ad52edd8 // indirect
19+
github.com/Microsoft/go-winio v0.6.1 // indirect
20+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
21+
github.com/davecgh/go-spew v1.1.1 // indirect
22+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
23+
github.com/distribution/reference v0.5.0 // indirect
24+
github.com/docker/distribution v2.8.3+incompatible // indirect
25+
github.com/docker/docker v24.0.7+incompatible // indirect
26+
github.com/docker/go-connections v0.4.0 // indirect
27+
github.com/docker/go-units v0.5.0 // indirect
28+
github.com/go-redis/redis/v8 v8.11.5 // indirect
29+
github.com/go-sql-driver/mysql v1.7.1 // indirect
30+
github.com/gogo/protobuf v1.3.2 // indirect
31+
github.com/google/uuid v1.3.0 // indirect
32+
github.com/icinga/icingadb v1.1.1-0.20230418113126-7c4b947aad3a // indirect
33+
github.com/lib/pq v1.10.9 // indirect
34+
github.com/opencontainers/go-digest v1.0.0 // indirect
35+
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
36+
github.com/pkg/errors v0.9.1 // indirect
37+
github.com/pmezard/go-difflib v1.0.0 // indirect
38+
go.uber.org/multierr v1.11.0 // indirect
39+
go.uber.org/zap v1.26.0 // indirect
40+
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d // indirect
41+
golang.org/x/mod v0.14.0 // indirect
42+
golang.org/x/net v0.20.0 // indirect
43+
golang.org/x/sync v0.6.0 // indirect
44+
golang.org/x/sys v0.16.0 // indirect
45+
golang.org/x/tools v0.16.1 // indirect
46+
gopkg.in/yaml.v3 v3.0.1 // indirect
47+
)

tests/go.sum

Lines changed: 327 additions & 0 deletions
Large diffs are not rendered by default.

tests/main_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package notifications_test
2+
3+
import (
4+
"github.com/icinga/icinga-testing"
5+
"github.com/icinga/icinga-testing/services"
6+
"github.com/jmoiron/sqlx"
7+
"github.com/stretchr/testify/require"
8+
"testing"
9+
)
10+
11+
var it *icingatesting.IT
12+
13+
func TestMain(m *testing.M) {
14+
it = icingatesting.NewIT()
15+
defer it.Cleanup()
16+
17+
m.Run()
18+
}
19+
20+
func getDatabase(t testing.TB) services.RelationalDatabase {
21+
rdb := getEmptyDatabase(t)
22+
rdb.ImportIcingaNotificationsSchema()
23+
24+
db, err := sqlx.Open(rdb.Driver(), rdb.DSN())
25+
require.NoError(t, err, "SQL database open")
26+
defer func() { _ = db.Close() }()
27+
28+
_, err = db.Exec(`
29+
INSERT INTO source (id, type, name, listener_password_hash)
30+
VALUES (1, 'icinga2', 'Icinga 2', '$2y$10$QU8bJ7cpW1SmoVQ/RndX5O2J5L1PJF7NZ2dlIW7Rv3zUEcbUFg3z2')`)
31+
require.NoError(t, err, "populating source table failed")
32+
33+
return rdb
34+
}
35+
36+
func getEmptyDatabase(t testing.TB) services.RelationalDatabase {
37+
// Currently, PostgreSQL is the only supported database backend.
38+
return it.PostgresqlDatabaseT(t)
39+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package notifications_test
2+
3+
import (
4+
"encoding/json"
5+
"github.com/icinga/icinga-notifications/pkg/plugin"
6+
"github.com/icinga/icinga-testing/utils/eventually"
7+
"github.com/jmoiron/sqlx"
8+
"github.com/stretchr/testify/require"
9+
"net/http"
10+
"testing"
11+
"time"
12+
)
13+
14+
// TestNotificationRoundTrip instructs an Icinga 2 node to send a notification back for further inspection.
15+
func TestNotificationRoundTrip(t *testing.T) {
16+
rdb := getDatabase(t)
17+
notifications := it.IcingaNotificationsInstanceT(t, rdb)
18+
icinga := it.Icinga2NodeT(t, "master")
19+
icinga.EnableIcingaNotifications(notifications)
20+
require.NoError(t, icinga.Reload(), "icinga.Reload()")
21+
22+
db, err := sqlx.Open(rdb.Driver(), rdb.DSN())
23+
require.NoError(t, err, "SQL database open")
24+
defer func() { require.NoError(t, db.Close(), "db.Cleanup") }()
25+
26+
webhookRec := it.IcingaNotificationsWebhookReceiverInstanceT(t)
27+
webhookRecReqCh := make(chan plugin.NotificationRequest)
28+
webhookRec.Handler = func(writer http.ResponseWriter, request *http.Request) {
29+
var notReq plugin.NotificationRequest
30+
require.NoError(t, json.NewDecoder(request.Body).Decode(&notReq), "decoding NotificationRequest from request body")
31+
require.NoError(t, request.Body.Close(), "closing request body")
32+
webhookRecReqCh <- notReq
33+
http.Error(writer, "forwarded", http.StatusOK)
34+
}
35+
36+
t.Run("configure channel in database", func(t *testing.T) {
37+
eventually.Require(t, func(t require.TestingT) {
38+
var channelCount int
39+
err := db.QueryRow(`SELECT COUNT(*) FROM available_channel_type WHERE type = 'webhook'`).Scan(&channelCount)
40+
require.NoError(t, err, "SQL SELECT FROM available_channel_type query")
41+
require.Equal(t, 1, channelCount, "webhook type missing from available_channel_type")
42+
}, 10*time.Second, time.Second)
43+
44+
_, err := db.Exec(`
45+
INSERT INTO channel (id, name, type, config)
46+
VALUES (1, 'webhook', 'webhook', '{"method":"POST","url_template":"http:\/\/` + webhookRec.ListenAddr + `\/","request_body_template":"{{json .}}"}');
47+
48+
INSERT INTO contact (id, full_name, username, default_channel_id, color)
49+
VALUES (1, 'icingaadmin', 'icingaadmin', 1, '#000000');
50+
51+
INSERT INTO rule (id, name, is_active) VALUES (1, 'webhook', 'y');
52+
INSERT INTO rule_escalation (id, rule_id, position) VALUES (1, 1, 1);
53+
INSERT INTO rule_escalation_recipient (id, rule_escalation_id, contact_id, channel_id) VALUES (1, 1, 1, 1);`)
54+
require.NoError(t, err, "populating tables failed")
55+
})
56+
57+
t.Run("create icinga objects", func(t *testing.T) {
58+
client := icinga.ApiClient()
59+
client.CreateObject(t, "checkcommands", "failure-check", map[string]any{
60+
"templates": []any{"plugin-check-command"},
61+
"attrs": map[string]any{"command": []string{"/bin/false"}},
62+
})
63+
client.CreateHost(t, "test-host", map[string]any{
64+
"attrs": map[string]any{"check_command": "failure-check"},
65+
})
66+
client.CreateService(t, "test-host", "test-service", map[string]any{
67+
"attrs": map[string]any{"check_command": "failure-check"},
68+
})
69+
})
70+
71+
t.Run("read notification back from channel", func(t *testing.T) {
72+
select {
73+
case notReq := <-webhookRecReqCh:
74+
require.Contains(t, notReq.Object.Name, "test-", "object name must contain test prefix")
75+
76+
case <-time.After(5 * time.Minute):
77+
require.Fail(t, "no notification was received")
78+
}
79+
})
80+
}

tests/test.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/sh
2+
3+
set -eux
4+
5+
ICINGA_TESTING_NOTIFICATIONS_IMAGE="icinga-notifications:latest"
6+
ICINGA_TESTING_ICINGA_NOTIFICATIONS_SHARED_DIR="$(realpath out)"
7+
ICINGA_TESTING_ICINGA_NOTIFICATIONS_SCHEMA_PGSQL="$(realpath ../schema/pgsql/schema.sql)"
8+
export ICINGA_TESTING_NOTIFICATIONS_IMAGE
9+
export ICINGA_TESTING_ICINGA_NOTIFICATIONS_SHARED_DIR
10+
export ICINGA_TESTING_ICINGA_NOTIFICATIONS_SCHEMA_PGSQL
11+
12+
docker build -t "$ICINGA_TESTING_NOTIFICATIONS_IMAGE" ..
13+
14+
test -d ./out && rm -r ./out
15+
mkdir ./out
16+
17+
go test -o ./out/icinga-notifications-test -c .
18+
19+
exec ./out/icinga-notifications-test -icingatesting.debuglog ./out/debug.log -test.v

0 commit comments

Comments
 (0)