Merge pull request #799 from Icinga/TestSetChecksums
Some checks failed
Compliance / compliance (push) Has been cancelled
Build and Publish Container Image / build-and-publish-container-image (push) Has been cancelled
Go / build-test (macos-latest) (push) Has been cancelled
Go / build-test (ubuntu-latest) (push) Has been cancelled
Go / lint (push) Has been cancelled
Go / vet (push) Has been cancelled
Go / fmt (push) Has been cancelled
Go / modtidy (push) Has been cancelled
Go / vendor-diff (push) Has been cancelled
Integration Tests / MySQL (push) Has been cancelled
Integration Tests / PostgreSQL (push) Has been cancelled
SQL / MySQL 5.5 (push) Has been cancelled
SQL / MySQL 5.6 (push) Has been cancelled
SQL / MariaDB 10.1 (push) Has been cancelled
SQL / MariaDB 10.2 (push) Has been cancelled
SQL / MariaDB 10.3 (push) Has been cancelled
SQL / MariaDB 10.4 (push) Has been cancelled
SQL / MariaDB 10.5 (push) Has been cancelled
SQL / MariaDB 10.6 (push) Has been cancelled
SQL / MariaDB 10.7 (push) Has been cancelled
SQL / MariaDB latest (push) Has been cancelled
SQL / MySQL 5.7 (push) Has been cancelled
SQL / MySQL 8 (push) Has been cancelled
SQL / MySQL latest (push) Has been cancelled
SQL / PostgreSQL 10 (push) Has been cancelled
SQL / PostgreSQL 11 (push) Has been cancelled
SQL / PostgreSQL 12 (push) Has been cancelled
SQL / PostgreSQL 13 (push) Has been cancelled
SQL / PostgreSQL 9.6 (push) Has been cancelled
SQL / PostgreSQL latest (push) Has been cancelled
Sync For-Container.md to Docker Hub / sync (push) Has been cancelled

Test icingaredis.SetChecksums()
This commit is contained in:
Alvar 2026-01-09 09:34:32 +00:00 committed by GitHub
commit 8b64be8197
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,14 +1,26 @@
package icingaredis
import (
"context"
"fmt"
"github.com/icinga/icinga-go-library/database"
"github.com/icinga/icinga-go-library/redis"
"github.com/icinga/icinga-go-library/types"
"github.com/icinga/icingadb/pkg/icingadb/v1"
"github.com/stretchr/testify/require"
"testing"
"time"
)
var latencies = []struct {
name string
latency time.Duration
}{
{"instantly", 0},
{"1us", time.Microsecond},
{"20ms", 20 * time.Millisecond},
}
type testEntity struct {
v1.EntityWithoutChecksum `json:",inline"`
Data int `json:"data"`
@ -59,15 +71,6 @@ func TestCreateEntities(t *testing.T) {
},
}
latencies := []struct {
name string
latency time.Duration
}{
{"instantly", 0},
{"1us", time.Microsecond},
{"20ms", 20 * time.Millisecond},
}
for _, st := range subtests {
t.Run(st.name, func(t *testing.T) {
for _, l := range latencies {
@ -145,3 +148,226 @@ func TestCreateEntities(t *testing.T) {
})
}
}
type testEntityWithChecksum struct {
v1.EntityWithChecksum `json:",inline"`
Data types.Binary `json:"data"`
}
func newTestEntityWithChecksum(id, checksum, data []byte) *testEntityWithChecksum {
return &testEntityWithChecksum{
EntityWithChecksum: v1.EntityWithChecksum{
EntityWithoutChecksum: v1.EntityWithoutChecksum{IdMeta: v1.IdMeta{Id: id}},
ChecksumMeta: v1.ChecksumMeta{PropertiesChecksum: checksum},
},
Data: data,
}
}
func newEntityWithChecksum(id, checksum []byte) *v1.EntityWithChecksum {
return &v1.EntityWithChecksum{
EntityWithoutChecksum: v1.EntityWithoutChecksum{IdMeta: v1.IdMeta{Id: id}},
ChecksumMeta: v1.ChecksumMeta{PropertiesChecksum: checksum},
}
}
func TestSetChecksums(t *testing.T) {
subtests := []struct {
name string
input []database.Entity
checksums map[string]database.Entity
output []database.Entity
error bool
}{
{name: "nil"},
{
name: "empty",
checksums: map[string]database.Entity{},
},
{
name: "one",
input: []database.Entity{newTestEntityWithChecksum([]byte{1}, nil, []byte{3})},
checksums: map[string]database.Entity{"01": newEntityWithChecksum([]byte{1}, []byte{2})},
output: []database.Entity{newTestEntityWithChecksum([]byte{1}, []byte{2}, []byte{3})},
},
{
name: "two",
input: []database.Entity{
newTestEntityWithChecksum([]byte{4}, nil, []byte{6}),
newTestEntityWithChecksum([]byte{7}, nil, []byte{9}),
},
checksums: map[string]database.Entity{
"04": newEntityWithChecksum([]byte{4}, []byte{5}),
"07": newEntityWithChecksum([]byte{7}, []byte{8}),
},
output: []database.Entity{
newTestEntityWithChecksum([]byte{4}, []byte{5}, []byte{6}),
newTestEntityWithChecksum([]byte{7}, []byte{8}, []byte{9}),
},
},
{
name: "three",
input: []database.Entity{
newTestEntityWithChecksum([]byte{10}, nil, []byte{12}),
newTestEntityWithChecksum([]byte{13}, nil, []byte{15}),
newTestEntityWithChecksum([]byte{16}, nil, []byte{18}),
},
checksums: map[string]database.Entity{
"0a": newEntityWithChecksum([]byte{10}, []byte{11}),
"0d": newEntityWithChecksum([]byte{13}, []byte{14}),
"10": newEntityWithChecksum([]byte{16}, []byte{17}),
},
output: []database.Entity{
newTestEntityWithChecksum([]byte{10}, []byte{11}, []byte{12}),
newTestEntityWithChecksum([]byte{13}, []byte{14}, []byte{15}),
newTestEntityWithChecksum([]byte{16}, []byte{17}, []byte{18}),
},
},
{
name: "superfluous-checksum",
checksums: map[string]database.Entity{"13": newEntityWithChecksum([]byte{19}, []byte{20})},
},
{
name: "missing-checksum",
input: []database.Entity{newTestEntityWithChecksum([]byte{22}, nil, []byte{24})},
error: true,
},
}
for _, st := range subtests {
t.Run(st.name, func(t *testing.T) {
for _, concurrency := range []int{1, 2, 30} {
t.Run(fmt.Sprint(concurrency), func(t *testing.T) {
for _, l := range latencies {
t.Run(l.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
input := make(chan database.Entity, 1)
go func() {
defer close(input)
for _, v := range st.input {
if l.latency > 0 {
select {
case <-time.After(l.latency):
case <-ctx.Done():
return
}
}
select {
case input <- v:
case <-ctx.Done():
return
}
}
}()
output, errs := SetChecksums(ctx, input, st.checksums, concurrency)
require.NotNil(t, output, "output channel should not be nil")
require.NotNil(t, errs, "error channel should not be nil")
for _, expected := range st.output {
select {
case actual, ok := <-output:
require.True(t, ok, "output channel should not be closed, yet")
if concurrency == 1 || l.latency >= time.Millisecond {
require.Equal(t, expected, actual)
}
case <-time.After(time.Second):
require.Fail(t, "output channel should not block")
}
}
if st.error {
select {
case err, ok := <-errs:
require.True(t, ok, "error channel should not be closed, yet")
require.Error(t, err)
case <-time.After(time.Second):
require.Fail(t, "error channel should not block")
}
}
select {
case actual, ok := <-output:
require.False(t, ok, "output channel should be closed, got %#v", actual)
case <-time.After(time.Second):
require.Fail(t, "output channel should not block")
}
select {
case err, ok := <-errs:
require.False(t, ok, "error channel should be closed, got %#v", err)
case <-time.After(time.Second):
require.Fail(t, "error channel should not block")
}
})
}
})
}
for _, concurrency := range []int{0, -1, -2, -30} {
t.Run(fmt.Sprint(concurrency), func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
input := make(chan database.Entity, 1)
input <- nil
output, errs := SetChecksums(ctx, input, st.checksums, concurrency)
require.NotNil(t, output, "output channel should not be nil")
require.NotNil(t, errs, "error channel should not be nil")
select {
case v, ok := <-output:
require.False(t, ok, "output channel should be closed, got %#v", v)
case <-time.After(time.Second):
require.Fail(t, "output channel should not block")
}
select {
case err, ok := <-errs:
require.False(t, ok, "error channel should be closed, got %#v", err)
case <-time.After(time.Second):
require.Fail(t, "error channel should not block")
}
select {
case input <- nil:
require.Fail(t, "input channel should not be read from")
default:
}
})
}
})
}
t.Run("cancel-ctx", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
output, errs := SetChecksums(ctx, make(chan database.Entity), map[string]database.Entity{}, 1)
require.NotNil(t, output, "output channel should not be nil")
require.NotNil(t, errs, "error channel should not be nil")
select {
case v, ok := <-output:
require.False(t, ok, "output channel should be closed, got %#v", v)
case <-time.After(time.Second):
require.Fail(t, "output channel should not block")
}
select {
case err, ok := <-errs:
require.True(t, ok, "error channel should not be closed, yet")
require.Error(t, err)
case <-time.After(time.Second):
require.Fail(t, "error channel should not block")
}
})
}