tests: replace godebug by go-cmp for structure diffs

go-cmp is stricter and allow to catch more problems. Moreover, the
output is a bit nicer.
This commit is contained in:
Vincent Bernat
2025-08-23 08:28:38 +02:00
parent 1a09ba9e7c
commit e2f1df9add
45 changed files with 852 additions and 710 deletions

View File

@@ -158,13 +158,13 @@ module2:
if err := yaml.Unmarshal(out.Bytes(), &gotRaw); err != nil { if err := yaml.Unmarshal(out.Bytes(), &gotRaw); err != nil {
t.Fatalf("Unmarshal() error:\n%+v", err) t.Fatalf("Unmarshal() error:\n%+v", err)
} }
expectedRaw := gin.H{ expectedRaw := map[string]gin.H{
"module1": gin.H{ "module1": {
"listen": "127.0.0.1:8080", "listen": "127.0.0.1:8080",
"topic": "flows", "topic": "flows",
"workers": 100, "workers": 100,
}, },
"module2": gin.H{ "module2": {
"stuff": "bye", "stuff": "bye",
"details": gin.H{ "details": gin.H{
"workers": 5, "workers": 5,
@@ -356,7 +356,7 @@ invalid key "unused"`); diff != "" {
} }
func TestDefaultInSlice(t *testing.T) { func TestDefaultInSlice(t *testing.T) {
try := func(t *testing.T, parse func(cmd.ConfigRelatedOptions, *bytes.Buffer) any) { try := func(t *testing.T, parse func(cmd.ConfigRelatedOptions, *bytes.Buffer) any, expected any) {
// Configuration file // Configuration file
config := `--- config := `---
modules: modules:
@@ -375,9 +375,25 @@ modules:
out := bytes.NewBuffer([]byte{}) out := bytes.NewBuffer([]byte{})
parsed := parse(c, out) parsed := parse(c, out)
if diff := helpers.Diff(parsed, expected); diff != "" {
t.Errorf("Parse() (-got, +want):\n%s", diff)
}
}
t.Run("without pointer", func(t *testing.T) {
try(t, func(c cmd.ConfigRelatedOptions, out *bytes.Buffer) any {
parsed := struct {
Modules []dummyConfiguration
}{}
if err := c.Parse(out, "dummy", &parsed); err != nil {
t.Fatalf("Parse() error:\n%+v", err)
}
return parsed
},
// Expected configuration // Expected configuration
expected := map[string][]dummyConfiguration{ struct {
"Modules": { Modules []dummyConfiguration
}{
Modules: []dummyConfiguration{
{ {
Module1: dummyModule1Configuration{ Module1: dummyModule1Configuration{
Listen: "127.0.0.1:8080", Listen: "127.0.0.1:8080",
@@ -402,7 +418,8 @@ modules:
}, },
}, },
}, },
}, { },
{
Module1: dummyModule1Configuration{ Module1: dummyModule1Configuration{
Listen: "127.0.0.1:8080", Listen: "127.0.0.1:8080",
Topic: "flows2", Topic: "flows2",
@@ -428,20 +445,6 @@ modules:
}, },
}, },
}, },
}
if diff := helpers.Diff(parsed, expected); diff != "" {
t.Errorf("Parse() (-got, +want):\n%s", diff)
}
}
t.Run("without pointer", func(t *testing.T) {
try(t, func(c cmd.ConfigRelatedOptions, out *bytes.Buffer) any {
parsed := struct {
Modules []dummyConfiguration
}{}
if err := c.Parse(out, "dummy", &parsed); err != nil {
t.Fatalf("Parse() error:\n%+v", err)
}
return parsed
}) })
}) })
t.Run("with pointer", func(t *testing.T) { t.Run("with pointer", func(t *testing.T) {
@@ -453,6 +456,62 @@ modules:
t.Fatalf("Parse() error:\n%+v", err) t.Fatalf("Parse() error:\n%+v", err)
} }
return parsed return parsed
},
struct {
Modules []*dummyConfiguration
}{
Modules: []*dummyConfiguration{
{
Module1: dummyModule1Configuration{
Listen: "127.0.0.1:8080",
Topic: "flows1",
Workers: 100,
},
Module2: dummyModule2Configuration{
MoreDetails: MoreDetails{
Stuff: "hello",
},
Details: dummyModule2DetailsConfiguration{
Workers: 1,
IntervalValue: time.Minute,
},
Elements: []dummyModule2ElementsConfiguration{
{
Name: "el1",
Gauge: 10,
}, {
Name: "el2",
Gauge: 11,
},
},
},
},
{
Module1: dummyModule1Configuration{
Listen: "127.0.0.1:8080",
Topic: "flows2",
Workers: 100,
},
Module2: dummyModule2Configuration{
MoreDetails: MoreDetails{
Stuff: "hello",
},
Details: dummyModule2DetailsConfiguration{
Workers: 1,
IntervalValue: time.Minute,
},
Elements: []dummyModule2ElementsConfiguration{
{
Name: "el1",
Gauge: 10,
}, {
Name: "el2",
Gauge: 11,
},
},
},
},
},
}) })
}) })
} }

View File

@@ -130,8 +130,8 @@ func TestRealClickHouse(t *testing.T) {
} }
expected := []struct { expected := []struct {
N uint64 N uint64 `ch:"n"`
M uint64 M uint64 `ch:"m"`
}{ }{
{0, 1}, {0, 1},
{1, 2}, {1, 2},

View File

@@ -7,6 +7,8 @@ import (
"testing" "testing"
"akvorado/common/helpers" "akvorado/common/helpers"
"github.com/google/go-cmp/cmp/cmpopts"
) )
type likeInt int type likeInt int
@@ -72,6 +74,7 @@ func TestPutCollision(t *testing.T) {
func TestTake(t *testing.T) { func TestTake(t *testing.T) {
p := NewPool[likeInt]() p := NewPool[likeInt]()
diffOpt := cmpopts.EquateComparable(internValue[likeInt]{})
val1 := likeInt(10) val1 := likeInt(10)
ref1 := p.Put(val1) ref1 := p.Put(val1)
@@ -91,7 +94,7 @@ func TestTake(t *testing.T) {
{value: 22, refCount: 1, previous: 2, next: 4}, {value: 22, refCount: 1, previous: 2, next: 4},
{value: 32, refCount: 1, previous: 3}, {value: 32, refCount: 1, previous: 3},
} }
if diff := helpers.Diff(p.values, expectedValues, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(p.values, expectedValues, diffOpt); diff != "" {
t.Fatalf("p.values (-got, +want):\n%s", diff) t.Fatalf("p.values (-got, +want):\n%s", diff)
} }
@@ -104,7 +107,7 @@ func TestTake(t *testing.T) {
{value: 22, refCount: 0, previous: 2, next: 4}, // free {value: 22, refCount: 0, previous: 2, next: 4}, // free
{value: 32, refCount: 1, previous: 2}, {value: 32, refCount: 1, previous: 2},
} }
if diff := helpers.Diff(p.values, expectedValues, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(p.values, expectedValues, diffOpt); diff != "" {
t.Fatalf("p.values (-got, +want):\n%s", diff) t.Fatalf("p.values (-got, +want):\n%s", diff)
} }
@@ -120,7 +123,7 @@ func TestTake(t *testing.T) {
{value: 42, refCount: 1, previous: 4}, {value: 42, refCount: 1, previous: 4},
{value: 32, refCount: 1, previous: 2, next: 3}, {value: 32, refCount: 1, previous: 2, next: 3},
} }
if diff := helpers.Diff(p.values, expectedValues, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(p.values, expectedValues, diffOpt); diff != "" {
t.Fatalf("p.values (-got, +want):\n%s", diff) t.Fatalf("p.values (-got, +want):\n%s", diff)
} }
@@ -133,7 +136,7 @@ func TestTake(t *testing.T) {
{value: 42, refCount: 1, previous: 4}, {value: 42, refCount: 1, previous: 4},
{value: 32, refCount: 1, next: 3}, {value: 32, refCount: 1, next: 3},
} }
if diff := helpers.Diff(p.values, expectedValues, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(p.values, expectedValues, diffOpt); diff != "" {
t.Fatalf("p.values (-got, +want):\n%s", diff) t.Fatalf("p.values (-got, +want):\n%s", diff)
} }
@@ -146,7 +149,7 @@ func TestTake(t *testing.T) {
{value: 42, refCount: 1}, {value: 42, refCount: 1},
{value: 32, refCount: 0, next: 3}, // free {value: 32, refCount: 0, next: 3}, // free
} }
if diff := helpers.Diff(p.values, expectedValues, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(p.values, expectedValues, diffOpt); diff != "" {
t.Fatalf("p.values (-got, +want):\n%s", diff) t.Fatalf("p.values (-got, +want):\n%s", diff)
} }
@@ -159,7 +162,7 @@ func TestTake(t *testing.T) {
{value: 42, refCount: 0}, // free {value: 42, refCount: 0}, // free
{value: 32, refCount: 0, next: 3}, // free {value: 32, refCount: 0, next: 3}, // free
} }
if diff := helpers.Diff(p.values, expectedValues, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(p.values, expectedValues, diffOpt); diff != "" {
t.Fatalf("p.values (-got, +want):\n%s", diff) t.Fatalf("p.values (-got, +want):\n%s", diff)
} }

View File

@@ -236,7 +236,7 @@ func TestParametrizedConfig(t *testing.T) {
Expected: OuterConfiguration{ Expected: OuterConfiguration{
AA: "a1", AA: "a1",
BB: "b1", BB: "b1",
Config: InnerConfigurationType1{ Config: &InnerConfigurationType1{
CC: "c1", CC: "c1",
DD: "d1", DD: "d1",
}, },
@@ -256,7 +256,7 @@ func TestParametrizedConfig(t *testing.T) {
Expected: OuterConfiguration{ Expected: OuterConfiguration{
AA: "a2", AA: "a2",
BB: "b2", BB: "b2",
Config: InnerConfigurationType2{ Config: &InnerConfigurationType2{
CC: "c2", CC: "c2",
EE: "e2", EE: "e2",
}, },

View File

@@ -6,6 +6,7 @@ package helpers_test
import ( import (
"net/netip" "net/netip"
"slices" "slices"
"strings"
"testing" "testing"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -19,6 +20,7 @@ import (
func TestSubnetMapUnmarshalHook(t *testing.T) { func TestSubnetMapUnmarshalHook(t *testing.T) {
var nilMap map[string]string var nilMap map[string]string
cases := []struct { cases := []struct {
Pos helpers.Pos
Description string Description string
Input any Input any
Tests map[string]string Tests map[string]string
@@ -26,20 +28,24 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
YAML any YAML any
}{ }{
{ {
Pos: helpers.Mark(),
Description: "nil", Description: "nil",
Input: nilMap, Input: nilMap,
Tests: map[string]string{ Tests: map[string]string{
"::ffff:203.0.113.1": "", "::ffff:203.0.113.1": "",
}, },
YAML: map[string]string{},
}, { }, {
Pos: helpers.Mark(),
Description: "empty", Description: "empty",
Input: gin.H{}, Input: map[string]string{},
Tests: map[string]string{ Tests: map[string]string{
"::ffff:203.0.113.1": "", "::ffff:203.0.113.1": "",
}, },
}, { }, {
Pos: helpers.Mark(),
Description: "IPv4 subnet", Description: "IPv4 subnet",
Input: gin.H{"203.0.113.0/24": "customer1"}, Input: map[string]string{"203.0.113.0/24": "customer1"},
Tests: map[string]string{ Tests: map[string]string{
"::ffff:203.0.113.18": "customer1", "::ffff:203.0.113.18": "customer1",
"::ffff:203.0.113.16": "customer1", "::ffff:203.0.113.16": "customer1",
@@ -49,59 +55,69 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
"2001:db8:1::12": "", "2001:db8:1::12": "",
}, },
}, { }, {
Pos: helpers.Mark(),
Description: "IPv4 IP", Description: "IPv4 IP",
Input: gin.H{"203.0.113.1": "customer1"}, Input: map[string]string{"203.0.113.1": "customer1"},
Tests: map[string]string{ Tests: map[string]string{
"::ffff:203.0.113.1": "customer1", "::ffff:203.0.113.1": "customer1",
"2001:db8:1::12": "", "2001:db8:1::12": "",
}, },
YAML: gin.H{"203.0.113.1/32": "customer1"}, YAML: map[string]string{"203.0.113.1/32": "customer1"},
}, { }, {
Pos: helpers.Mark(),
Description: "IPv6 subnet", Description: "IPv6 subnet",
Input: gin.H{"2001:db8:1::/64": "customer2"}, Input: map[string]string{"2001:db8:1::/64": "customer2"},
Tests: map[string]string{ Tests: map[string]string{
"2001:db8:1::1": "customer2", "2001:db8:1::1": "customer2",
"2001:db8:1::2": "customer2", "2001:db8:1::2": "customer2",
"2001:db8:2::2": "", "2001:db8:2::2": "",
}, },
}, { }, {
Pos: helpers.Mark(),
Description: "IPv6-mapped-IPv4 subnet", Description: "IPv6-mapped-IPv4 subnet",
Input: gin.H{"::ffff:203.0.113.0/120": "customer2"}, Input: map[string]string{"::ffff:203.0.113.0/120": "customer2"},
Tests: map[string]string{ Tests: map[string]string{
"::ffff:203.0.113.10": "customer2", "::ffff:203.0.113.10": "customer2",
"::ffff:203.0.112.10": "", "::ffff:203.0.112.10": "",
}, },
YAML: gin.H{"203.0.113.0/24": "customer2"}, YAML: map[string]string{"203.0.113.0/24": "customer2"},
}, { }, {
Pos: helpers.Mark(),
Description: "IPv6 IP", Description: "IPv6 IP",
Input: gin.H{"2001:db8:1::1": "customer2"}, Input: map[string]string{"2001:db8:1::1": "customer2"},
Tests: map[string]string{ Tests: map[string]string{
"2001:db8:1::1": "customer2", "2001:db8:1::1": "customer2",
"2001:db8:1::2": "", "2001:db8:1::2": "",
"2001:db8:2::2": "", "2001:db8:2::2": "",
}, },
YAML: gin.H{"2001:db8:1::1/128": "customer2"}, YAML: map[string]string{"2001:db8:1::1/128": "customer2"},
}, { }, {
Pos: helpers.Mark(),
Description: "Invalid subnet (1)", Description: "Invalid subnet (1)",
Input: gin.H{"192.0.2.1/38": "customer"}, Input: map[string]string{"192.0.2.1/38": "customer"},
Error: true, Error: true,
}, { }, {
Pos: helpers.Mark(),
Description: "Invalid subnet (2)", Description: "Invalid subnet (2)",
Input: gin.H{"192.0.2.1/255.0.255.0": "customer"}, Input: map[string]string{"192.0.2.1/255.0.255.0": "customer"},
Error: true, Error: true,
}, { }, {
Pos: helpers.Mark(),
Description: "Invalid subnet (3)", Description: "Invalid subnet (3)",
Input: gin.H{"2001:db8::/1000": "customer"}, Input: map[string]string{"2001:db8::/1000": "customer"},
Error: true, Error: true,
}, { }, {
Pos: helpers.Mark(),
Description: "Invalid IP", Description: "Invalid IP",
Input: gin.H{"200.33.300.1": "customer"}, Input: map[string]string{"200.33.300.1": "customer"},
Error: true, Error: true,
}, { }, {
Pos: helpers.Mark(),
Description: "Random key", Description: "Random key",
Input: gin.H{"kfgdjgkfj": "customer"}, Input: map[string]string{"kfgdjgkfj": "customer"},
Error: true, Error: true,
}, { }, {
Pos: helpers.Mark(),
Description: "Single value", Description: "Single value",
Input: "customer", Input: "customer",
Tests: map[string]string{ Tests: map[string]string{
@@ -115,11 +131,10 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
} }
for _, tc := range cases { for _, tc := range cases {
if tc.YAML == nil { if tc.YAML == nil {
if tc.Error {
tc.YAML = map[string]string{}
} else {
tc.YAML = tc.Input tc.YAML = tc.Input
} }
if tc.Tests == nil {
tc.Tests = map[string]string{}
} }
t.Run(tc.Description, func(t *testing.T) { t.Run(tc.Description, func(t *testing.T) {
var tree helpers.SubnetMap[string] var tree helpers.SubnetMap[string]
@@ -134,9 +149,11 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
} }
err = decoder.Decode(tc.Input) err = decoder.Decode(tc.Input)
if err != nil && !tc.Error { if err != nil && !tc.Error {
t.Fatalf("Decode() error:\n%+v", err) t.Fatalf("%sDecode() error:\n%+v", tc.Pos, err)
} else if err == nil && tc.Error { } else if err == nil && tc.Error {
t.Fatal("Decode() did not return an error") t.Fatalf("%sDecode() did not return an error", tc.Pos)
} else if tc.Error {
return
} }
got := map[string]string{} got := map[string]string{}
for k := range tc.Tests { for k := range tc.Tests {
@@ -144,20 +161,20 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
got[k] = v got[k] = v
} }
if diff := helpers.Diff(got, tc.Tests); diff != "" { if diff := helpers.Diff(got, tc.Tests); diff != "" {
t.Fatalf("Decode() (-got, +want):\n%s", diff) t.Fatalf("%sDecode() (-got, +want):\n%s", tc.Pos, diff)
} }
// Try to unmarshal with YAML // Try to unmarshal with YAML
buf, err := yaml.Marshal(tree) buf, err := yaml.Marshal(tree)
if err != nil { if err != nil {
t.Fatalf("yaml.Marshal() error:\n%+v", err) t.Fatalf("%syaml.Marshal() error:\n%+v", tc.Pos, err)
} }
got = map[string]string{} got = map[string]string{}
if err := yaml.Unmarshal(buf, &got); err != nil { if err := yaml.Unmarshal(buf, &got); err != nil {
t.Fatalf("yaml.Unmarshal() error:\n%+v", err) t.Fatalf("%syaml.Unmarshal() error:\n%+v", tc.Pos, err)
} }
if diff := helpers.Diff(got, tc.YAML); diff != "" { if diff := helpers.Diff(got, tc.YAML); diff != "" {
t.Fatalf("MarshalYAML() (-got, +want):\n%s", diff) t.Fatalf("%sMarshalYAML() (-got, +want):\n%s", tc.Pos, diff)
} }
}) })
} }
@@ -171,7 +188,7 @@ func TestSubnetMapUnmarshalHookWithMapValue(t *testing.T) {
cases := []struct { cases := []struct {
Pos helpers.Pos Pos helpers.Pos
Input gin.H Input gin.H
Expected gin.H Expected any
}{ }{
{ {
Pos: helpers.Mark(), Pos: helpers.Mark(),
@@ -179,10 +196,10 @@ func TestSubnetMapUnmarshalHookWithMapValue(t *testing.T) {
"blip": "some", "blip": "some",
"blop": "thing", "blop": "thing",
}, },
Expected: gin.H{ Expected: map[string]SomeStruct{
"::/0": gin.H{ "::/0": {
"Blip": "some", Blip: "some",
"Blop": "thing", Blop: "thing",
}, },
}, },
}, { }, {
@@ -197,14 +214,14 @@ func TestSubnetMapUnmarshalHookWithMapValue(t *testing.T) {
"blop": "stuff", "blop": "stuff",
}, },
}, },
Expected: gin.H{ Expected: map[string]SomeStruct{
"::/0": gin.H{ "::/0": {
"Blip": "some", Blip: "some",
"Blop": "thing", Blop: "thing",
}, },
"203.0.113.14/32": gin.H{ "203.0.113.14/32": {
"Blip": "other", Blip: "other",
"Blop": "stuff", Blop: "stuff",
}, },
}, },
}, },
@@ -717,3 +734,31 @@ func TestSubnetMapAllMaybeSorted(t *testing.T) {
} }
}) })
} }
func TestSubnetmapDiff(t *testing.T) {
got := helpers.MustNewSubnetMap(map[string]string{
"2001:db8::/64": "hello",
"::ffff:192.0.2.0/120": "bye",
})
expected := helpers.MustNewSubnetMap(map[string]string{
"2001:db8::/64": "hello",
"::ffff:192.0.2.0/120": "bye",
})
if diff := helpers.Diff(got, expected); diff != "" {
t.Fatalf("Diff():\n%+v", diff)
}
got.Set(netip.MustParsePrefix("2001:db8:1::/64"), "bye")
diffGot := helpers.Diff(got, expected)
diffGot = strings.ReplaceAll(diffGot, "\u00a0", " ")
diffExpected := ` (*helpers.SubnetMap[string])(Inverse(subnetmap.Transform, map[string]string{
"192.0.2.0/24": "bye",
- "2001:db8:1::/64": "bye",
"2001:db8::/64": "hello",
}))
`
if diff := helpers.Diff(diffGot, diffExpected); diff != "" {
t.Fatalf("Diff() (-got, +want):\n%+v", diff)
}
}

View File

@@ -141,7 +141,7 @@ func Mark() Pos {
// String returns a textual representation of a Pos. // String returns a textual representation of a Pos.
func (p Pos) String() string { func (p Pos) String() string {
if p.file != "" { if p.file != "" {
return fmt.Sprintf("%s:%d", p.file, p.line) return fmt.Sprintf("%s:%d: ", p.file, p.line)
} }
return "" return ""
} }

View File

@@ -10,6 +10,7 @@ import (
"testing" "testing"
"github.com/go-viper/mapstructure/v2" "github.com/go-viper/mapstructure/v2"
"github.com/google/go-cmp/cmp"
"akvorado/common/helpers/yaml" "akvorado/common/helpers/yaml"
) )
@@ -28,7 +29,7 @@ type ConfigurationDecodeCases []struct {
} }
// TestConfigurationDecode helps decoding configuration. It also test decoding from YAML. // TestConfigurationDecode helps decoding configuration. It also test decoding from YAML.
func TestConfigurationDecode(t *testing.T, cases ConfigurationDecodeCases, options ...DiffOption) { func TestConfigurationDecode(t *testing.T, cases ConfigurationDecodeCases, options ...cmp.Option) {
t.Helper() t.Helper()
for _, tc := range cases { for _, tc := range cases {
for _, fromYAML := range []bool{false, true} { for _, fromYAML := range []bool{false, true} {

View File

@@ -6,85 +6,28 @@
package helpers package helpers
import ( import (
"fmt"
"net"
"net/netip" "net/netip"
"reflect"
"time"
"github.com/kylelemons/godebug/pretty" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
) )
var prettyC = pretty.Config{ var diffCmpOptions cmp.Options
Diffable: true,
PrintStringers: false,
SkipZeroFields: true,
IncludeUnexported: false,
}
func formatByte(v any) string { // RegisterCmpOption adds an option that will be used in all call to Diff().
return fmt.Sprintf("0x%x", v) func RegisterCmpOption(option cmp.Option) {
} diffCmpOptions = append(diffCmpOptions, option)
func defaultPrettyFormatters() map[reflect.Type]any {
result := map[reflect.Type]any{
reflect.TypeOf(net.IP{}): fmt.Sprint,
reflect.TypeOf(netip.Addr{}): fmt.Sprint,
reflect.TypeOf(netip.Prefix{}): fmt.Sprint,
reflect.TypeOf(time.Time{}): fmt.Sprint,
reflect.TypeOf(SubnetMap[string]{}): fmt.Sprint,
reflect.TypeOf(SubnetMap[uint]{}): fmt.Sprint,
reflect.TypeOf(SubnetMap[uint16]{}): fmt.Sprint,
reflect.TypeOf(byte(0)): formatByte,
}
for t, fn := range nonDefaultPrettyFormatters {
result[t] = fn
}
return result
}
var nonDefaultPrettyFormatters = map[reflect.Type]any{}
// AddPrettyFormatter add a global pretty formatter. We cannot put everything in
// the default map due to cycles.
func AddPrettyFormatter(t reflect.Type, fn any) {
nonDefaultPrettyFormatters[t] = fn
}
// DiffOption changes the behavior of the Diff function.
type DiffOption struct {
kind int
// When this is a formatter
t reflect.Type
fn any
} }
// Diff return a diff of two objects. If no diff, an empty string is // Diff return a diff of two objects. If no diff, an empty string is
// returned. // returned.
func Diff(a, b any, options ...DiffOption) string { func Diff(a, b any, options ...cmp.Option) string {
prettyC := prettyC options = append(options, diffCmpOptions...)
prettyC.Formatter = defaultPrettyFormatters() return cmp.Diff(a, b, options...)
for _, option := range options {
switch option.kind {
case DiffUnexported.kind:
prettyC.IncludeUnexported = true
case DiffZero.kind:
prettyC.SkipZeroFields = false
case DiffFormatter(nil, nil).kind:
prettyC.Formatter[option.t] = option.fn
}
}
return prettyC.Compare(a, b)
} }
var ( func init() {
// DiffUnexported will display diff of unexported fields too. RegisterCmpOption(cmpopts.EquateComparable(netip.Addr{}))
DiffUnexported = DiffOption{kind: 1} RegisterCmpOption(cmpopts.EquateComparable(netip.Prefix{}))
// DiffZero will include zero-field in diff RegisterCmpOption(cmpopts.EquateErrors())
DiffZero = DiffOption{kind: 2}
)
// DiffFormatter adds a new formatter
func DiffFormatter(t reflect.Type, fn any) DiffOption {
return DiffOption{kind: 3, t: t, fn: fn}
} }

View File

@@ -24,12 +24,12 @@ type HTTPEndpointCases []struct {
Method string Method string
URL string URL string
Header http.Header Header http.Header
JSONInput any JSONInput gin.H
ContentType string ContentType string
StatusCode int StatusCode int
FirstLines []string FirstLines []string
JSONOutput any JSONOutput gin.H
} }
// TestHTTPEndpoints test a few HTTP endpoints // TestHTTPEndpoints test a few HTTP endpoints
@@ -108,6 +108,9 @@ func TestHTTPEndpoints(t *testing.T, serverAddr net.Addr, cases HTTPEndpointCase
for reader.Scan() && len(got) < len(tc.FirstLines) { for reader.Scan() && len(got) < len(tc.FirstLines) {
got = append(got, reader.Text()) got = append(got, reader.Text())
} }
if tc.FirstLines == nil {
tc.FirstLines = []string{}
}
if diff := Diff(got, tc.FirstLines); diff != "" { if diff := Diff(got, tc.FirstLines); diff != "" {
t.Errorf("%s%s %s (-got, +want):\n%s", tc.Pos, tc.Method, tc.URL, diff) t.Errorf("%s%s %s (-got, +want):\n%s", tc.Pos, tc.Method, tc.URL, diff)
} }
@@ -117,7 +120,18 @@ func TestHTTPEndpoints(t *testing.T, serverAddr net.Addr, cases HTTPEndpointCase
if err := decoder.Decode(&got); err != nil { if err := decoder.Decode(&got); err != nil {
t.Fatalf("%s%s %s:\n%+v", tc.Pos, tc.Method, tc.URL, err) t.Fatalf("%s%s %s:\n%+v", tc.Pos, tc.Method, tc.URL, err)
} }
if diff := Diff(got, tc.JSONOutput); diff != "" {
// Encode/decode expected to compare JSON stuff
var expected gin.H
expectedBytes, err := json.Marshal(tc.JSONOutput)
if err != nil {
t.Fatalf("json.Marshal() error:\n%+v", err)
}
if err := json.Unmarshal(expectedBytes, &expected); err != nil {
t.Fatalf("json.Unmarshal() error:\n%+v", err)
}
if diff := Diff(got, expected); diff != "" {
t.Fatalf("%s%s %s (-got, +want):\n%s", tc.Pos, tc.Method, tc.URL, diff) t.Fatalf("%s%s %s (-got, +want):\n%s", tc.Pos, tc.Method, tc.URL, diff)
} }
} }

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
//go:build !release
package helpers
import "github.com/google/go-cmp/cmp"
// RegisterSubnetMapCmp register a subnetmap to work with cmp.Equal()/cmp.Diff()
func RegisterSubnetMapCmp[T any]() {
RegisterCmpOption(
cmp.Transformer("subnetmap.Transform",
func(sm *SubnetMap[T]) map[string]T {
return sm.ToMap()
}))
}
func init() {
RegisterSubnetMapCmp[uint16]()
RegisterSubnetMapCmp[uint]()
RegisterSubnetMapCmp[string]()
}

View File

@@ -9,8 +9,6 @@ import (
"akvorado/common/helpers" "akvorado/common/helpers"
"akvorado/common/helpers/yaml" "akvorado/common/helpers/yaml"
"github.com/gin-gonic/gin"
) )
func TestUnmarshalWithIn(t *testing.T) { func TestUnmarshalWithIn(t *testing.T) {
@@ -19,14 +17,14 @@ func TestUnmarshalWithIn(t *testing.T) {
if err := yaml.UnmarshalWithInclude(fsys, "base.yaml", &got); err != nil { if err := yaml.UnmarshalWithInclude(fsys, "base.yaml", &got); err != nil {
t.Fatalf("UnmarshalWithInclude() error:\n%+v", err) t.Fatalf("UnmarshalWithInclude() error:\n%+v", err)
} }
expected := gin.H{ expected := map[string]any{
"file1": gin.H{"name": "1.yaml"}, "file1": map[string]any{"name": "1.yaml"},
"file2": gin.H{"name": "2.yaml"}, "file2": map[string]any{"name": "2.yaml"},
"nested": gin.H{ "nested": map[string]any{
"file1": gin.H{"name": "1.yaml"}, "file1": map[string]any{"name": "1.yaml"},
}, },
"list1": []string{"el1", "el2", "el3"}, "list1": []any{"el1", "el2", "el3"},
"list2": []any{gin.H{ "list2": []any{map[string]any{
"protocol": "tcp", "protocol": "tcp",
"size": 1300, "size": 1300,
}, "el2", "el3"}, }, "el2", "el3"},

View File

@@ -3,15 +3,14 @@
//go:build !release //go:build !release
package clickhouse package pb
import ( import (
"fmt"
"reflect"
"akvorado/common/helpers" "akvorado/common/helpers"
"github.com/google/go-cmp/cmp/cmpopts"
) )
func init() { func init() {
helpers.AddPrettyFormatter(reflect.TypeOf(helpers.SubnetMap[NetworkAttributes]{}), fmt.Sprint) helpers.RegisterCmpOption(cmpopts.IgnoreUnexported(RawFlow{}))
} }

View File

@@ -4,8 +4,6 @@
package remotedatasource package remotedatasource
import ( import (
"fmt"
"reflect"
"testing" "testing"
"time" "time"
@@ -100,5 +98,5 @@ func TestSourceDecode(t *testing.T) {
}, },
Error: true, Error: true,
}, },
}, helpers.DiffFormatter(reflect.TypeOf(TransformQuery{}), fmt.Sprint), helpers.DiffZero) })
} }

View File

@@ -136,7 +136,7 @@ func TestSource(t *testing.T) {
handler := remoteDataHandler{ handler := remoteDataHandler{
data: []remoteData{}, data: []remoteData{},
} }
var expected []remoteData expected := []remoteData{}
handler.fetcher, _ = New[remoteData](r, handler.UpdateData, "test", config) handler.fetcher, _ = New[remoteData](r, handler.UpdateData, "test", config)
handler.fetcher.Start() handler.fetcher.Start()

View File

@@ -139,12 +139,12 @@ func TestHealthcheckHTTPHandler(t *testing.T) {
} }
expected := gin.H{ expected := gin.H{
"status": "error", "status": "error",
"details": gin.H{ "details": map[string]any{
"hc1": map[string]string{ "hc1": map[string]any{
"status": "ok", "status": "ok",
"reason": "all well", "reason": "all well",
}, },
"hc2": map[string]string{ "hc2": map[string]any{
"status": "error", "status": "error",
"reason": "trying to be better", "reason": "trying to be better",
}, },

View File

@@ -161,19 +161,23 @@ func (bf *FlowMessage) AppendUint(columnKey ColumnKey, value uint64) {
switch col := col.(type) { switch col := col.(type) {
case *proto.ColUInt64: case *proto.ColUInt64:
col.Append(value) col.Append(value)
bf.appendDebug(columnKey, value)
case *proto.ColUInt32: case *proto.ColUInt32:
col.Append(uint32(value)) col.Append(uint32(value))
bf.appendDebug(columnKey, uint32(value))
case *proto.ColUInt16: case *proto.ColUInt16:
col.Append(uint16(value)) col.Append(uint16(value))
bf.appendDebug(columnKey, uint16(value))
case *proto.ColUInt8: case *proto.ColUInt8:
col.Append(uint8(value)) col.Append(uint8(value))
bf.appendDebug(columnKey, uint8(value))
case *proto.ColEnum8: case *proto.ColEnum8:
col.Append(proto.Enum8(value)) col.Append(proto.Enum8(value))
bf.appendDebug(columnKey, uint8(value))
default: default:
panic(fmt.Sprintf("unhandled uint type %q", col.Type())) panic(fmt.Sprintf("unhandled uint type %q", col.Type()))
} }
bf.batch.columnSet.Set(uint(columnKey)) bf.batch.columnSet.Set(uint(columnKey))
bf.appendDebug(columnKey, value)
} }
// AppendString adds a String value to the provided column // AppendString adds a String value to the provided column

View File

@@ -6,11 +6,13 @@ package schema
import ( import (
"net/netip" "net/netip"
"slices" "slices"
"strings"
"testing" "testing"
"akvorado/common/helpers" "akvorado/common/helpers"
"github.com/ClickHouse/ch-go/proto" "github.com/ClickHouse/ch-go/proto"
"github.com/google/go-cmp/cmp"
) )
func TestAppendDefault(t *testing.T) { func TestAppendDefault(t *testing.T) {
@@ -50,10 +52,10 @@ func TestAppendBasics(t *testing.T) {
bf.AppendUint(ColumnPackets, 200) bf.AppendUint(ColumnPackets, 200)
expected := map[ColumnKey]any{ expected := map[ColumnKey]any{
ColumnTimeReceived: 1000, ColumnTimeReceived: uint32(1000),
ColumnSamplingRate: 20000, ColumnSamplingRate: uint64(20000),
ColumnDstAS: 65000, ColumnDstAS: uint32(65000),
ColumnPackets: 100, ColumnPackets: uint64(100),
} }
got := bf.OtherColumns got := bf.OtherColumns
if diff := helpers.Diff(got, expected); diff != "" { if diff := helpers.Diff(got, expected); diff != "" {
@@ -94,7 +96,7 @@ func TestAppendArrayUInt32Columns(t *testing.T) {
Offsets: proto.ColUInt64{3, 6}, Offsets: proto.ColUInt64{3, 6},
Data: &proto.ColUInt32{65400, 65500, 65001, 65403, 65503, 65003}, Data: &proto.ColUInt32{65400, 65500, 65001, 65403, 65503, 65003},
} }
if diff := helpers.Diff(got, expected); diff != "" { if diff := helpers.Diff(got, &expected); diff != "" {
t.Errorf("AppendArrayUInt32 (-got, +want):\n%s", diff) t.Errorf("AppendArrayUInt32 (-got, +want):\n%s", diff)
} }
} }
@@ -122,7 +124,7 @@ func TestAppendArrayUInt128Columns(t *testing.T) {
{High: (65401 << 32) + 100, Low: 201}, {High: (65401 << 32) + 100, Low: 201},
}, },
} }
if diff := helpers.Diff(got, expected); diff != "" { if diff := helpers.Diff(got, &expected); diff != "" {
t.Errorf("AppendArrayUInt128 (-got, +want):\n%s", diff) t.Errorf("AppendArrayUInt128 (-got, +want):\n%s", diff)
} }
} }
@@ -142,10 +144,10 @@ func TestUndoUInt64(t *testing.T) {
expectedBytes := proto.ColUInt64{100} expectedBytes := proto.ColUInt64{100}
expectedPackets := proto.ColUInt64{200} expectedPackets := proto.ColUInt64{200}
if diff := helpers.Diff(bytesCol, expectedBytes); diff != "" { if diff := helpers.Diff(bytesCol, &expectedBytes); diff != "" {
t.Errorf("Initial bytes column state (-got, +want):\n%s", diff) t.Errorf("Initial bytes column state (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(packetsCol, expectedPackets); diff != "" { if diff := helpers.Diff(packetsCol, &expectedPackets); diff != "" {
t.Errorf("Initial packets column state (-got, +want):\n%s", diff) t.Errorf("Initial packets column state (-got, +want):\n%s", diff)
} }
@@ -155,10 +157,10 @@ func TestUndoUInt64(t *testing.T) {
expectedBytesAfter := proto.ColUInt64{} expectedBytesAfter := proto.ColUInt64{}
expectedPacketsAfter := proto.ColUInt64{} expectedPacketsAfter := proto.ColUInt64{}
if diff := helpers.Diff(bytesCol, expectedBytesAfter); diff != "" { if diff := helpers.Diff(bytesCol, &expectedBytesAfter); diff != "" {
t.Errorf("Bytes column after undo (-got, +want):\n%s", diff) t.Errorf("Bytes column after undo (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(packetsCol, expectedPacketsAfter); diff != "" { if diff := helpers.Diff(packetsCol, &expectedPacketsAfter); diff != "" {
t.Errorf("Packets column after undo (-got, +want):\n%s", diff) t.Errorf("Packets column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -178,10 +180,10 @@ func TestUndoUInt32(t *testing.T) {
expectedSrc := proto.ColUInt32{65001} expectedSrc := proto.ColUInt32{65001}
expectedDst := proto.ColUInt32{65002} expectedDst := proto.ColUInt32{65002}
if diff := helpers.Diff(srcCol, expectedSrc); diff != "" { if diff := helpers.Diff(srcCol, &expectedSrc); diff != "" {
t.Errorf("Initial SrcAS column state (-got, +want):\n%s", diff) t.Errorf("Initial SrcAS column state (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(dstCol, expectedDst); diff != "" { if diff := helpers.Diff(dstCol, &expectedDst); diff != "" {
t.Errorf("Initial DstAS column state (-got, +want):\n%s", diff) t.Errorf("Initial DstAS column state (-got, +want):\n%s", diff)
} }
@@ -191,10 +193,10 @@ func TestUndoUInt32(t *testing.T) {
expectedSrcAfter := proto.ColUInt32{} expectedSrcAfter := proto.ColUInt32{}
expectedDstAfter := proto.ColUInt32{} expectedDstAfter := proto.ColUInt32{}
if diff := helpers.Diff(srcCol, expectedSrcAfter); diff != "" { if diff := helpers.Diff(srcCol, &expectedSrcAfter); diff != "" {
t.Errorf("SrcAS column after undo (-got, +want):\n%s", diff) t.Errorf("SrcAS column after undo (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(dstCol, expectedDstAfter); diff != "" { if diff := helpers.Diff(dstCol, &expectedDstAfter); diff != "" {
t.Errorf("DstAS column after undo (-got, +want):\n%s", diff) t.Errorf("DstAS column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -214,10 +216,10 @@ func TestUndoUInt16(t *testing.T) {
expectedSrc := proto.ColUInt16{80} expectedSrc := proto.ColUInt16{80}
expectedDst := proto.ColUInt16{443} expectedDst := proto.ColUInt16{443}
if diff := helpers.Diff(srcCol, expectedSrc); diff != "" { if diff := helpers.Diff(srcCol, &expectedSrc); diff != "" {
t.Errorf("Initial SrcPort column state (-got, +want):\n%s", diff) t.Errorf("Initial SrcPort column state (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(dstCol, expectedDst); diff != "" { if diff := helpers.Diff(dstCol, &expectedDst); diff != "" {
t.Errorf("Initial DstPort column state (-got, +want):\n%s", diff) t.Errorf("Initial DstPort column state (-got, +want):\n%s", diff)
} }
@@ -227,10 +229,10 @@ func TestUndoUInt16(t *testing.T) {
expectedSrcAfter := proto.ColUInt16{} expectedSrcAfter := proto.ColUInt16{}
expectedDstAfter := proto.ColUInt16{} expectedDstAfter := proto.ColUInt16{}
if diff := helpers.Diff(srcCol, expectedSrcAfter); diff != "" { if diff := helpers.Diff(srcCol, &expectedSrcAfter); diff != "" {
t.Errorf("SrcPort column after undo (-got, +want):\n%s", diff) t.Errorf("SrcPort column after undo (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(dstCol, expectedDstAfter); diff != "" { if diff := helpers.Diff(dstCol, &expectedDstAfter); diff != "" {
t.Errorf("DstPort column after undo (-got, +want):\n%s", diff) t.Errorf("DstPort column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -246,7 +248,7 @@ func TestUndoUInt8(t *testing.T) {
col := bf.batch.columns[ColumnSrcNetMask].(*proto.ColUInt8) col := bf.batch.columns[ColumnSrcNetMask].(*proto.ColUInt8)
expected := proto.ColUInt8{6} expected := proto.ColUInt8{6}
if diff := helpers.Diff(col, expected); diff != "" { if diff := helpers.Diff(col, &expected); diff != "" {
t.Errorf("Initial Proto column state (-got, +want):\n%s", diff) t.Errorf("Initial Proto column state (-got, +want):\n%s", diff)
} }
@@ -255,7 +257,7 @@ func TestUndoUInt8(t *testing.T) {
expectedAfter := proto.ColUInt8{} expectedAfter := proto.ColUInt8{}
if diff := helpers.Diff(col, expectedAfter); diff != "" { if diff := helpers.Diff(col, &expectedAfter); diff != "" {
t.Errorf("Proto column after undo (-got, +want):\n%s", diff) t.Errorf("Proto column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -278,10 +280,10 @@ func TestUndoIPv6(t *testing.T) {
expectedSrc := proto.ColIPv6{srcAddr.As16()} expectedSrc := proto.ColIPv6{srcAddr.As16()}
expectedDst := proto.ColIPv6{dstAddr.As16()} expectedDst := proto.ColIPv6{dstAddr.As16()}
if diff := helpers.Diff(srcCol, expectedSrc); diff != "" { if diff := helpers.Diff(srcCol, &expectedSrc); diff != "" {
t.Errorf("Initial SrcAddr column state (-got, +want):\n%s", diff) t.Errorf("Initial SrcAddr column state (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(dstCol, expectedDst); diff != "" { if diff := helpers.Diff(dstCol, &expectedDst); diff != "" {
t.Errorf("Initial DstAddr column state (-got, +want):\n%s", diff) t.Errorf("Initial DstAddr column state (-got, +want):\n%s", diff)
} }
@@ -291,10 +293,10 @@ func TestUndoIPv6(t *testing.T) {
expectedSrcAfter := proto.ColIPv6{} expectedSrcAfter := proto.ColIPv6{}
expectedDstAfter := proto.ColIPv6{} expectedDstAfter := proto.ColIPv6{}
if diff := helpers.Diff(srcCol, expectedSrcAfter); diff != "" { if diff := helpers.Diff(srcCol, &expectedSrcAfter); diff != "" {
t.Errorf("SrcAddr column after undo (-got, +want):\n%s", diff) t.Errorf("SrcAddr column after undo (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(dstCol, expectedDstAfter); diff != "" { if diff := helpers.Diff(dstCol, &expectedDstAfter); diff != "" {
t.Errorf("DstAddr column after undo (-got, +want):\n%s", diff) t.Errorf("DstAddr column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -310,7 +312,7 @@ func TestUndoDateTime(t *testing.T) {
col := bf.batch.columns[ColumnTimeReceived].(*proto.ColDateTime) col := bf.batch.columns[ColumnTimeReceived].(*proto.ColDateTime)
expected := proto.ColDateTime{Data: []proto.DateTime{1000}} expected := proto.ColDateTime{Data: []proto.DateTime{1000}}
if diff := helpers.Diff(col, expected); diff != "" { if diff := helpers.Diff(col, &expected); diff != "" {
t.Errorf("Initial TimeReceived column state (-got, +want):\n%s", diff) t.Errorf("Initial TimeReceived column state (-got, +want):\n%s", diff)
} }
@@ -319,7 +321,7 @@ func TestUndoDateTime(t *testing.T) {
expectedAfter := proto.ColDateTime{Data: []proto.DateTime{}} expectedAfter := proto.ColDateTime{Data: []proto.DateTime{}}
if diff := helpers.Diff(col, expectedAfter); diff != "" { if diff := helpers.Diff(col, &expectedAfter); diff != "" {
t.Errorf("TimeReceived column after undo (-got, +want):\n%s", diff) t.Errorf("TimeReceived column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -335,7 +337,7 @@ func TestUndoEnum8(t *testing.T) {
col := bf.batch.columns[ColumnInIfBoundary].(*proto.ColEnum8) col := bf.batch.columns[ColumnInIfBoundary].(*proto.ColEnum8)
expected := proto.ColEnum8{proto.Enum8(InterfaceBoundaryExternal)} expected := proto.ColEnum8{proto.Enum8(InterfaceBoundaryExternal)}
if diff := helpers.Diff(col, expected); diff != "" { if diff := helpers.Diff(col, &expected); diff != "" {
t.Errorf("Initial InIfBoundary column state (-got, +want):\n%s", diff) t.Errorf("Initial InIfBoundary column state (-got, +want):\n%s", diff)
} }
@@ -344,7 +346,7 @@ func TestUndoEnum8(t *testing.T) {
expectedAfter := proto.ColEnum8{} expectedAfter := proto.ColEnum8{}
if diff := helpers.Diff(col, expectedAfter); diff != "" { if diff := helpers.Diff(col, &expectedAfter); diff != "" {
t.Errorf("InIfBoundary column after undo (-got, +want):\n%s", diff) t.Errorf("InIfBoundary column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -364,10 +366,14 @@ func TestUndoLowCardinalityString(t *testing.T) {
expectedName := proto.ColLowCardinality[string]{Values: []string{"router1"}} expectedName := proto.ColLowCardinality[string]{Values: []string{"router1"}}
expectedRole := proto.ColLowCardinality[string]{Values: []string{"edge"}} expectedRole := proto.ColLowCardinality[string]{Values: []string{"edge"}}
if diff := helpers.Diff(nameCol, expectedName); diff != "" { diffOpt := cmp.Comparer(func(x, y proto.ColLowCardinality[string]) bool {
return slices.Compare(x.Values, y.Values) == 0
})
if diff := helpers.Diff(nameCol, &expectedName, diffOpt); diff != "" {
t.Errorf("Initial ExporterName column state (-got, +want):\n%s", diff) t.Errorf("Initial ExporterName column state (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(roleCol, expectedRole); diff != "" { if diff := helpers.Diff(roleCol, &expectedRole, diffOpt); diff != "" {
t.Errorf("Initial ExporterRole column state (-got, +want):\n%s", diff) t.Errorf("Initial ExporterRole column state (-got, +want):\n%s", diff)
} }
@@ -377,10 +383,10 @@ func TestUndoLowCardinalityString(t *testing.T) {
expectedNameAfter := proto.ColLowCardinality[string]{Values: []string{}} expectedNameAfter := proto.ColLowCardinality[string]{Values: []string{}}
expectedRoleAfter := proto.ColLowCardinality[string]{Values: []string{}} expectedRoleAfter := proto.ColLowCardinality[string]{Values: []string{}}
if diff := helpers.Diff(nameCol, expectedNameAfter); diff != "" { if diff := helpers.Diff(nameCol, &expectedNameAfter, diffOpt); diff != "" {
t.Errorf("ExporterName column after undo (-got, +want):\n%s", diff) t.Errorf("ExporterName column after undo (-got, +want):\n%s", diff)
} }
if diff := helpers.Diff(roleCol, expectedRoleAfter); diff != "" { if diff := helpers.Diff(roleCol, &expectedRoleAfter, diffOpt); diff != "" {
t.Errorf("ExporterRole column after undo (-got, +want):\n%s", diff) t.Errorf("ExporterRole column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -393,11 +399,17 @@ func TestUndoLowCardinalityIPv6(t *testing.T) {
addr := netip.MustParseAddr("2001:db8::1") addr := netip.MustParseAddr("2001:db8::1")
bf.AppendIPv6(ColumnExporterAddress, addr) bf.AppendIPv6(ColumnExporterAddress, addr)
diffOpt := cmp.Comparer(func(x, y proto.ColLowCardinality[proto.IPv6]) bool {
return slices.CompareFunc(x.Values, y.Values, func(a, b proto.IPv6) int {
return strings.Compare(a.String(), b.String())
}) == 0
})
// Check we have the expected initial state // Check we have the expected initial state
col := bf.batch.columns[ColumnExporterAddress].(*proto.ColLowCardinality[proto.IPv6]) col := bf.batch.columns[ColumnExporterAddress].(*proto.ColLowCardinality[proto.IPv6])
expected := proto.ColLowCardinality[proto.IPv6]{Values: []proto.IPv6{addr.As16()}} expected := proto.ColLowCardinality[proto.IPv6]{Values: []proto.IPv6{addr.As16()}}
if diff := helpers.Diff(col, expected); diff != "" { if diff := helpers.Diff(col, &expected, diffOpt); diff != "" {
t.Errorf("Initial ExporterAddress column state (-got, +want):\n%s", diff) t.Errorf("Initial ExporterAddress column state (-got, +want):\n%s", diff)
} }
@@ -406,7 +418,7 @@ func TestUndoLowCardinalityIPv6(t *testing.T) {
expectedAfter := proto.ColLowCardinality[proto.IPv6]{Values: []proto.IPv6{}} expectedAfter := proto.ColLowCardinality[proto.IPv6]{Values: []proto.IPv6{}}
if diff := helpers.Diff(col, expectedAfter); diff != "" { if diff := helpers.Diff(col, &expectedAfter, diffOpt); diff != "" {
t.Errorf("ExporterAddress column after undo (-got, +want):\n%s", diff) t.Errorf("ExporterAddress column after undo (-got, +want):\n%s", diff)
} }
} }
@@ -593,11 +605,11 @@ func TestBuildProtoInput(t *testing.T) {
// Let's compare a subset // Let's compare a subset
expected := proto.Input{ expected := proto.Input{
{Name: "TimeReceived", Data: proto.ColDateTime{Data: []proto.DateTime{1002, 1003}}}, {Name: "TimeReceived", Data: &proto.ColDateTime{Data: []proto.DateTime{1002, 1003}}},
{Name: "SrcAS", Data: proto.ColUInt32{65000, 65001}}, {Name: "SrcAS", Data: &proto.ColUInt32{65000, 65001}},
{Name: "DstAS", Data: proto.ColUInt32{0, 0}}, {Name: "DstAS", Data: &proto.ColUInt32{0, 0}},
{Name: "Bytes", Data: proto.ColUInt64{2000, 202}}, {Name: "Bytes", Data: &proto.ColUInt64{2000, 202}},
{Name: "Packets", Data: proto.ColUInt64{30, 3}}, {Name: "Packets", Data: &proto.ColUInt64{30, 3}},
} }
got = slices.DeleteFunc(got, func(col proto.InputColumn) bool { got = slices.DeleteFunc(got, func(col proto.InputColumn) bool {
return !slices.Contains([]string{"TimeReceived", "SrcAS", "DstAS", "Packets", "Bytes"}, col.Name) return !slices.Contains([]string{"TimeReceived", "SrcAS", "DstAS", "Packets", "Bytes"}, col.Name)

View File

@@ -7,6 +7,9 @@ import (
"testing" "testing"
"akvorado/common/helpers" "akvorado/common/helpers"
"github.com/bits-and-blooms/bitset"
"github.com/google/go-cmp/cmp"
) )
func TestFlowsClickHouse(t *testing.T) { func TestFlowsClickHouse(t *testing.T) {
@@ -35,7 +38,11 @@ func TestFinalizeTwice(t *testing.T) {
c := NewMock(t) c := NewMock(t)
oldSchema := c.Schema oldSchema := c.Schema
newSchema := c.finalize() newSchema := c.finalize()
if diff := helpers.Diff(oldSchema, newSchema, helpers.DiffUnexported); diff != "" { if diff := helpers.Diff(oldSchema, newSchema,
cmp.AllowUnexported(Schema{}),
cmp.Comparer(func(x, y bitset.BitSet) bool {
return x.Equal(&y)
})); diff != "" {
t.Fatalf("finalize() (-old, +new):\n%s", diff) t.Fatalf("finalize() (-old, +new):\n%s", diff)
} }
} }

View File

@@ -160,29 +160,29 @@ func TestInsertMemory(t *testing.T) {
"SamplingRate": "20000", "SamplingRate": "20000",
"ExporterAddress": "::ffff:203.0.113.14", "ExporterAddress": "::ffff:203.0.113.14",
"ExporterName": "router1.example.net", "ExporterName": "router1.example.net",
"SrcAS": 65000, "SrcAS": float64(65000),
"DstAS": 12322, "DstAS": float64(12322),
"Bytes": "20", "Bytes": "20",
"Packets": "3", "Packets": "3",
"InIfBoundary": "internal", "InIfBoundary": "internal",
"OutIfBoundary": "external", "OutIfBoundary": "external",
"InIfSpeed": 10000, "InIfSpeed": float64(10000),
"EType": helpers.ETypeIPv4, "EType": float64(helpers.ETypeIPv4),
}, { }, {
"TimeReceived": "1970-01-01 00:16:41", "TimeReceived": "1970-01-01 00:16:41",
"SamplingRate": "20000", "SamplingRate": "20000",
"ExporterAddress": "::ffff:203.0.113.14", "ExporterAddress": "::ffff:203.0.113.14",
"ExporterName": "router1.example.net", "ExporterName": "router1.example.net",
"SrcAS": 12322, "SrcAS": float64(12322),
"DstAS": 65000, "DstAS": float64(65000),
"Bytes": "200", "Bytes": "200",
"Packets": "3", "Packets": "3",
"InIfBoundary": "external", "InIfBoundary": "external",
"OutIfBoundary": "undefined", "OutIfBoundary": "undefined",
"OutIfSpeed": 10000, "OutIfSpeed": float64(10000),
"EType": helpers.ETypeIPv4, "EType": float64(helpers.ETypeIPv4),
"DstASPath": []uint32{65400, 65500, 65001}, "DstASPath": []any{float64(65400), float64(65500), float64(65001)},
"DstLargeCommunities": []string{ "DstLargeCommunities": []any{
"1206435509165107881967816", // 65401:100:200 "1206435509165107881967816", // 65401:100:200
"1206435509165107881967817", // 65401:100:201 "1206435509165107881967817", // 65401:100:201
}, },

View File

@@ -6,11 +6,11 @@
package schema package schema
import ( import (
"fmt"
"reflect"
"testing" "testing"
"akvorado/common/helpers" "akvorado/common/helpers"
"github.com/google/go-cmp/cmp/cmpopts"
) )
var debug = true var debug = true
@@ -44,5 +44,5 @@ func (c *Component) EnableAllColumns() *Component {
} }
func init() { func init() {
helpers.AddPrettyFormatter(reflect.TypeOf(ColumnBytes), fmt.Sprint) helpers.RegisterCmpOption(cmpopts.IgnoreUnexported(FlowMessage{}))
} }

View File

@@ -351,7 +351,6 @@ output provider */ = 'telia'`,
s = s.EnableAllColumns() s = s.EnableAllColumns()
for _, tc := range cases { for _, tc := range cases {
tc.MetaIn.Schema = s tc.MetaIn.Schema = s
tc.MetaOut.Schema = tc.MetaIn.Schema
got, err := Parse("", []byte(tc.Input), GlobalStore("meta", &tc.MetaIn)) got, err := Parse("", []byte(tc.Input), GlobalStore("meta", &tc.MetaIn))
if err != nil { if err != nil {
t.Errorf("Parse(%q) error:\n%+v", tc.Input, err) t.Errorf("Parse(%q) error:\n%+v", tc.Input, err)
@@ -397,7 +396,6 @@ func TestValidMaterializedFilter(t *testing.T) {
cs.ClickHouseMaterialized = true cs.ClickHouseMaterialized = true
tc.MetaIn.Schema = s tc.MetaIn.Schema = s
tc.MetaOut.Schema = tc.MetaIn.Schema
got, err := Parse("", []byte(tc.Input), GlobalStore("meta", &tc.MetaIn)) got, err := Parse("", []byte(tc.Input), GlobalStore("meta", &tc.MetaIn))
if err != nil { if err != nil {
t.Errorf("Parse(%q) error:\n%+v", tc.Input, err) t.Errorf("Parse(%q) error:\n%+v", tc.Input, err)

16
console/filter/tests.go Normal file
View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2025 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
//go:build !release
package filter
import (
"akvorado/common/helpers"
"github.com/google/go-cmp/cmp/cmpopts"
)
func init() {
helpers.RegisterCmpOption(cmpopts.IgnoreFields(Meta{}, "Schema"))
}

View File

@@ -46,6 +46,8 @@ func TestGraphLineInputReverseDirection(t *testing.T) {
}, },
Points: 100, Points: 100,
} }
input.Filter.Validate(input.schema)
expected.Filter.Validate(input.schema)
query.Columns(input.Dimensions).Validate(input.schema) query.Columns(input.Dimensions).Validate(input.schema)
query.Columns(expected.Dimensions).Validate(input.schema) query.Columns(expected.Dimensions).Validate(input.schema)
got := input.reverseDirection() got := input.reverseDirection()

View File

@@ -39,6 +39,11 @@ func (qc Column) String() string {
return qc.name return qc.name
} }
// Equal returns true iff two columns have the same name.
func (qc Column) Equal(oqc Column) bool {
return qc.name == oqc.name
}
// MarshalText turns a column into a string. // MarshalText turns a column into a string.
func (qc Column) MarshalText() ([]byte, error) { func (qc Column) MarshalText() ([]byte, error) {
return []byte(qc.name), nil return []byte(qc.name), nil

View File

@@ -4,8 +4,6 @@
package query_test package query_test
import ( import (
"fmt"
"reflect"
"testing" "testing"
"akvorado/common/helpers" "akvorado/common/helpers"
@@ -38,7 +36,7 @@ func TestUnmarshalQueryColumn(t *testing.T) {
if err != nil { if err != nil {
continue continue
} }
if diff := helpers.Diff(qc.Key(), tc.Expected, helpers.DiffFormatter(reflect.TypeOf(schema.ColumnBytes), fmt.Sprint)); diff != "" { if diff := helpers.Diff(qc.Key(), tc.Expected); diff != "" {
t.Fatalf("UnmarshalText(%q) (-got, +want):\n%s", tc.Input, diff) t.Fatalf("UnmarshalText(%q) (-got, +want):\n%s", tc.Input, diff)
} }
} }
@@ -145,7 +143,7 @@ func TestReverseDirection(t *testing.T) {
query.NewColumn("ExporterName"), query.NewColumn("ExporterName"),
query.NewColumn("OutIfProvider"), query.NewColumn("OutIfProvider"),
} }
if diff := helpers.Diff(columns, expected, helpers.DiffFormatter(reflect.TypeOf(query.Column{}), fmt.Sprint)); diff != "" { if diff := helpers.Diff(columns, expected); diff != "" {
t.Fatalf("Reverse() (-got, +want):\n%s", diff) t.Fatalf("Reverse() (-got, +want):\n%s", diff)
} }
} }

View File

@@ -34,6 +34,11 @@ func (qf Filter) String() string {
return qf.filter return qf.filter
} }
// Equal tells if two filters are equal.
func (qf Filter) Equal(oqf Filter) bool {
return qf.filter == oqf.filter
}
// MarshalText turns a filter into a string. // MarshalText turns a filter into a string.
func (qf Filter) MarshalText() ([]byte, error) { func (qf Filter) MarshalText() ([]byte, error) {
return []byte(qf.filter), nil return []byte(qf.filter), nil

View File

@@ -9,6 +9,8 @@ import (
"testing" "testing"
"github.com/benbjohnson/clock" "github.com/benbjohnson/clock"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"akvorado/common/clickhousedb" "akvorado/common/clickhousedb"
"akvorado/common/clickhousedb/mocks" "akvorado/common/clickhousedb/mocks"
@@ -43,3 +45,8 @@ func NewMock(t *testing.T, config Configuration) (*Component, *httpserver.Compon
helpers.StartStop(t, c) helpers.StartStop(t, c)
return c, h, mockConn, mockClock return c, h, mockConn, mockClock
} }
func init() {
helpers.RegisterCmpOption(cmpopts.IgnoreUnexported(graphCommonHandlerInput{}))
helpers.RegisterCmpOption(cmp.AllowUnexported(graphLineHandlerInput{}))
}

View File

@@ -129,13 +129,13 @@ func TestGetNetFlowData(t *testing.T) {
SrcNetMask: 24, SrcNetMask: 24,
DstNetMask: 23, DstNetMask: 23,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 34974, schema.ColumnDstPort: uint16(34974),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
}, },
}, },
{ {
@@ -150,13 +150,13 @@ func TestGetNetFlowData(t *testing.T) {
SrcNetMask: 24, SrcNetMask: 24,
DstNetMask: 24, DstNetMask: 24,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1339, schema.ColumnBytes: uint64(1339),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 33199, schema.ColumnDstPort: uint16(33199),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
}, },
}, },
{ {
@@ -171,13 +171,13 @@ func TestGetNetFlowData(t *testing.T) {
SrcNetMask: 48, SrcNetMask: 48,
DstNetMask: 48, DstNetMask: 48,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1300, schema.ColumnBytes: uint64(1300),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 33179, schema.ColumnSrcPort: uint16(33179),
schema.ColumnDstPort: 443, schema.ColumnDstPort: uint16(443),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
}, },
}, },
} }

View File

@@ -70,11 +70,11 @@ func TestSNMPServer(t *testing.T) {
Type: gosnmp.OctetString, Type: gosnmp.OctetString,
}, { }, {
Name: ".1.3.6.1.2.1.31.1.1.1.15.1", Name: ".1.3.6.1.2.1.31.1.1.1.15.1",
Value: 10000, Value: uint(10000),
Type: gosnmp.Gauge32, Type: gosnmp.Gauge32,
}, { }, {
Name: ".1.3.6.1.2.1.31.1.1.1.15.2", Name: ".1.3.6.1.2.1.31.1.1.1.15.2",
Value: 10000, Value: uint(10000),
Type: gosnmp.Gauge32, Type: gosnmp.Gauge32,
}, { }, {
Name: ".1.3.6.1.2.1.31.1.1.1.18.1", Name: ".1.3.6.1.2.1.31.1.1.1.18.1",

3
go.mod
View File

@@ -22,12 +22,12 @@ require (
github.com/go-playground/validator/v10 v10.20.0 github.com/go-playground/validator/v10 v10.20.0
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
github.com/go-viper/mapstructure/v2 v2.4.0 github.com/go-viper/mapstructure/v2 v2.4.0
github.com/google/go-cmp v0.7.0
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/gosnmp/gosnmp v1.42.1 github.com/gosnmp/gosnmp v1.42.1
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/go-version v1.7.0
github.com/itchyny/gojq v0.12.17 github.com/itchyny/gojq v0.12.17
github.com/kylelemons/godebug v1.1.0
github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-isatty v0.0.20
github.com/netsampler/goflow2/v2 v2.2.3 github.com/netsampler/goflow2/v2 v2.2.3
github.com/openconfig/gnmi v0.14.0 github.com/openconfig/gnmi v0.14.0
@@ -111,7 +111,6 @@ require (
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/licensecheck v0.3.1 // indirect github.com/google/licensecheck v0.3.1 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect

View File

@@ -22,7 +22,7 @@ func TestFileInput(t *testing.T) {
configuration.Paths = []string{path.Join("testdata", "file1.txt"), path.Join("testdata", "file2.txt")} configuration.Paths = []string{path.Join("testdata", "file1.txt"), path.Join("testdata", "file2.txt")}
done := make(chan bool) done := make(chan bool)
expected := []pb.RawFlow{ expected := []*pb.RawFlow{
{ {
Payload: []byte("hello world!\n"), Payload: []byte("hello world!\n"),
SourceAddress: net.ParseIP("127.0.0.1").To16(), SourceAddress: net.ParseIP("127.0.0.1").To16(),

View File

@@ -4,8 +4,6 @@
package clickhouse package clickhouse
import ( import (
"fmt"
"reflect"
"testing" "testing"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -16,46 +14,53 @@ import (
func TestNetworkNamesUnmarshalHook(t *testing.T) { func TestNetworkNamesUnmarshalHook(t *testing.T) {
helpers.TestConfigurationDecode(t, helpers.ConfigurationDecodeCases{ helpers.TestConfigurationDecode(t, helpers.ConfigurationDecodeCases{
{ {
Pos: helpers.Mark(),
Description: "nil", Description: "nil",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return nil }, Configuration: func() any { return nil },
Expected: helpers.SubnetMap[NetworkAttributes]{}, Expected: &helpers.SubnetMap[NetworkAttributes]{},
}, { }, {
Pos: helpers.Mark(),
Description: "empty", Description: "empty",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{} }, Configuration: func() any { return gin.H{} },
Expected: helpers.SubnetMap[NetworkAttributes]{}, Expected: &helpers.SubnetMap[NetworkAttributes]{},
}, { }, {
Pos: helpers.Mark(),
Description: "IPv4", Description: "IPv4",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{"203.0.113.0/24": gin.H{"name": "customer"}} }, Configuration: func() any { return gin.H{"203.0.113.0/24": gin.H{"name": "customer"}} },
Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{ Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{
"::ffff:203.0.113.0/120": {Name: "customer"}, "::ffff:203.0.113.0/120": {Name: "customer"},
}), }),
}, { }, {
Pos: helpers.Mark(),
Description: "IPv6", Description: "IPv6",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{"2001:db8:1::/64": gin.H{"name": "customer"}} }, Configuration: func() any { return gin.H{"2001:db8:1::/64": gin.H{"name": "customer"}} },
Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{ Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{
"2001:db8:1::/64": {Name: "customer"}, "2001:db8:1::/64": {Name: "customer"},
}), }),
}, { }, {
Pos: helpers.Mark(),
Description: "IPv4 subnet (compatibility)", Description: "IPv4 subnet (compatibility)",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{"203.0.113.0/24": "customer"} }, Configuration: func() any { return gin.H{"203.0.113.0/24": "customer"} },
Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{ Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{
"::ffff:203.0.113.0/120": {Name: "customer"}, "::ffff:203.0.113.0/120": {Name: "customer"},
}), }),
}, { }, {
Pos: helpers.Mark(),
Description: "IPv6 subnet (compatibility)", Description: "IPv6 subnet (compatibility)",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{"2001:db8:1::/64": "customer"} }, Configuration: func() any { return gin.H{"2001:db8:1::/64": "customer"} },
Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{ Expected: helpers.MustNewSubnetMap(map[string]NetworkAttributes{
"2001:db8:1::/64": {Name: "customer"}, "2001:db8:1::/64": {Name: "customer"},
}), }),
}, { }, {
Pos: helpers.Mark(),
Description: "all attributes", Description: "all attributes",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { Configuration: func() any {
return gin.H{"203.0.113.0/24": gin.H{ return gin.H{"203.0.113.0/24": gin.H{
"name": "customer1", "name": "customer1",
@@ -73,17 +78,19 @@ func TestNetworkNamesUnmarshalHook(t *testing.T) {
Tenant: "mobile", Tenant: "mobile",
}}), }}),
}, { }, {
Pos: helpers.Mark(),
Description: "Invalid subnet (1)", Description: "Invalid subnet (1)",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{"192.0.2.1/38": "customer"} }, Configuration: func() any { return gin.H{"192.0.2.1/38": "customer"} },
Error: true, Error: true,
}, { }, {
Pos: helpers.Mark(),
Description: "Invalid subnet (2)", Description: "Invalid subnet (2)",
Initial: func() any { return helpers.SubnetMap[NetworkAttributes]{} }, Initial: func() any { return &helpers.SubnetMap[NetworkAttributes]{} },
Configuration: func() any { return gin.H{"192.0.2.1/255.0.255.0": "customer"} }, Configuration: func() any { return gin.H{"192.0.2.1/255.0.255.0": "customer"} },
Error: true, Error: true,
}, },
}, helpers.DiffFormatter(reflect.TypeOf(helpers.SubnetMap[NetworkAttributes]{}), fmt.Sprint)) })
} }
func TestDefaultConfiguration(t *testing.T) { func TestDefaultConfiguration(t *testing.T) {
@@ -92,3 +99,7 @@ func TestDefaultConfiguration(t *testing.T) {
t.Fatalf("validate.Struct() error:\n%+v", err) t.Fatalf("validate.Struct() error:\n%+v", err)
} }
} }
func init() {
helpers.RegisterSubnetMapCmp[NetworkAttributes]()
}

View File

@@ -23,9 +23,9 @@ type Configuration struct {
// ClassifierCacheDuration defines the default TTL for classifier cache // ClassifierCacheDuration defines the default TTL for classifier cache
ClassifierCacheDuration time.Duration `validate:"min=1s"` ClassifierCacheDuration time.Duration `validate:"min=1s"`
// DefaultSamplingRate defines the default sampling rate to use when the information is missing // DefaultSamplingRate defines the default sampling rate to use when the information is missing
DefaultSamplingRate helpers.SubnetMap[uint] DefaultSamplingRate *helpers.SubnetMap[uint]
// OverrideSamplingRate defines a sampling rate to use instead of the received on // OverrideSamplingRate defines a sampling rate to use instead of the received on
OverrideSamplingRate helpers.SubnetMap[uint] OverrideSamplingRate *helpers.SubnetMap[uint]
// ASNProviders defines the source used to get AS numbers // ASNProviders defines the source used to get AS numbers
ASNProviders []ASNProvider `validate:"dive"` ASNProviders []ASNProvider `validate:"dive"`
// NetProviders defines the source used to get Prefix/Network Information // NetProviders defines the source used to get Prefix/Network Information

View File

@@ -58,8 +58,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -89,8 +89,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -115,8 +115,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -145,8 +145,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -181,8 +181,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -214,8 +214,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -282,8 +282,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -314,8 +314,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Super Speed", schema.ColumnOutIfDescription: "Super Speed",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -349,8 +349,8 @@ func TestEnrich(t *testing.T) {
schema.ColumnOutIfName: "Gi0/0/200.300", schema.ColumnOutIfName: "Gi0/0/200.300",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
}, },
}, },
}, },
@@ -385,10 +385,10 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`,
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
schema.ColumnInIfBoundary: schema.InterfaceBoundaryInternal, schema.ColumnInIfBoundary: uint8(schema.InterfaceBoundaryInternal),
schema.ColumnOutIfBoundary: schema.InterfaceBoundaryInternal, schema.ColumnOutIfBoundary: uint8(schema.InterfaceBoundaryInternal),
}, },
}, },
}, },
@@ -419,10 +419,10 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`,
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
schema.ColumnInIfBoundary: 2, // Internal schema.ColumnInIfBoundary: uint8(2), // Internal
schema.ColumnOutIfBoundary: 2, schema.ColumnOutIfBoundary: uint8(2),
}, },
}, },
}, },
@@ -453,8 +453,8 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`,
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
schema.ColumnInIfProvider: "telia", schema.ColumnInIfProvider: "telia",
schema.ColumnOutIfProvider: "telia", schema.ColumnOutIfProvider: "telia",
}, },
@@ -488,14 +488,14 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`,
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
schema.ColumnInIfConnectivity: "p100", schema.ColumnInIfConnectivity: "p100",
schema.ColumnOutIfConnectivity: "core", schema.ColumnOutIfConnectivity: "core",
schema.ColumnInIfProvider: "othello", schema.ColumnInIfProvider: "othello",
schema.ColumnOutIfProvider: "othello", schema.ColumnOutIfProvider: "othello",
schema.ColumnInIfBoundary: 1, // external schema.ColumnInIfBoundary: uint8(1), // external
schema.ColumnOutIfBoundary: 2, // internal schema.ColumnOutIfBoundary: uint8(2), // internal
}, },
}, },
}, },
@@ -532,14 +532,14 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`,
schema.ColumnOutIfName: "Gi0/0/2010", schema.ColumnOutIfName: "Gi0/0/2010",
schema.ColumnInIfDescription: "Interface 1010", schema.ColumnInIfDescription: "Interface 1010",
schema.ColumnOutIfDescription: "Interface 2010", schema.ColumnOutIfDescription: "Interface 2010",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
schema.ColumnInIfConnectivity: "p1010", schema.ColumnInIfConnectivity: "p1010",
schema.ColumnOutIfConnectivity: "metadata connectivity", schema.ColumnOutIfConnectivity: "metadata connectivity",
schema.ColumnInIfProvider: "othello", schema.ColumnInIfProvider: "othello",
schema.ColumnOutIfProvider: "metadata provider", schema.ColumnOutIfProvider: "metadata provider",
schema.ColumnInIfBoundary: schema.InterfaceBoundaryExternal, schema.ColumnInIfBoundary: uint8(schema.InterfaceBoundaryExternal),
schema.ColumnOutIfBoundary: schema.InterfaceBoundaryExternal, schema.ColumnOutIfBoundary: uint8(schema.InterfaceBoundaryExternal),
}, },
}, },
}, },
@@ -573,8 +573,8 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`,
schema.ColumnOutIfName: "Gi0/0/200", schema.ColumnOutIfName: "Gi0/0/200",
schema.ColumnInIfDescription: "Interface 100", schema.ColumnInIfDescription: "Interface 100",
schema.ColumnOutIfDescription: "Interface 200", schema.ColumnOutIfDescription: "Interface 200",
schema.ColumnInIfSpeed: 1000, schema.ColumnInIfSpeed: uint32(1000),
schema.ColumnOutIfSpeed: 1000, schema.ColumnOutIfSpeed: uint32(1000),
schema.ColumnDstASPath: []uint32{64200, 1299, 174}, schema.ColumnDstASPath: []uint32{64200, 1299, 174},
schema.ColumnDstCommunities: []uint32{100, 200, 400}, schema.ColumnDstCommunities: []uint32{100, 200, 400},
schema.ColumnDstLargeCommunities: []schema.UInt128{ schema.ColumnDstLargeCommunities: []schema.UInt128{

View File

@@ -83,12 +83,12 @@ func TestCore(t *testing.T) {
SrcAddr: netip.MustParseAddr("::ffff:67.43.156.77"), SrcAddr: netip.MustParseAddr("::ffff:67.43.156.77"),
DstAddr: netip.MustParseAddr("::ffff:2.125.160.216"), DstAddr: netip.MustParseAddr("::ffff:2.125.160.216"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 6765, schema.ColumnBytes: uint64(6765),
schema.ColumnPackets: 4, schema.ColumnPackets: uint64(4),
schema.ColumnEType: 0x800, schema.ColumnEType: uint32(0x800),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 8534, schema.ColumnSrcPort: uint16(8534),
schema.ColumnDstPort: 80, schema.ColumnDstPort: uint16(80),
}, },
} }
return msg return msg
@@ -102,8 +102,8 @@ func TestCore(t *testing.T) {
expected.OtherColumns[schema.ColumnOutIfName] = fmt.Sprintf("Gi0/0/%d", out) expected.OtherColumns[schema.ColumnOutIfName] = fmt.Sprintf("Gi0/0/%d", out)
expected.OtherColumns[schema.ColumnInIfDescription] = fmt.Sprintf("Interface %d", in) expected.OtherColumns[schema.ColumnInIfDescription] = fmt.Sprintf("Interface %d", in)
expected.OtherColumns[schema.ColumnOutIfDescription] = fmt.Sprintf("Interface %d", out) expected.OtherColumns[schema.ColumnOutIfDescription] = fmt.Sprintf("Interface %d", out)
expected.OtherColumns[schema.ColumnInIfSpeed] = 1000 expected.OtherColumns[schema.ColumnInIfSpeed] = uint32(1000)
expected.OtherColumns[schema.ColumnOutIfSpeed] = 1000 expected.OtherColumns[schema.ColumnOutIfSpeed] = uint32(1000)
expected.OtherColumns[schema.ColumnExporterName] = strings.ReplaceAll(exporter, ".", "_") expected.OtherColumns[schema.ColumnExporterName] = strings.ReplaceAll(exporter, ".", "_")
return expected return expected
} }
@@ -259,34 +259,34 @@ func TestCore(t *testing.T) {
t.Fatalf("GET /api/v0/outlet/flows error while reading body:\n%+v", err) t.Fatalf("GET /api/v0/outlet/flows error while reading body:\n%+v", err)
} }
expected := gin.H{ expected := gin.H{
"TimeReceived": 200, "TimeReceived": float64(200),
"SamplingRate": 1000, "SamplingRate": float64(1000),
"ExporterAddress": "::ffff:192.0.2.142", "ExporterAddress": "::ffff:192.0.2.142",
"SrcAddr": "::ffff:67.43.156.77", "SrcAddr": "::ffff:67.43.156.77",
"DstAddr": "::ffff:2.125.160.216", "DstAddr": "::ffff:2.125.160.216",
"SrcAS": 0, // no geoip enrich anymore "SrcAS": float64(0), // no geoip enrich anymore
"InIf": 434, "InIf": float64(434),
"OutIf": 677, "OutIf": float64(677),
"NextHop": "", "NextHop": "",
"SrcNetMask": 0, "SrcNetMask": float64(0),
"DstNetMask": 0, "DstNetMask": float64(0),
"SrcVlan": 0, "SrcVlan": float64(0),
"DstVlan": 0, "DstVlan": float64(0),
"DstAS": 0, "DstAS": float64(0),
"OtherColumns": gin.H{ "OtherColumns": map[string]any{
"ExporterName": "192_0_2_142", "ExporterName": "192_0_2_142",
"InIfName": "Gi0/0/434", "InIfName": "Gi0/0/434",
"OutIfName": "Gi0/0/677", "OutIfName": "Gi0/0/677",
"InIfDescription": "Interface 434", "InIfDescription": "Interface 434",
"OutIfDescription": "Interface 677", "OutIfDescription": "Interface 677",
"InIfSpeed": 1000, "InIfSpeed": float64(1000),
"OutIfSpeed": 1000, "OutIfSpeed": float64(1000),
"Bytes": 6765, "Bytes": float64(6765),
"Packets": 4, "Packets": float64(4),
"SrcPort": 8534, "SrcPort": float64(8534),
"DstPort": 80, "DstPort": float64(80),
"EType": 2048, "EType": float64(2048),
"Proto": 6, "Proto": float64(6),
}, },
} }
if diff := helpers.Diff(got, expected); diff != "" { if diff := helpers.Diff(got, expected); diff != "" {

View File

@@ -43,10 +43,10 @@ func TestGobDecoder(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:192.0.2.200"), DstAddr: netip.MustParseAddr("::ffff:192.0.2.200"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: uint64(1024), schema.ColumnBytes: uint64(1024),
schema.ColumnPackets: 10, schema.ColumnPackets: uint64(10),
schema.ColumnInIfBoundary: schema.InterfaceBoundaryExternal, schema.ColumnInIfBoundary: uint8(schema.InterfaceBoundaryExternal),
schema.ColumnExporterName: "hello", schema.ColumnExporterName: "hello",
schema.ColumnDstNetMask: uint32(8), schema.ColumnDstNetMask: uint8(8),
schema.ColumnDstPort: uint16(80), schema.ColumnDstPort: uint16(80),
schema.ColumnDstASPath: []uint32{65000, 65001, 65002}, schema.ColumnDstASPath: []uint32{65000, 65001, 65002},
}, },

View File

@@ -20,21 +20,21 @@ func TestDecodeMPLSAndIPv4(t *testing.T) {
if l != 40 { if l != 40 {
t.Errorf("ParseEthernet() returned %d, expected 40", l) t.Errorf("ParseEthernet() returned %d, expected 40", l)
} }
expected := schema.FlowMessage{ expected := &schema.FlowMessage{
SrcAddr: netip.MustParseAddr("::ffff:10.31.0.1"), SrcAddr: netip.MustParseAddr("::ffff:10.31.0.1"),
DstAddr: netip.MustParseAddr("::ffff:10.34.0.1"), DstAddr: netip.MustParseAddr("::ffff:10.34.0.1"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 11001, schema.ColumnSrcPort: uint16(11001),
schema.ColumnDstPort: 23, schema.ColumnDstPort: uint16(23),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
schema.ColumnMPLSLabels: []uint64{18, 16}, schema.ColumnMPLSLabels: []uint32{18, 16},
schema.ColumnIPTTL: 255, schema.ColumnIPTTL: uint8(255),
schema.ColumnIPTos: 0xb0, schema.ColumnIPTos: uint8(0xb0),
schema.ColumnIPFragmentID: 8, schema.ColumnIPFragmentID: uint32(8),
schema.ColumnSrcMAC: 0x003096052838, schema.ColumnSrcMAC: uint64(0x003096052838),
schema.ColumnDstMAC: 0x003096e6fc39, schema.ColumnDstMAC: uint64(0x003096e6fc39),
}, },
} }
if diff := helpers.Diff(bf, expected); diff != "" { if diff := helpers.Diff(bf, expected); diff != "" {
@@ -50,16 +50,16 @@ func TestDecodeVLANAndIPv6(t *testing.T) {
if l != 179 { if l != 179 {
t.Errorf("ParseEthernet() returned %d, expected 179", l) t.Errorf("ParseEthernet() returned %d, expected 179", l)
} }
expected := schema.FlowMessage{ expected := &schema.FlowMessage{
SrcVlan: 100, SrcVlan: 100,
SrcAddr: netip.MustParseAddr("2402:f000:1:8e01::5555"), SrcAddr: netip.MustParseAddr("2402:f000:1:8e01::5555"),
DstAddr: netip.MustParseAddr("2607:fcd0:100:2300::b108:2a6b"), DstAddr: netip.MustParseAddr("2607:fcd0:100:2300::b108:2a6b"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 4, schema.ColumnProto: uint32(4),
schema.ColumnIPTTL: 246, schema.ColumnIPTTL: uint8(246),
schema.ColumnSrcMAC: 0x00121ef2613d, schema.ColumnSrcMAC: uint64(0x00121ef2613d),
schema.ColumnDstMAC: 0xc500000082c4, schema.ColumnDstMAC: uint64(0xc500000082c4),
}, },
} }
if diff := helpers.Diff(bf, expected); diff != "" { if diff := helpers.Diff(bf, expected); diff != "" {

View File

@@ -138,14 +138,14 @@ func TestDecode(t *testing.T) {
SrcNetMask: 24, SrcNetMask: 24,
DstNetMask: 14, DstNetMask: 14,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 19624, schema.ColumnDstPort: uint16(19624),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
}, },
}, { }, {
SamplingRate: 30000, SamplingRate: 30000,
@@ -158,14 +158,14 @@ func TestDecode(t *testing.T) {
SrcNetMask: 24, SrcNetMask: 24,
DstNetMask: 14, DstNetMask: 14,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 2444, schema.ColumnDstPort: uint16(2444),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
}, },
}, { }, {
SamplingRate: 30000, SamplingRate: 30000,
@@ -178,14 +178,14 @@ func TestDecode(t *testing.T) {
SrcNetMask: 20, SrcNetMask: 20,
DstNetMask: 18, DstNetMask: 18,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1400, schema.ColumnBytes: uint64(1400),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 53697, schema.ColumnDstPort: uint16(53697),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
}, },
}, { }, {
SamplingRate: 30000, SamplingRate: 30000,
@@ -198,19 +198,19 @@ func TestDecode(t *testing.T) {
SrcNetMask: 16, SrcNetMask: 16,
DstNetMask: 14, DstNetMask: 14,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1448, schema.ColumnBytes: uint64(1448),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 52300, schema.ColumnDstPort: uint16(52300),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
}, },
}, },
} }
if diff := helpers.Diff(got, expectedFlows); diff != "" { if diff := helpers.Diff(got, &expectedFlows); diff != "" {
t.Fatalf("Decode() (-got, +want):\n%s", diff) t.Fatalf("Decode() (-got, +want):\n%s", diff)
} }
gotMetrics = r.GetMetrics( gotMetrics = r.GetMetrics(
@@ -293,12 +293,12 @@ func TestDecodeSamplingRate(t *testing.T) {
SrcVlan: 701, SrcVlan: 701,
NextHop: netip.MustParseAddr("::ffff:0.0.0.0"), NextHop: netip.MustParseAddr("::ffff:0.0.0.0"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnBytes: 160, schema.ColumnBytes: uint64(160),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 13245, schema.ColumnSrcPort: uint16(13245),
schema.ColumnDstPort: 10907, schema.ColumnDstPort: uint16(10907),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
}, },
}, },
} }
@@ -353,17 +353,17 @@ func TestDecodeMultipleSamplingRates(t *testing.T) {
InIf: 97, InIf: 97,
OutIf: 6, OutIf: 6,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnPackets: 18, schema.ColumnPackets: uint64(18),
schema.ColumnBytes: 1348, schema.ColumnBytes: uint64(1348),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 52616, schema.ColumnDstPort: uint16(52616),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
schema.ColumnIPTTL: 127, schema.ColumnIPTTL: uint8(127),
schema.ColumnIPTos: 64, schema.ColumnIPTos: uint8(64),
schema.ColumnIPv6FlowLabel: 252813, schema.ColumnIPv6FlowLabel: uint32(252813),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
}, },
}, },
{ {
@@ -377,16 +377,16 @@ func TestDecodeMultipleSamplingRates(t *testing.T) {
InIf: 103, InIf: 103,
OutIf: 6, OutIf: 6,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnPackets: 4, schema.ColumnPackets: uint64(4),
schema.ColumnBytes: 579, schema.ColumnBytes: uint64(579),
schema.ColumnProto: 17, schema.ColumnProto: uint32(17),
schema.ColumnSrcPort: 2121, schema.ColumnSrcPort: uint16(2121),
schema.ColumnDstPort: 2121, schema.ColumnDstPort: uint16(2121),
schema.ColumnForwardingStatus: 64, schema.ColumnForwardingStatus: uint32(64),
schema.ColumnIPTTL: 57, schema.ColumnIPTTL: uint8(57),
schema.ColumnIPTos: 40, schema.ColumnIPTos: uint8(40),
schema.ColumnIPv6FlowLabel: 570164, schema.ColumnIPv6FlowLabel: uint32(570164),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
}, },
}, },
} }
@@ -421,12 +421,12 @@ func TestDecodeICMP(t *testing.T) {
SrcAddr: netip.MustParseAddr("2001:db8::"), SrcAddr: netip.MustParseAddr("2001:db8::"),
DstAddr: netip.MustParseAddr("2001:db8::1"), DstAddr: netip.MustParseAddr("2001:db8::1"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 104, schema.ColumnBytes: uint64(104),
schema.ColumnDstPort: 32768, schema.ColumnDstPort: uint16(32768),
schema.ColumnEType: 34525, schema.ColumnEType: uint32(34525),
schema.ColumnICMPv6Type: 128, // Code: 0 schema.ColumnICMPv6Type: uint8(128), // Code: 0
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnProto: 58, schema.ColumnProto: uint32(58),
}, },
}, },
{ {
@@ -434,12 +434,12 @@ func TestDecodeICMP(t *testing.T) {
SrcAddr: netip.MustParseAddr("2001:db8::1"), SrcAddr: netip.MustParseAddr("2001:db8::1"),
DstAddr: netip.MustParseAddr("2001:db8::"), DstAddr: netip.MustParseAddr("2001:db8::"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 104, schema.ColumnBytes: uint64(104),
schema.ColumnDstPort: 33024, schema.ColumnDstPort: uint16(33024),
schema.ColumnEType: 34525, schema.ColumnEType: uint32(34525),
schema.ColumnICMPv6Type: 129, // Code: 0 schema.ColumnICMPv6Type: uint8(129), // Code: 0
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnProto: 58, schema.ColumnProto: uint32(58),
}, },
}, },
{ {
@@ -447,12 +447,12 @@ func TestDecodeICMP(t *testing.T) {
SrcAddr: netip.MustParseAddr("::ffff:203.0.113.4"), SrcAddr: netip.MustParseAddr("::ffff:203.0.113.4"),
DstAddr: netip.MustParseAddr("::ffff:203.0.113.5"), DstAddr: netip.MustParseAddr("::ffff:203.0.113.5"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 84, schema.ColumnBytes: uint64(84),
schema.ColumnDstPort: 2048, schema.ColumnDstPort: uint16(2048),
schema.ColumnEType: 2048, schema.ColumnEType: uint32(2048),
schema.ColumnICMPv4Type: 8, // Code: 0 schema.ColumnICMPv4Type: uint8(8), // Code: 0
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnProto: 1, schema.ColumnProto: uint32(1),
}, },
}, },
{ {
@@ -460,16 +460,16 @@ func TestDecodeICMP(t *testing.T) {
SrcAddr: netip.MustParseAddr("::ffff:203.0.113.5"), SrcAddr: netip.MustParseAddr("::ffff:203.0.113.5"),
DstAddr: netip.MustParseAddr("::ffff:203.0.113.4"), DstAddr: netip.MustParseAddr("::ffff:203.0.113.4"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 84, schema.ColumnBytes: uint64(84),
schema.ColumnEType: 2048, schema.ColumnEType: uint32(2048),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnProto: 1, schema.ColumnProto: uint32(1),
// Type/Code = 0 // Type/Code = 0
}, },
}, },
} }
if diff := helpers.Diff(got, expectedFlows); diff != "" { if diff := helpers.Diff(got, &expectedFlows); diff != "" {
t.Fatalf("Decode() (-got, +want):\n%s", diff) t.Fatalf("Decode() (-got, +want):\n%s", diff)
} }
} }
@@ -502,21 +502,21 @@ func TestDecodeDataLink(t *testing.T) {
InIf: 582, InIf: 582,
OutIf: 0, OutIf: 0,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 96, schema.ColumnBytes: uint64(96),
schema.ColumnSrcPort: 55501, schema.ColumnSrcPort: uint16(55501),
schema.ColumnDstPort: 11777, schema.ColumnDstPort: uint16(11777),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnProto: 17, schema.ColumnProto: uint32(17),
schema.ColumnSrcMAC: 0xb402165592f4, schema.ColumnSrcMAC: uint64(0xb402165592f4),
schema.ColumnDstMAC: 0x182ad36e503f, schema.ColumnDstMAC: uint64(0x182ad36e503f),
schema.ColumnIPFragmentID: 0x8f00, schema.ColumnIPFragmentID: uint32(0x8f00),
schema.ColumnIPTTL: 119, schema.ColumnIPTTL: uint8(119),
}, },
}, },
} }
if diff := helpers.Diff(got, expectedFlows); diff != "" { if diff := helpers.Diff(got, &expectedFlows); diff != "" {
t.Fatalf("Decode() (-got, +want):\n%s", diff) t.Fatalf("Decode() (-got, +want):\n%s", diff)
} }
} }
@@ -534,7 +534,7 @@ func TestDecodeWithoutTemplate(t *testing.T) {
} }
expectedFlows := []*schema.FlowMessage{} expectedFlows := []*schema.FlowMessage{}
if diff := helpers.Diff(got, expectedFlows); diff != "" { if diff := helpers.Diff(got, &expectedFlows); diff != "" {
t.Fatalf("Decode() (-got, +want):\n%s", diff) t.Fatalf("Decode() (-got, +want):\n%s", diff)
} }
} }
@@ -560,14 +560,14 @@ func TestDecodeMPLS(t *testing.T) {
SamplingRate: 10, SamplingRate: 10,
OutIf: 16, OutIf: 16,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 89, schema.ColumnBytes: uint64(89),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnForwardingStatus: 66, schema.ColumnForwardingStatus: uint32(66),
schema.ColumnIPTTL: 255, schema.ColumnIPTTL: uint8(255),
schema.ColumnProto: 17, schema.ColumnProto: uint32(17),
schema.ColumnSrcPort: 49153, schema.ColumnSrcPort: uint16(49153),
schema.ColumnDstPort: 862, schema.ColumnDstPort: uint16(862),
schema.ColumnMPLSLabels: []uint32{20005, 524250}, schema.ColumnMPLSLabels: []uint32{20005, 524250},
}, },
}, { }, {
@@ -578,20 +578,20 @@ func TestDecodeMPLS(t *testing.T) {
SamplingRate: 10, SamplingRate: 10,
OutIf: 17, OutIf: 17,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 890, schema.ColumnBytes: uint64(890),
schema.ColumnPackets: 10, schema.ColumnPackets: uint64(10),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnForwardingStatus: 66, schema.ColumnForwardingStatus: uint32(66),
schema.ColumnIPTTL: 255, schema.ColumnIPTTL: uint8(255),
schema.ColumnProto: 17, schema.ColumnProto: uint32(17),
schema.ColumnSrcPort: 49153, schema.ColumnSrcPort: uint16(49153),
schema.ColumnDstPort: 862, schema.ColumnDstPort: uint16(862),
schema.ColumnMPLSLabels: []uint32{20006, 524275}, schema.ColumnMPLSLabels: []uint32{20006, 524275},
}, },
}, },
} }
if diff := helpers.Diff(got, expectedFlows); diff != "" { if diff := helpers.Diff(got, &expectedFlows); diff != "" {
t.Fatalf("Decode() (-got, +want):\n%s", diff) t.Fatalf("Decode() (-got, +want):\n%s", diff)
} }
} }
@@ -633,13 +633,13 @@ func TestDecodeNFv5(t *testing.T) {
SrcNetMask: 19, SrcNetMask: 19,
DstNetMask: 24, DstNetMask: 24,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 133, schema.ColumnBytes: uint64(133),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 30104, schema.ColumnSrcPort: uint16(30104),
schema.ColumnDstPort: 11963, schema.ColumnDstPort: uint16(11963),
schema.ColumnTCPFlags: 0x18, schema.ColumnTCPFlags: uint16(0x18),
}, },
}, },
} }
@@ -742,14 +742,14 @@ func TestDecodeNAT(t *testing.T) {
SrcAddr: netip.MustParseAddr("::ffff:172.16.100.198"), SrcAddr: netip.MustParseAddr("::ffff:172.16.100.198"),
DstAddr: netip.MustParseAddr("::ffff:10.89.87.1"), DstAddr: netip.MustParseAddr("::ffff:10.89.87.1"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnSrcPort: 35303, schema.ColumnSrcPort: uint16(35303),
schema.ColumnDstPort: 53, schema.ColumnDstPort: uint16(53),
schema.ColumnSrcAddrNAT: netip.MustParseAddr("::ffff:10.143.52.29"), schema.ColumnSrcAddrNAT: netip.MustParseAddr("::ffff:10.143.52.29"),
schema.ColumnDstAddrNAT: netip.MustParseAddr("::ffff:10.89.87.1"), schema.ColumnDstAddrNAT: netip.MustParseAddr("::ffff:10.89.87.1"),
schema.ColumnSrcPortNAT: 35303, schema.ColumnSrcPortNAT: uint16(35303),
schema.ColumnDstPortNAT: 53, schema.ColumnDstPortNAT: uint16(53),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 17, schema.ColumnProto: uint32(17),
}, },
}, },
} }
@@ -785,15 +785,15 @@ func TestDecodePhysicalInterfaces(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:212.82.101.24"), DstAddr: netip.MustParseAddr("::ffff:212.82.101.24"),
NextHop: netip.MustParseAddr("::"), NextHop: netip.MustParseAddr("::"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnSrcMAC: 0xc014fef6c365, schema.ColumnSrcMAC: uint64(0xc014fef6c365),
schema.ColumnDstMAC: 0xe8b6c24ae34c, schema.ColumnDstMAC: uint64(0xe8b6c24ae34c),
schema.ColumnPackets: 3, schema.ColumnPackets: uint64(3),
schema.ColumnBytes: 4506, schema.ColumnBytes: uint64(4506),
schema.ColumnSrcPort: 55629, schema.ColumnSrcPort: uint16(55629),
schema.ColumnDstPort: 993, schema.ColumnDstPort: uint16(993),
schema.ColumnTCPFlags: 0x10, schema.ColumnTCPFlags: uint16(0x10),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
}, },
}, },
} }

View File

@@ -51,18 +51,18 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("2a0c:8880:2:0:185:21:130:39"), DstAddr: netip.MustParseAddr("2a0c:8880:2:0:185:21:130:39"),
ExporterAddress: netip.MustParseAddr("::ffff:172.16.0.3"), ExporterAddress: netip.MustParseAddr("::ffff:172.16.0.3"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 46026, schema.ColumnSrcPort: uint16(46026),
schema.ColumnDstPort: 22, schema.ColumnDstPort: uint16(22),
schema.ColumnSrcMAC: 40057391053392, schema.ColumnSrcMAC: uint64(40057391053392),
schema.ColumnDstMAC: 40057381862408, schema.ColumnDstMAC: uint64(40057381862408),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 0x8, schema.ColumnIPTos: uint8(0x8),
schema.ColumnIPv6FlowLabel: 0x68094, schema.ColumnIPv6FlowLabel: uint32(0x68094),
schema.ColumnTCPFlags: 0x10, schema.ColumnTCPFlags: uint16(0x10),
}, },
}, { }, {
SamplingRate: 1024, SamplingRate: 1024,
@@ -78,17 +78,17 @@ func TestDecode(t *testing.T) {
SrcNetMask: 20, SrcNetMask: 20,
DstNetMask: 27, DstNetMask: 27,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 421, schema.ColumnBytes: uint64(421),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 443, schema.ColumnSrcPort: uint16(443),
schema.ColumnDstPort: 56876, schema.ColumnDstPort: uint16(56876),
schema.ColumnSrcMAC: 216372595274807, schema.ColumnSrcMAC: uint64(216372595274807),
schema.ColumnDstMAC: 191421060163210, schema.ColumnDstMAC: uint64(191421060163210),
schema.ColumnIPFragmentID: 0xa572, schema.ColumnIPFragmentID: uint32(0xa572),
schema.ColumnIPTTL: 59, schema.ColumnIPTTL: uint8(59),
schema.ColumnTCPFlags: 0x18, schema.ColumnTCPFlags: uint16(0x18),
}, },
}, { }, {
SamplingRate: 1024, SamplingRate: 1024,
@@ -100,18 +100,18 @@ func TestDecode(t *testing.T) {
SrcVlan: 100, SrcVlan: 100,
DstVlan: 100, DstVlan: 100,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 46026, schema.ColumnSrcPort: uint16(46026),
schema.ColumnDstPort: 22, schema.ColumnDstPort: uint16(22),
schema.ColumnSrcMAC: 40057391053392, schema.ColumnSrcMAC: uint64(40057391053392),
schema.ColumnDstMAC: 40057381862408, schema.ColumnDstMAC: uint64(40057381862408),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 0x8, schema.ColumnIPTos: uint8(0x8),
schema.ColumnIPv6FlowLabel: 0x68094, schema.ColumnIPv6FlowLabel: uint32(0x68094),
schema.ColumnTCPFlags: 0x10, schema.ColumnTCPFlags: uint16(0x10),
}, },
}, { }, {
SamplingRate: 1024, SamplingRate: 1024,
@@ -127,19 +127,19 @@ func TestDecode(t *testing.T) {
SrcNetMask: 27, SrcNetMask: 27,
DstNetMask: 17, DstNetMask: 17,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 40, schema.ColumnBytes: uint64(40),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 55658, schema.ColumnSrcPort: uint16(55658),
schema.ColumnDstPort: 5555, schema.ColumnDstPort: uint16(5555),
schema.ColumnSrcMAC: 138617863011056, schema.ColumnSrcMAC: uint64(138617863011056),
schema.ColumnDstMAC: 216372595274807, schema.ColumnDstMAC: uint64(216372595274807),
schema.ColumnDstASPath: []uint32{203698, 6762, 26615}, schema.ColumnDstASPath: []uint32{203698, 6762, 26615},
schema.ColumnDstCommunities: []uint64{2583495656, 2583495657, 4259880000, 4259880001, 4259900001}, schema.ColumnDstCommunities: []uint32{2583495656, 2583495657, 4259880000, 4259880001, 4259900001},
schema.ColumnIPFragmentID: 0xd431, schema.ColumnIPFragmentID: uint32(0xd431),
schema.ColumnIPTTL: 255, schema.ColumnIPTTL: uint8(255),
schema.ColumnTCPFlags: 0x2, schema.ColumnTCPFlags: uint16(0x2),
}, },
}, { }, {
SamplingRate: 1024, SamplingRate: 1024,
@@ -151,18 +151,18 @@ func TestDecode(t *testing.T) {
SrcVlan: 100, SrcVlan: 100,
DstVlan: 100, DstVlan: 100,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 46026, schema.ColumnSrcPort: uint16(46026),
schema.ColumnDstPort: 22, schema.ColumnDstPort: uint16(22),
schema.ColumnSrcMAC: 40057391053392, schema.ColumnSrcMAC: uint64(40057391053392),
schema.ColumnDstMAC: 40057381862408, schema.ColumnDstMAC: uint64(40057381862408),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 0x8, schema.ColumnIPTos: uint8(0x8),
schema.ColumnIPv6FlowLabel: 0x68094, schema.ColumnIPv6FlowLabel: uint32(0x68094),
schema.ColumnTCPFlags: 0x10, schema.ColumnTCPFlags: uint16(0x10),
}, },
}, },
} }
@@ -205,18 +205,18 @@ func TestDecode(t *testing.T) {
SrcVlan: 100, SrcVlan: 100,
DstVlan: 100, DstVlan: 100,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 46026, schema.ColumnSrcPort: uint16(46026),
schema.ColumnDstPort: 22, schema.ColumnDstPort: uint16(22),
schema.ColumnSrcMAC: 40057391053392, schema.ColumnSrcMAC: uint64(40057391053392),
schema.ColumnDstMAC: 40057381862408, schema.ColumnDstMAC: uint64(40057381862408),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
schema.ColumnIPv6FlowLabel: 426132, schema.ColumnIPv6FlowLabel: uint32(426132),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 8, schema.ColumnIPTos: uint8(8),
}, },
}, },
} }
@@ -246,19 +246,19 @@ func TestDecode(t *testing.T) {
SrcVlan: 100, SrcVlan: 100,
DstVlan: 100, DstVlan: 100,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 46026, schema.ColumnSrcPort: uint16(46026),
schema.ColumnDstPort: 22, schema.ColumnDstPort: uint16(22),
schema.ColumnForwardingStatus: 128, schema.ColumnForwardingStatus: uint32(128),
schema.ColumnSrcMAC: 40057391053392, schema.ColumnSrcMAC: uint64(40057391053392),
schema.ColumnDstMAC: 40057381862408, schema.ColumnDstMAC: uint64(40057381862408),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
schema.ColumnIPv6FlowLabel: 426132, schema.ColumnIPv6FlowLabel: uint32(426132),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 8, schema.ColumnIPTos: uint8(8),
}, },
}, },
} }
@@ -288,18 +288,18 @@ func TestDecode(t *testing.T) {
SrcVlan: 100, SrcVlan: 100,
DstVlan: 100, DstVlan: 100,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1500, schema.ColumnBytes: uint64(1500),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 46026, schema.ColumnSrcPort: uint16(46026),
schema.ColumnDstPort: 22, schema.ColumnDstPort: uint16(22),
schema.ColumnSrcMAC: 40057391053392, schema.ColumnSrcMAC: uint64(40057391053392),
schema.ColumnDstMAC: 40057381862408, schema.ColumnDstMAC: uint64(40057381862408),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
schema.ColumnIPv6FlowLabel: 426132, schema.ColumnIPv6FlowLabel: uint32(426132),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 8, schema.ColumnIPTos: uint8(8),
}, },
}, },
} }
@@ -333,20 +333,20 @@ func TestDecode(t *testing.T) {
SrcNetMask: 32, SrcNetMask: 32,
DstNetMask: 22, DstNetMask: 22,
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 104, schema.ColumnBytes: uint64(104),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcPort: 22, schema.ColumnSrcPort: uint16(22),
schema.ColumnDstPort: 52237, schema.ColumnDstPort: uint16(52237),
schema.ColumnDstASPath: []uint32{8218, 29605, 203361}, schema.ColumnDstASPath: []uint32{8218, 29605, 203361},
schema.ColumnDstCommunities: []uint64{538574949, 1911619684, 1911669584, 1911671290}, schema.ColumnDstCommunities: []uint32{538574949, 1911619684, 1911669584, 1911671290},
schema.ColumnTCPFlags: 0x18, schema.ColumnTCPFlags: uint16(0x18),
schema.ColumnIPFragmentID: 0xab4e, schema.ColumnIPFragmentID: uint32(0xab4e),
schema.ColumnIPTTL: 61, schema.ColumnIPTTL: uint8(61),
schema.ColumnIPTos: 0x8, schema.ColumnIPTos: uint8(0x8),
schema.ColumnSrcMAC: 0x948ed30a713b, schema.ColumnSrcMAC: uint64(0x948ed30a713b),
schema.ColumnDstMAC: 0x22421f4a9fcd, schema.ColumnDstMAC: uint64(0x22421f4a9fcd),
}, },
}, },
} }
@@ -375,16 +375,16 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:51.51.51.51"), DstAddr: netip.MustParseAddr("::ffff:51.51.51.51"),
ExporterAddress: netip.MustParseAddr("::ffff:49.49.49.49"), ExporterAddress: netip.MustParseAddr("::ffff:49.49.49.49"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 1344, schema.ColumnBytes: uint64(1344),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 17, schema.ColumnProto: uint32(17),
schema.ColumnSrcPort: 46622, schema.ColumnSrcPort: uint16(46622),
schema.ColumnDstPort: 58631, schema.ColumnDstPort: uint16(58631),
schema.ColumnSrcMAC: 1094287164743, schema.ColumnSrcMAC: uint64(1094287164743),
schema.ColumnDstMAC: 1101091482116, schema.ColumnDstMAC: uint64(1101091482116),
schema.ColumnIPFragmentID: 41647, schema.ColumnIPFragmentID: uint32(41647),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
}, },
}, },
} }
@@ -412,13 +412,13 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:92.222.186.1"), DstAddr: netip.MustParseAddr("::ffff:92.222.186.1"),
ExporterAddress: netip.MustParseAddr("::ffff:172.19.64.116"), ExporterAddress: netip.MustParseAddr("::ffff:172.19.64.116"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 32, schema.ColumnBytes: uint64(32),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 1, schema.ColumnProto: uint32(1),
schema.ColumnIPFragmentID: 4329, schema.ColumnIPFragmentID: uint32(4329),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 8, schema.ColumnIPTos: uint8(8),
}, },
}, { }, {
SamplingRate: 1, SamplingRate: 1,
@@ -428,13 +428,13 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:92.222.184.1"), DstAddr: netip.MustParseAddr("::ffff:92.222.184.1"),
ExporterAddress: netip.MustParseAddr("::ffff:172.19.64.116"), ExporterAddress: netip.MustParseAddr("::ffff:172.19.64.116"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 32, schema.ColumnBytes: uint64(32),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 1, schema.ColumnProto: uint32(1),
schema.ColumnIPFragmentID: 62945, schema.ColumnIPFragmentID: uint32(62945),
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPTos: 8, schema.ColumnIPTos: uint8(8),
}, },
}, },
} }
@@ -460,16 +460,16 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:203.0.113.5"), DstAddr: netip.MustParseAddr("::ffff:203.0.113.5"),
ExporterAddress: netip.MustParseAddr("::ffff:127.0.0.1"), ExporterAddress: netip.MustParseAddr("::ffff:127.0.0.1"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 84, schema.ColumnBytes: uint64(84),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 1, schema.ColumnProto: uint32(1),
schema.ColumnDstMAC: 0xd25b45ee5ecf, schema.ColumnDstMAC: uint64(0xd25b45ee5ecf),
schema.ColumnSrcMAC: 0xe2efc68f8cd4, schema.ColumnSrcMAC: uint64(0xe2efc68f8cd4),
schema.ColumnICMPv4Type: 8, schema.ColumnICMPv4Type: uint8(8),
// schema.ColumnICMPv4Code: 0, // schema.ColumnICMPv4Code: 0,
schema.ColumnIPTTL: 64, schema.ColumnIPTTL: uint8(64),
schema.ColumnIPFragmentID: 0x90c5, schema.ColumnIPFragmentID: uint32(0x90c5),
}, },
}, },
} }
@@ -495,14 +495,14 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("2001:db8::"), DstAddr: netip.MustParseAddr("2001:db8::"),
ExporterAddress: netip.MustParseAddr("::ffff:127.0.0.1"), ExporterAddress: netip.MustParseAddr("::ffff:127.0.0.1"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 72, schema.ColumnBytes: uint64(72),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv6, schema.ColumnEType: uint32(helpers.ETypeIPv6),
schema.ColumnProto: 58, schema.ColumnProto: uint32(58),
schema.ColumnSrcMAC: 0xd25b45ee5ecf, schema.ColumnSrcMAC: uint64(0xd25b45ee5ecf),
schema.ColumnDstMAC: 0xe2efc68f8cd4, schema.ColumnDstMAC: uint64(0xe2efc68f8cd4),
schema.ColumnIPTTL: 255, schema.ColumnIPTTL: uint8(255),
schema.ColumnICMPv6Type: 135, schema.ColumnICMPv6Type: uint8(135),
// schema.ColumnICMPv6Code: 0, // schema.ColumnICMPv6Code: 0,
}, },
}, },
@@ -532,17 +532,17 @@ func TestDecode(t *testing.T) {
DstAddr: netip.MustParseAddr("::ffff:49.49.49.109"), DstAddr: netip.MustParseAddr("::ffff:49.49.49.109"),
ExporterAddress: netip.MustParseAddr("::ffff:172.17.128.58"), ExporterAddress: netip.MustParseAddr("::ffff:172.17.128.58"),
OtherColumns: map[schema.ColumnKey]any{ OtherColumns: map[schema.ColumnKey]any{
schema.ColumnBytes: 80, schema.ColumnBytes: uint64(80),
schema.ColumnPackets: 1, schema.ColumnPackets: uint64(1),
schema.ColumnEType: helpers.ETypeIPv4, schema.ColumnEType: uint32(helpers.ETypeIPv4),
schema.ColumnProto: 6, schema.ColumnProto: uint32(6),
schema.ColumnSrcMAC: 0x4caea3520ff6, schema.ColumnSrcMAC: uint64(0x4caea3520ff6),
schema.ColumnDstMAC: 0x000110621493, schema.ColumnDstMAC: uint64(0x000110621493),
schema.ColumnIPTTL: 62, schema.ColumnIPTTL: uint8(62),
schema.ColumnIPFragmentID: 56159, schema.ColumnIPFragmentID: uint32(56159),
schema.ColumnTCPFlags: 16, schema.ColumnTCPFlags: uint16(16),
schema.ColumnSrcPort: 32017, schema.ColumnSrcPort: uint16(32017),
schema.ColumnDstPort: 443, schema.ColumnDstPort: uint16(443),
}, },
}, },
} }

View File

@@ -289,31 +289,31 @@ func TestNeedUpdates(t *testing.T) {
cases := []struct { cases := []struct {
Minutes time.Duration Minutes time.Duration
Expected map[string][]uint Expected map[netip.Addr][]uint
}{ }{
{9, map[string][]uint{ {9, map[netip.Addr][]uint{
"::ffff:127.0.0.1": { netip.MustParseAddr("::ffff:127.0.0.1"): {
676, 676,
678, 678,
}, },
"::ffff:127.0.0.2": { netip.MustParseAddr("::ffff:127.0.0.2"): {
678, 678,
}, },
}}, }},
{19, map[string][]uint{ {19, map[netip.Addr][]uint{
"::ffff:127.0.0.1": { netip.MustParseAddr("::ffff:127.0.0.1"): {
678, 678,
}, },
"::ffff:127.0.0.2": { netip.MustParseAddr("::ffff:127.0.0.2"): {
678, 678,
}, },
}}, }},
{29, map[string][]uint{ {29, map[netip.Addr][]uint{
"::ffff:127.0.0.1": { netip.MustParseAddr("::ffff:127.0.0.1"): {
678, 678,
}, },
}}, }},
{39, map[string][]uint{}}, {39, map[netip.Addr][]uint{}},
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(fmt.Sprintf("%d minutes", tc.Minutes), func(t *testing.T) { t.Run(fmt.Sprintf("%d minutes", tc.Minutes), func(t *testing.T) {

View File

@@ -8,6 +8,8 @@ import (
"akvorado/common/helpers" "akvorado/common/helpers"
"akvorado/outlet/metadata/provider" "akvorado/outlet/metadata/provider"
"github.com/google/go-cmp/cmp/cmpopts"
) )
func TestStateUpdate(t *testing.T) { func TestStateUpdate(t *testing.T) {
@@ -28,7 +30,7 @@ func TestStateUpdate(t *testing.T) {
Name: "", Name: "",
Interfaces: map[uint]provider.Interface{}, Interfaces: map[uint]provider.Interface{},
} }
if diff := helpers.Diff(state, expected); diff != "" { if diff := helpers.Diff(state, expected, cmpopts.IgnoreUnexported(state)); diff != "" {
t.Fatalf("udpate() (-got, +want):\n%s", diff) t.Fatalf("udpate() (-got, +want):\n%s", diff)
} }
@@ -90,7 +92,7 @@ func TestStateUpdate(t *testing.T) {
}, },
}, },
} }
if diff := helpers.Diff(state, expected); diff != "" { if diff := helpers.Diff(state, expected, cmpopts.IgnoreUnexported(state)); diff != "" {
t.Fatalf("udpate() (-got, +want):\n%s", diff) t.Fatalf("udpate() (-got, +want):\n%s", diff)
} }
} }

View File

@@ -6,12 +6,9 @@
package snmp package snmp
import ( import (
"fmt"
"reflect"
"akvorado/common/helpers" "akvorado/common/helpers"
) )
func init() { func init() {
helpers.AddPrettyFormatter(reflect.TypeOf(helpers.SubnetMap[Credentials]{}), fmt.Sprint) helpers.RegisterSubnetMapCmp[Credentials]()
} }

View File

@@ -6,12 +6,9 @@
package static package static
import ( import (
"fmt"
"reflect"
"akvorado/common/helpers" "akvorado/common/helpers"
) )
func init() { func init() {
helpers.AddPrettyFormatter(reflect.TypeOf(helpers.SubnetMap[ExporterConfiguration]{}), fmt.Sprint) helpers.RegisterSubnetMapCmp[ExporterConfiguration]()
} }

View File

@@ -206,7 +206,7 @@ func TestRemoveRoutes(t *testing.T) {
if count != 1 { if count != 1 {
t.Error("removeRoutes() should have removed 1 route") t.Error("removeRoutes() should have removed 1 route")
} }
if diff := helpers.Diff(r.routes, map[prefixIndex]route{}); diff != "" { if diff := helpers.Diff(r.routes, map[routeKey]route{}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff) t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
} }
}) })

View File

@@ -6,10 +6,8 @@
package bmp package bmp
import ( import (
"fmt"
"net" "net"
"net/netip" "net/netip"
"reflect"
"testing" "testing"
"akvorado/common/daemon" "akvorado/common/daemon"
@@ -18,6 +16,7 @@ import (
"akvorado/outlet/routing/provider" "akvorado/outlet/routing/provider"
"github.com/benbjohnson/clock" "github.com/benbjohnson/clock"
"github.com/google/go-cmp/cmp"
"github.com/osrg/gobgp/v3/pkg/packet/bgp" "github.com/osrg/gobgp/v3/pkg/packet/bgp"
"github.com/osrg/gobgp/v3/pkg/packet/bmp" "github.com/osrg/gobgp/v3/pkg/packet/bmp"
) )
@@ -149,5 +148,5 @@ func MustParseRD(input string) RD {
} }
func init() { func init() {
helpers.AddPrettyFormatter(reflect.TypeOf(route{}), fmt.Sprint) helpers.RegisterCmpOption(cmp.AllowUnexported(route{}))
} }