replace go-acc, richgo with gotestsum (#3567)

This commit is contained in:
mmetc 2025-04-14 16:21:32 +02:00 committed by GitHub
parent 89761938c7
commit c17d42278f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 118 additions and 159 deletions

View file

@ -52,16 +52,7 @@ jobs:
- name: "Collect coverage data"
run: |
go tool covdata textfmt -i test/coverage -o coverage-bats-raw.out
# filter out unwanted packages, should match the argument to "go-acc --ignore"
grep -v \
-e '/pkg/database' \
-e '/plugins/notifications' \
-e '/pkg/protobufs' \
-e '/pkg/cwversions' \
-e '/pkg/models' \
< coverage-bats-raw.out \
> coverage-bats.out
go tool covdata textfmt -i test/coverage -o coverage-bats.out
#
# In case you need to inspect the database status after the failure of a given test

View file

@ -15,7 +15,6 @@ on:
- 'README.md'
env:
RICHGO_FORCE_COLOR: 1
CROWDSEC_FEATURE_DISABLE_HTTP_RETRY_BACKOFF: true
jobs:
@ -44,12 +43,10 @@ jobs:
run: |
.github/generate-codecov-yml.sh >> .github/codecov.yml
- name: Run tests
- name: Unit tests
run: |
go install github.com/kyoh86/richgo@v0.3.10
go test -tags expr_debug -coverprofile coverage.out -covermode=atomic ./... > out.txt
if(!$?) { cat out.txt | sed 's/ *coverage:.*of statements in.*//' | richgo testfilter; Exit 1 }
cat out.txt | sed 's/ *coverage:.*of statements in.*//' | richgo testfilter
go install gotest.tools/gotestsum@v1.12.1
make testcover
- name: Upload unit coverage to Codecov
uses: codecov/codecov-action@v4

View file

@ -21,7 +21,6 @@ on:
# these env variables are for localstack, so we can emulate aws services
env:
RICHGO_FORCE_COLOR: 1
AWS_HOST: localstack
# these are to mimic aws config
AWS_ACCESS_KEY_ID: test
@ -170,24 +169,25 @@ jobs:
run: |
.github/generate-codecov-yml.sh >> .github/codecov.yml
- name: Build and run tests, static
- name: Ensure we can do a dynamic build
run: |
sudo apt -qq -y -o=Dpkg::Use-Pty=0 install build-essential libre2-dev
go install github.com/ory/go-acc@v0.2.8
go install github.com/kyoh86/richgo@v0.3.10
set -o pipefail
make build BUILD_STATIC=1
make go-acc | sed 's/ *coverage:.*of statements in.*//' | richgo testfilter
make build
- name: Ensure we can do a static build
run: |
make clean build BUILD_STATIC=1
- name: Unit tests
run: |
go install gotest.tools/gotestsum@v1.12.1
make testcover
# check if some component stubs are missing
- name: "Build profile: minimal"
run: |
make build BUILD_PROFILE=minimal
- name: Ensure we can do a dynamic build, without tests
run: |
make clean build
- name: Upload unit coverage to Codecov
uses: codecov/codecov-action@v4
with:

View file

@ -6,7 +6,7 @@ on:
- prereleased
permissions:
# Use write for: hub release edit
# Use write for: gh release upload
contents: write
jobs:

2
.gitignore vendored
View file

@ -31,7 +31,7 @@ test/bats/.bats/run-logs
# Test binaries, built from *_test.go
pkg/csplugin/tests/cs_plugin_test*
# Output of go-acc, go -cover
# Output of test coverage
*.out
test/coverage/*

View file

@ -260,7 +260,7 @@ clean-rpm:
@$(RM) -r rpm/SRPMS
.PHONY: clean
clean: clean-debian clean-rpm testclean ## Remove build artifacts
clean: clean-debian clean-rpm bats-clean ## Remove build artifacts
@$(MAKE) -C $(CROWDSEC_FOLDER) clean $(MAKE_FLAGS)
@$(MAKE) -C $(CSCLI_FOLDER) clean $(MAKE_FLAGS)
@$(RM) $(CROWDSEC_BIN) $(WIN_IGNORE_ERR)
@ -279,28 +279,55 @@ cscli: ## Build cscli
crowdsec: ## Build crowdsec
@$(MAKE) -C $(CROWDSEC_FOLDER) build $(MAKE_FLAGS)
.PHONY: testclean
testclean: bats-clean ## Remove test artifacts
@$(RM) pkg/apiserver/ent $(WIN_IGNORE_ERR)
@$(RM) pkg/cwhub/hubdir $(WIN_IGNORE_ERR)
@$(RM) pkg/cwhub/install $(WIN_IGNORE_ERR)
@$(RM) pkg/types/example.txt $(WIN_IGNORE_ERR)
# for the tests with localstack
export AWS_ENDPOINT_FORCE=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
testenv:
@echo 'NOTE: You need to run "make localstack" in a separate shell, "make localstack-stop" to terminate it'
ifeq ($(TEST_LOCAL_ONLY),)
@echo 'NOTE: You need to run "make localstack" in a separate shell, "make localstack-stop" to terminate it; or define the envvar TEST_LOCAL_ONLY to some value.'
else
@echo 'TEST_LOCAL_ONLY: skipping tests that require mock containers (localstack, kafka...)'
endif
.PHONY: check_gotestsum
check_gotestsum:
ifeq ($(OS),Windows_NT)
@where gotestsum >nul || (echo "Error: gotestsum is not installed. Install it with 'go install gotest.tools/gotestsum@latest'" && exit 1)
else
@command -v gotestsum > /dev/null 2>&1 || (echo "Error: gotestsum is not installed. Install it with 'go install gotest.tools/gotestsum@latest'" && exit 1)
endif
# Default format
GOTESTSUM_FORMAT := pkgname
# If running in GitHub Actions, change format
ifdef GITHUB_ACTIONS
GOTESTSUM_FORMAT := github-actions
endif
.PHONY: test
test: testenv ## Run unit tests with localstack
$(GOTEST) --tags=$(GO_TAGS) $(LD_OPTS) ./...
test: check_gotestsum testenv ## Run unit tests
# The quotes in the next command are required for PowerShell
gotestsum --format $(GOTESTSUM_FORMAT) --format-hide-empty-pkg -- "-tags=$(GO_TAGS)" ./...
.PHONY: go-acc
go-acc: testenv ## Run unit tests with localstack + coverage
go-acc ./... -o coverage.out --ignore database,notifications,protobufs,cwversion,cstest,models --tags $(GO_TAGS) -- $(LD_OPTS)
.PHONY: testcover
testcover: check_gotestsum testenv ## Run unit tests with coverage report
# The quotes in the next command are required for PowerShell
gotestsum --format $(GOTESTSUM_FORMAT) --format-hide-empty-pkg -- -covermode=atomic "-coverprofile=coverage.out" -coverpkg=./... "-tags=$(GO_TAGS)" ./...
.PHONY: check_golangci-lint
check_golangci-lint:
ifeq ($(OS),Windows_NT)
@where golangci-lint >nul || (echo "Error: golangci-lint is not installed. Install it from https://github.com/golangci/golangci-lint" && exit 1)
else
@command -v galangci-lint > /dev/null 2>&1 || (echo "Error: golangci-lint is not installed. Install it from https://github.com/golangci/golangci-lint" && exit 1)
endif
.PHONY: lint
lint: check_golangci-lint ## Run go linters
@golangci-lint run
check_docker:
@if ! docker info > /dev/null 2>&1; then \

2
go.mod
View file

@ -24,7 +24,7 @@ require (
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/creack/pty v1.1.21 // indirect
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26
github.com/crowdsecurity/go-cs-lib v0.0.16
github.com/crowdsecurity/go-cs-lib v0.0.17
github.com/crowdsecurity/grokky v0.2.2
github.com/crowdsecurity/machineid v1.0.2
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc

4
go.sum
View file

@ -111,8 +111,8 @@ github.com/crowdsecurity/coraza/v3 v3.0.0-20250320231801-749b8bded21a h1:2Nyr+47
github.com/crowdsecurity/coraza/v3 v3.0.0-20250320231801-749b8bded21a/go.mod h1:xSaXWOhFMSbrV8qOOfBKAyw3aOqfwaSaOy5BgSF8XlA=
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU=
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:zpv7r+7KXwgVUZnUNjyP22zc/D7LKjyoY02weH2RBbk=
github.com/crowdsecurity/go-cs-lib v0.0.16 h1:2/htodjwc/sfsv4deX8F/2Fzg1bOI8w3O1/BPSvvsB0=
github.com/crowdsecurity/go-cs-lib v0.0.16/go.mod h1:XwGcvTt4lMq4Tm1IRMSKMDf0CVrnytTU8Uoofa7AR+g=
github.com/crowdsecurity/go-cs-lib v0.0.17 h1:VM++7EDa34kVCXsCRwOjaua3XHru8FVfKUAbqEoQPas=
github.com/crowdsecurity/go-cs-lib v0.0.17/go.mod h1:XwGcvTt4lMq4Tm1IRMSKMDf0CVrnytTU8Uoofa7AR+g=
github.com/crowdsecurity/grokky v0.2.2 h1:yALsI9zqpDArYzmSSxfBq2dhYuGUTKMJq8KOEIAsuo4=
github.com/crowdsecurity/grokky v0.2.2/go.mod h1:33usDIYzGDsgX1kHAThCbseso6JuWNJXOzRQDGXHtWM=
github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5lirI+bdc=

View file

@ -64,6 +64,10 @@ func TestMain(m *testing.M) {
os.Exit(0)
}
if os.Getenv("TEST_LOCAL_ONLY") != "" {
os.Exit(0)
}
if err := checkForLocalStackAvailability(); err != nil {
log.Fatalf("local stack error : %s", err)
}
@ -80,9 +84,7 @@ func TestMain(m *testing.M) {
func TestWatchLogGroupForStreams(t *testing.T) {
ctx := t.Context()
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
log.SetLevel(log.DebugLevel)
@ -533,9 +535,7 @@ stream_name: test_stream`),
func TestConfiguration(t *testing.T) {
ctx := t.Context()
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
log.SetLevel(log.DebugLevel)
@ -611,9 +611,7 @@ stream_name: test_stream`),
}
func TestConfigureByDSN(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
log.SetLevel(log.DebugLevel)
@ -660,9 +658,7 @@ func TestConfigureByDSN(t *testing.T) {
func TestOneShotAcquisition(t *testing.T) {
ctx := t.Context()
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
log.SetLevel(log.DebugLevel)

View file

@ -4,7 +4,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
@ -21,9 +20,7 @@ import (
)
func TestBadConfiguration(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
tests := []struct {
config string
@ -59,9 +56,7 @@ journalctl_filter:
}
func TestConfigureDSN(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
tests := []struct {
dsn string
@ -107,11 +102,9 @@ func TestConfigureDSN(t *testing.T) {
}
func TestOneShot(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
config string
@ -190,11 +183,9 @@ journalctl_filter:
}
func TestStreaming(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
config string

View file

@ -3,7 +3,6 @@ package kafkaacquisition
import (
"context"
"net"
"runtime"
"strconv"
"testing"
"time"
@ -128,11 +127,10 @@ func createTopic(topic string, broker string) {
}
func TestStreamingAcquisition(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
name string
@ -202,11 +200,10 @@ topic: crowdsecplaintext`), subLogger, configuration.METRICS_NONE)
}
func TestStreamingAcquisitionWithSSL(t *testing.T) {
ctx := t.Context()
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
cstest.SkipOnWindows(t)
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
name string

View file

@ -7,7 +7,6 @@ import (
"fmt"
"net"
"os"
"runtime"
"strconv"
"strings"
"testing"
@ -112,9 +111,7 @@ func TestMain(m *testing.M) {
}
func TestBadConfiguration(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
cstest.SkipOnWindows(t)
tests := []struct {
config string
@ -156,11 +153,10 @@ stream_arn: arn:aws:kinesis:eu-west-1:123456789012:stream/my-stream`,
}
func TestReadFromStream(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
config string
@ -204,11 +200,10 @@ stream_name: stream-1-shard`,
}
func TestReadFromMultipleShards(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
config string
@ -255,11 +250,10 @@ stream_name: stream-2-shards`,
}
func TestFromSubscription(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
tests := []struct {
config string

View file

@ -9,7 +9,6 @@ import (
"net/http"
"net/url"
"os"
"runtime"
"strings"
"testing"
"time"
@ -332,11 +331,10 @@ func feedLoki(ctx context.Context, logger *log.Entry, n int, title string) error
}
func TestOneShotAcquisition(t *testing.T) {
ctx := t.Context()
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
cstest.SkipOnWindows(t)
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
log.SetOutput(os.Stdout)
log.SetLevel(log.InfoLevel)
@ -394,11 +392,10 @@ since: 1h
}
func TestStreamingAcquisition(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
log.SetOutput(os.Stdout)
log.SetLevel(log.InfoLevel)
@ -510,11 +507,10 @@ query: >
}
func TestStopStreaming(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
config := `
mode: tail

View file

@ -9,7 +9,6 @@ import (
"net/http"
"net/url"
"os"
"runtime"
"strconv"
"strings"
"testing"
@ -254,11 +253,10 @@ func feedVLogs(ctx context.Context, logger *log.Entry, n int, title string) erro
}
func TestOneShotAcquisition(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
log.SetOutput(os.Stdout)
log.SetLevel(log.InfoLevel)
@ -319,11 +317,10 @@ since: 1h
}
func TestStreamingAcquisition(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
log.SetOutput(os.Stdout)
log.SetLevel(log.InfoLevel)
@ -431,11 +428,10 @@ query: >
}
func TestStopStreaming(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindows(t)
cstest.SkipIfDefined(t, "TEST_LOCAL_ONLY")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
ctx := t.Context()
config := `
mode: tail

View file

@ -3,7 +3,6 @@ package csplugin
import (
"context"
"log"
"runtime"
"testing"
"time"
@ -49,11 +48,9 @@ func listenChannelWithTimeout(ctx context.Context, channel chan string) error {
}
func TestPluginWatcherInterval(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindowsBecause(t, "timing is not reliable")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows because timing is not reliable")
}
ctx := t.Context()
pw := PluginWatcher{}
alertsByPluginName := make(map[string][]*models.Alert)
@ -85,11 +82,9 @@ func TestPluginWatcherInterval(t *testing.T) {
}
func TestPluginAlertCountWatcher(t *testing.T) {
ctx := t.Context()
cstest.SkipOnWindowsBecause(t, "timing is not reliable")
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows because timing is not reliable")
}
ctx := t.Context()
pw := PluginWatcher{}
alertsByPluginName := make(map[string][]*models.Alert)

View file

@ -392,9 +392,7 @@ func TestDetectSimpleRule(t *testing.T) {
}
func TestDetectUnitError(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping on windows")
}
cstest.SkipOnWindows(t)
require := require.New(t)
setup.ExecCommand = fakeExecCommandNotFound
@ -563,10 +561,7 @@ func TestDetectForcedUnit(t *testing.T) {
}
func TestDetectForcedProcess(t *testing.T) {
if runtime.GOOS == "windows" {
// while looking for service wizard: rule 'ProcessRunning("foobar")': while looking up running processes: could not get Name: A device attached to the system is not functioning.
t.Skip("skipping on windows")
}
cstest.SkipOnWindows(t)
require := require.New(t)
setup.ExecCommand = fakeExecCommand
@ -593,9 +588,7 @@ func TestDetectForcedProcess(t *testing.T) {
}
func TestDetectSkipService(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping on windows")
}
cstest.SkipOnWindows(t)
require := require.New(t)
setup.ExecCommand = fakeExecCommand

View file

@ -356,15 +356,7 @@ If you are not using Docker, you may need to adjust the `PGHOST`/`PGPORT`/`PGPAS
An additional user and database both named `crowdsec_test` will be created.
Now you can build and run the tests (we skip bats-test-hub here, they really
should not be affected by a change in DB).
```
$ export DB_BACKEND=postgres
$ make clean bats-build bats-fixture bats-test
```
or with the pgx driver:
Now you can build and run the tests:
```
$ export DB_BACKEND=pgx

View file

@ -55,7 +55,7 @@ export GOCOVERDIR="$(TEST_DIR)/coverage"
export PATH="$(TEST_DIR)/tools:$(PATH)"
endef
bats-all: bats-clean bats-build bats-fixture bats-test bats-test-hub
bats-all: bats-clean bats-build bats-fixture bats-test
# Source this to run the scripts outside of the Makefile
# Old versions of make don't have $(file) directive
@ -102,9 +102,6 @@ bats-clean: ## Remove functional test environment
bats-test: bats-environment ## Run functional tests
$(TEST_DIR)/run-tests $(TEST_DIR)/bats
bats-test-hub: bats-environment bats-check-requirements ## Run all hub tests
$(TEST_DIR)/run-tests $(TEST_DIR)/dyn-bats
# Not failproof but they can catch bugs and improve learning of sh/bash
bats-lint: ## Static checks for the test scripts.
@shellcheck --version >/dev/null 2>&1 || (echo "ERROR: shellcheck is required."; exit 1)
@ -113,4 +110,3 @@ bats-lint: ## Static checks for the test scripts.
bats-test-package: bats-environment ## CI only - test a binary package (deb, rpm, ...)
$(TEST_DIR)/instance-data make
$(TEST_DIR)/run-tests $(TEST_DIR)/bats
$(TEST_DIR)/run-tests $(TEST_DIR)/dyn-bats

View file

@ -1,2 +0,0 @@
This directory is for dynamically generated tests. Do not commit them.
Any `*.bats` file here will be removed by the Makefile.