Integration Tests with SparrowCI and Golang

melezhik
3 min readNov 16, 2022

SparrowCI is fun because it lets you easily mix different languages together and write integration tests for your code efficiently.

Today I am going to write an integration test for notify-1 Go package that sends notifications to various systems, including syslog.

First let’s create a Go code we are going to test:

tasks:
-
name: send-message
default: true
language: go
code: |
package main

import (
"context"
"log"

sl "log/syslog"

"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/syslog"
)

func main() {
syslogSvc, err := syslog.New(sl.LOG_USER, "")
if err != nil {
log.Fatalf("syslog.New() failed: %v", err)
}

notify.UseServices(syslogSvc)

err = notify.Send(context.Background(), "TEST", "Hello, World!")
if err != nil {
log.Fatalf("notify.Send() failed: %v", err)
}
}

That’s easy!

The code just sends a message to a syslog service. This is defined as main Sparrow task — send-message.

The whole task depends on a couple of things:

  • installing Go
  • running syslog

This is achived via standard SparrowCI mechanism of dependency tasks:

tasks:
-
name: send-message
default: true
language: go
code: |
package main
# the rest of the task a code
# is cut for bravety

depends:
-
name: install-go
-
name: run-syslog
-
name: install-go
language: Bash
code: |
sudo apk add go \
--repository=http://dl-cdn.alpinelinux.org/alpine/edge/community
-
name: run-syslog
language: Bash
code: |
sudo nohup syslogd -n 1>/tmp/syslog 2>&1 &

So we define two tasks — install-go and install-syslog that run before main send-message task. These are Bash tasks and work just fine with the main Go task. Syslog gets run as `nohup &` because CI runs on a docker image where normal systemd run is not availble …

— -

When the pipeline runs we could see the following output:

[dump code: task.go]
[1] package main
[2]
[3] import (
[4] "context"
[5] "log"
[6]
[7] sl "log/syslog"
[8]
[9] "github.com/nikoksr/notify"
[10] "github.com/nikoksr/notify/service/syslog"
[11] )
[12]
[13] func main() {
[14] syslogSvc, err := syslog.New(sl.LOG_USER, "")
[15] if err != nil {
[16] log.Fatalf("syslog.New() failed: %v", err)
[17] }
[18]
[19] notify.UseServices(syslogSvc)
[20]
[21] err = notify.Send(context.Background(), "TEST", "Hello, World!")
[22] if err != nil {
[23] log.Fatalf("notify.Send() failed: %v", err)
[24] }
[25] }
[26]
[task stdout]
[task stderr]
20:09:37 :: go: creating new go.mod: module sparrow/task
20:09:37 :: go: to add module requirements and sums:
20:09:37 :: go mod tidy
20:09:37 :: go: downloading github.com/melezhik/sparrowgo v0.0.0-20221114205142-53fe404116c9
20:09:37 :: go: added github.com/melezhik/sparrowgo v0.0.0-20221114205142-53fe404116c9
20:09:37 :: go: finding module for package github.com/nikoksr/notify/service/syslog
20:09:37 :: go: finding module for package github.com/nikoksr/notify
20:09:37 :: go: downloading github.com/nikoksr/notify v0.33.0
20:09:37 :: go: found github.com/nikoksr/notify in github.com/nikoksr/notify v0.33.0
20:09:37 :: go: found github.com/nikoksr/notify/service/syslog in github.com/nikoksr/notify v0.33.0
20:09:37 :: go: downloading github.com/google/go-cmp v0.5.9
20:09:37 :: go: downloading github.com/pkg/errors v0.9.1
20:09:37 :: go: downloading golang.org/x/sync v0.1.0
20:09:37 :: go: downloading github.com/stretchr/testify v1.8.1
20:09:37 :: go: downloading github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
20:09:37 :: go: downloading github.com/davecgh/go-spew v1.1.1
20:09:37 :: go: downloading github.com/pmezard/go-difflib v1.0.0
20:09:37 :: go: downloading github.com/stretchr/objx v0.5.0
20:09:37 :: go: downloading gopkg.in/yaml.v3 v3.0.1
20:09:37 :: go: downloading gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405

So, basically a Go code has executed successfully and wrote a message into a syslog service …

— -

The final bit here — we would like to see if a message is really saved in a syslog file. Let’s add one more followup task that gets executed after the main Go task:

tasks:
-
name: send-message
default: true
language: go
code: |
package main

# the rest of the task a code
# is cut for bravety

followup:
-
name: check-syslog
depends:
-
name: install-go
-
name: run-syslog
-
name: check-syslog
language: Bash
code: |
sudo cat /var/log/messages
check: |
regexp: "user.emerg notify[" \d+ "]: TEST: Hello, World!"

Check-syslog is Bash task that cats a syslog file (/var/log/messages) and checks that it contains a string matching some regular expression (defined in Raku regexp format):

"user.emerg notify[" \d+ "]: TEST: Hello, World!"

When the pipeline runs we could see that expected string is found in a syslog file:

[task run: task.bash - tasks/check-syslog]
[dump code: task.bash]
[1] sudo cat /var/log/messages
[2]
[task stdout]
20:37:26 :: Nov 16 20:36:53 a79458bf33f6 syslog.info syslogd started: BusyBox v1.35.0
20:37:26 :: Nov 16 20:37:07 a79458bf33f6 user.emerg notify[323]: TEST: Hello, World!
20:37:26 :: Nov 16 20:37:26 a79458bf33f6 authpriv.notice sudo: worker : PWD=/var/.sparrowdo/env/send-message/.sparrowdo ; USER=root ; COMMAND=/bin/cat /var/log/messages
[task check]
stdout match <"user.emerg notify[" \d+ "]: TEST: Hello, World!"> True

— -

That is it. The complete code of Sparrow pipeline could be found here.

Thanks for reading.

--

--

melezhik

I am the author of SparrowCI - super flexible CICD pipelines with many languages support. https://ci.sparrowhub.io