//go:build linux
// +build linux
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nfacct
import (
"syscall"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// fakeHandler is a mock implementation of the handler interface, designed for testing.
type fakeHandler struct {
// requests stores instances of fakeRequest, capturing new requests.
requests []*fakeRequest
// responses holds responses for the subsequent fakeRequest.Execute calls.
responses [][][]byte
// errs holds errors for the subsequent fakeRequest.Execute calls.
errs []error
}
// newRequest creates a request object with the given cmd, flags, predefined response and error.
// It additionally records the created request object.
func (fh *fakeHandler) newRequest(cmd int, flags uint16) request {
var response [][]byte
if fh.responses != nil && len(fh.responses) > 0 {
response = fh.responses[0]
// remove the response from the list of predefined responses and add it to request object for mocking.
fh.responses = fh.responses[1:]
}
var err error
if fh.errs != nil && len(fh.errs) > 0 {
err = fh.errs[0]
// remove the error from the list of predefined errors and add it to request object for mocking.
fh.errs = fh.errs[1:]
}
req := &fakeRequest{cmd: cmd, flags: flags, response: response, err: err}
fh.requests = append(fh.requests, req)
return req
}
// fakeRequest records information about the cmd and flags used when creating a new request,
// maintains a list for netlink attributes, and stores a predefined response and an optional
// error for subsequent execution.
type fakeRequest struct {
// cmd and flags which were used to create the request.
cmd int
flags uint16
// data holds netlink attributes.
data []nl.NetlinkRequestData
// response and err are the predefined output of execution.
response [][]byte
err error
}
// Serialize is part of request interface.
func (fr *fakeRequest) Serialize() []byte { return nil }
// AddData is part of request interface.
func (fr *fakeRequest) AddData(data nl.NetlinkRequestData) {
fr.data = append(fr.data, data)
}
// AddRawData is part of request interface.
func (fr *fakeRequest) AddRawData(_ []byte) {}
// Execute is part of request interface.
func (fr *fakeRequest) Execute(_ int, _ uint16) ([][]byte, error) {
return fr.response, fr.err
}
func TestRunner_Add(t *testing.T) {
testCases := []struct {
name string
counterName string
handler *fakeHandler
err error
netlinkCalls int
}{
{
name: "valid",
counterName: "metric-1",
handler: &fakeHandler{},
// expected calls: NFNL_MSG_ACCT_NEW
netlinkCalls: 1,
},
{
name: "add duplicate counter",
counterName: "metric-2",
handler: &fakeHandler{
errs: []error{syscall.EBUSY},
},
err: ErrObjectAlreadyExists,
// expected calls: NFNL_MSG_ACCT_NEW
netlinkCalls: 1,
},
{
name: "insufficient privilege",
counterName: "metric-2",
handler: &fakeHandler{
errs: []error{syscall.EPERM},
},
err: ErrUnexpected,
// expected calls: NFNL_MSG_ACCT_NEW
netlinkCalls: 1,
},
{
name: "exceeds max length",
counterName: "this-is-a-string-with-more-than-32-characters",
handler: &fakeHandler{},
err: ErrNameExceedsMaxLength,
// expected calls: zero (the error should be returned by this library)
netlinkCalls: 0,
},
{
name: "falls below min length",
counterName: "",
handler: &fakeHandler{},
err: ErrEmptyName,
// expected calls: zero (the error should be returned by this library)
netlinkCalls: 0,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rnr, err := newInternal(tc.handler)
assert.NoError(t, err)
err = rnr.Add(tc.counterName)
if tc.err != nil {
assert.ErrorContains(t, err, tc.err.Error())
} else {
assert.NoError(t, err)
}
// validate number of requests
assert.Len(t, tc.handler.requests, tc.netlinkCalls)
if tc.netlinkCalls > 0 {
// validate request
assert.Equal(t, cmdNew, tc.handler.requests[0].cmd)
assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_CREATE|unix.NLM_F_ACK), tc.handler.requests[0].flags)
// validate attribute(NFACCT_NAME)
assert.Len(t, tc.handler.requests[0].data, 1)
assert.Equal(t,
tc.handler.requests[0].data[0].Serialize(),
nl.NewRtAttr(attrName, nl.ZeroTerminated(tc.counterName)).Serialize(),
)
}
})
}
}
func TestRunner_Get(t *testing.T) {
testCases := []struct {
name string
counterName string
counter *Counter
handler *fakeHandler
netlinkCalls int
err error
}{
{
name: "valid with padding",
counterName: "metric-1",
counter: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
},
{
name: "valid without padding",
counterName: "metrics",
counter: &Counter{Name: "metrics", Packets: 12, Bytes: 503},
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x00,
0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
},
{
name: "missing netfilter generic header",
counterName: "metrics",
counter: nil,
handler: &fakeHandler{
responses: [][][]byte{{{
0x01, 0x00, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
0x73, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xf7, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x01,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrUnexpected,
},
{
name: "incorrect padding",
counterName: "metric-1",
counter: nil,
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x0f, 0xca, 0xf6, 0x63, 0x0c, 0x00, 0x03,
0x00, 0x00, 0x09, 0x0e, 0x06, 0xf6, 0xda, 0xcd,
0xd1, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x01,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrUnexpected,
},
{
name: "missing bytes attribute",
counterName: "metric-1",
counter: nil,
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrUnexpected,
},
{
name: "missing packets attribute",
counterName: "metric-1",
counter: nil,
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x09, 0x0e, 0x06, 0xf6, 0xda, 0xcd, 0xd1,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrUnexpected,
},
{
name: "only name attribute",
counterName: "metric-1",
counter: nil,
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00,
}}},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrUnexpected,
},
{
name: "get non-existent counter",
counterName: "metric-2",
handler: &fakeHandler{
errs: []error{syscall.ENOENT},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrObjectNotFound,
},
{
name: "unexpected error",
counterName: "metric-2",
handler: &fakeHandler{
errs: []error{syscall.EMFILE},
},
// expected calls: NFNL_MSG_ACCT_GET
netlinkCalls: 1,
err: ErrUnexpected,
},
{
name: "exceeds max length",
counterName: "this-is-a-string-with-more-than-32-characters",
handler: &fakeHandler{},
// expected calls: zero (the error should be returned by this library)
netlinkCalls: 0,
err: ErrNameExceedsMaxLength,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rnr, err := newInternal(tc.handler)
assert.NoError(t, err)
counter, err := rnr.Get(tc.counterName)
// validate number of requests
assert.Len(t, tc.handler.requests, tc.netlinkCalls)
if tc.netlinkCalls > 0 {
// validate request
assert.Equal(t, cmdGet, tc.handler.requests[0].cmd)
assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_ACK), tc.handler.requests[0].flags)
// validate attribute(NFACCT_NAME)
assert.Len(t, tc.handler.requests[0].data, 1)
assert.Equal(t,
tc.handler.requests[0].data[0].Serialize(),
nl.NewRtAttr(attrName, nl.ZeroTerminated(tc.counterName)).Serialize())
// validate response
if tc.err != nil {
assert.Nil(t, counter)
assert.ErrorContains(t, err, tc.err.Error())
} else {
assert.NotNil(t, counter)
assert.NoError(t, err)
assert.Equal(t, tc.counter.Name, counter.Name)
assert.Equal(t, tc.counter.Packets, counter.Packets)
assert.Equal(t, tc.counter.Bytes, counter.Bytes)
}
}
})
}
}
func TestRunner_Ensure(t *testing.T) {
testCases := []struct {
name string
counterName string
netlinkCalls int
handler *fakeHandler
}{
{
name: "counter doesnt exist",
counterName: "ct_established_accepted_packets",
handler: &fakeHandler{
errs: []error{syscall.ENOENT},
},
// expected calls - NFNL_MSG_ACCT_GET + NFNL_MSG_ACCT_NEW
netlinkCalls: 2,
},
{
name: "counter already exists",
counterName: "ct_invalid_dropped_packets",
handler: &fakeHandler{
responses: [][][]byte{{{
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x02, 0x68, 0xf3, 0x16, 0x58, 0x0e, 0x63,
0x0c, 0x00, 0x03, 0x00, 0x12, 0xc5, 0x37, 0xdf,
0xe5, 0xa1, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
}}},
},
// expected calls - NFNL_MSG_ACCT_GET
netlinkCalls: 1,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rnr, err := newInternal(tc.handler)
assert.NoError(t, err)
err = rnr.Ensure(tc.counterName)
assert.NoError(t, err)
// validate number of netlink requests
assert.Len(t, tc.handler.requests, tc.netlinkCalls)
})
}
}
func TestRunner_List(t *testing.T) {
hndlr := &fakeHandler{
responses: [][][]byte{{
{
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x74,
0x65, 0x73, 0x74, 0x2d, 0x6d, 0x65, 0x74, 0x72,
0x69, 0x63, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x60, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
},
{
0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00,
0x6e, 0x66, 0x61, 0x63, 0x63, 0x74, 0x2d, 0x6c,
0x69, 0x73, 0x74, 0x2d, 0x74, 0x65, 0x73, 0x74,
0x2d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x00,
0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x0b, 0x96, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xe6, 0xc5, 0x74,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
},
{
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x86, 0x8d,
0x44, 0xeb, 0xc7, 0x02, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x6e, 0x5f, 0xe2, 0x89, 0x69, 0x3f, 0x9e,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
},
{
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x01, 0x1e, 0x6e, 0xac, 0x20, 0xe9,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x6d,
0x30, 0x11, 0x8a, 0xec, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
},
}},
}
expected := []*Counter{
{Name: "random-test-metric", Packets: 134, Bytes: 2144},
{Name: "nfacct-list-test-metric", Packets: 134038, Bytes: 31901044},
{Name: "test", Packets: 147941304813314, Bytes: 31067674010795934},
{Name: "ct_invalid_dropped_packets", Packets: 1230217421033, Bytes: 14762609052396},
}
rnr, err := newInternal(hndlr)
assert.NoError(t, err)
counters, err := rnr.List()
// validate request(NFNL_MSG_ACCT_GET)
assert.Len(t, hndlr.requests, 1)
assert.Equal(t, cmdGet, hndlr.requests[0].cmd)
assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_DUMP), hndlr.requests[0].flags)
// validate attributes
assert.Empty(t, hndlr.requests[0].data)
// validate response
assert.NoError(t, err)
assert.NotNil(t, counters)
assert.Equal(t, len(expected), len(counters))
for i := 0; i < len(expected); i++ {
assert.Equal(t, expected[i].Name, counters[i].Name)
assert.Equal(t, expected[i].Packets, counters[i].Packets)
assert.Equal(t, expected[i].Bytes, counters[i].Bytes)
}
}
func TestDecode(t *testing.T) {
testCases := []struct {
name string
msg []byte
expected *Counter
}{
{
name: "valid",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
},
expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
},
{
name: "attribute name missing",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x96,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xe6, 0xc5, 0x74, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
},
expected: &Counter{Packets: 134038, Bytes: 31901044},
},
{
name: "attribute packets missing",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
},
expected: &Counter{Name: "ct_invalid_dropped_packets", Bytes: 2144},
},
{
name: "attribute bytes missing",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x74,
0x65, 0x73, 0x74, 0x2d, 0x6d, 0x65, 0x74, 0x72,
0x69, 0x63, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7,
},
expected: &Counter{Name: "random-test-metric", Packets: 503},
},
{
name: "attribute packets and bytes missing",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00,
},
expected: &Counter{Name: "test"},
},
{
name: "only netfilter generic header present",
msg: []byte{
0x00, 0x00, 0x00, 0x00,
},
expected: &Counter{},
},
{
name: "only packets attribute",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x96,
},
expected: &Counter{Packets: 134038},
},
{
name: "only bytes attribute",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
},
expected: &Counter{Bytes: 12},
},
{
name: "new attribute in the beginning",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01,
},
expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
},
{
name: "new attribute in the end",
msg: []byte{
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x01, 0x00,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01,
0x02, 0x03, 0x0e, 0x3f, 0xf6, 0xda, 0xcd, 0xd1,
},
expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
counter, err := decode(tc.msg, false)
assert.NoError(t, err)
assert.NotNil(t, counter)
assert.Equal(t, tc.expected.Name, counter.Name)
assert.Equal(t, tc.expected.Packets, counter.Packets)
assert.Equal(t, tc.expected.Bytes, counter.Bytes)
})
}
}