mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
348 lines
9.8 KiB
Go
348 lines
9.8 KiB
Go
// SPDX-FileCopyrightText: 2022 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package console
|
|
|
|
import (
|
|
"net"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"akvorado/common/clickhousedb/mocks"
|
|
"akvorado/common/helpers"
|
|
)
|
|
|
|
func TestWidgetLastFlow(t *testing.T) {
|
|
_, h, mockConn, _ := NewMock(t, DefaultConfiguration())
|
|
|
|
ctrl := gomock.NewController(t)
|
|
mockRows := mocks.NewMockRows(ctrl)
|
|
mockConn.EXPECT().Query(gomock.Any(), `
|
|
SELECT * EXCEPT (DstCommunities, DstLargeCommunities),
|
|
arrayMap(c -> concat(toString(bitShiftRight(c, 16)), ':',
|
|
toString(bitAnd(c, 0xffff))), DstCommunities) AS DstCommunities,
|
|
arrayMap(c -> concat(toString(bitAnd(bitShiftRight(c, 64), 0xffffffff)), ':',
|
|
toString(bitAnd(bitShiftRight(c, 32), 0xffffffff)), ':',
|
|
toString(bitAnd(c, 0xffffffff))), DstLargeCommunities) AS DstLargeCommunities
|
|
FROM flows
|
|
WHERE TimeReceived=(SELECT MAX(TimeReceived) FROM flows)
|
|
LIMIT 1`).
|
|
Return(mockRows, nil)
|
|
mockRows.EXPECT().Next().Return(true)
|
|
mockRows.EXPECT().Close()
|
|
mockRows.EXPECT().Columns().Return([]string{
|
|
"TimeReceived", "SamplingRate",
|
|
"SrcAddr", "SrcCountry",
|
|
"InIfName", "InIfBoundary", "InIfSpeed",
|
|
}).AnyTimes()
|
|
|
|
colTimeReceived := mocks.NewMockColumnType(ctrl)
|
|
colSamplingRate := mocks.NewMockColumnType(ctrl)
|
|
colSrcAddr := mocks.NewMockColumnType(ctrl)
|
|
colSrcCountry := mocks.NewMockColumnType(ctrl)
|
|
colInIfName := mocks.NewMockColumnType(ctrl)
|
|
colInIfBoundary := mocks.NewMockColumnType(ctrl)
|
|
colInIfSpeed := mocks.NewMockColumnType(ctrl)
|
|
colTimeReceived.EXPECT().ScanType().Return(reflect.TypeOf(time.Time{}))
|
|
colSamplingRate.EXPECT().ScanType().Return(reflect.TypeOf(uint64(0)))
|
|
colSrcAddr.EXPECT().ScanType().Return(reflect.TypeOf(net.IP{}))
|
|
colSrcCountry.EXPECT().ScanType().Return(reflect.TypeOf(""))
|
|
colInIfName.EXPECT().ScanType().Return(reflect.TypeOf(""))
|
|
colInIfBoundary.EXPECT().ScanType().Return(reflect.TypeOf(""))
|
|
colInIfSpeed.EXPECT().ScanType().Return(reflect.TypeOf(uint32(0)))
|
|
mockRows.EXPECT().ColumnTypes().Return([]driver.ColumnType{
|
|
colTimeReceived,
|
|
colSamplingRate,
|
|
colSrcAddr,
|
|
colSrcCountry,
|
|
colInIfName,
|
|
colInIfBoundary,
|
|
colInIfSpeed,
|
|
}).AnyTimes()
|
|
|
|
mockRows.EXPECT().Scan(gomock.Any()).
|
|
DoAndReturn(func(args ...any) any {
|
|
arg0 := args[0].(*time.Time)
|
|
*arg0 = time.Date(2022, 4, 4, 8, 36, 11, 10, time.UTC)
|
|
arg1 := args[1].(*uint64)
|
|
*arg1 = uint64(10000)
|
|
arg2 := args[2].(*net.IP)
|
|
*arg2 = net.ParseIP("2001:db8::22")
|
|
arg3 := args[3].(*string)
|
|
*arg3 = "FR"
|
|
arg4 := args[4].(*string)
|
|
*arg4 = "Hu0/0/1/10"
|
|
arg5 := args[5].(*string)
|
|
*arg5 = "external"
|
|
arg6 := args[6].(*uint32)
|
|
*arg6 = uint32(100000)
|
|
return nil
|
|
})
|
|
|
|
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
|
{
|
|
URL: "/api/v0/console/widget/flow-last",
|
|
JSONOutput: gin.H{
|
|
"InIfBoundary": "external",
|
|
"InIfName": "Hu0/0/1/10",
|
|
"InIfSpeed": 100000,
|
|
"SamplingRate": 10000,
|
|
"SrcAddr": "2001:db8::22",
|
|
"SrcCountry": "FR",
|
|
"TimeReceived": "2022-04-04T08:36:11.00000001Z",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestFlowRate(t *testing.T) {
|
|
_, h, mockConn, _ := NewMock(t, DefaultConfiguration())
|
|
|
|
ctrl := gomock.NewController(t)
|
|
mockRow := mocks.NewMockRow(ctrl)
|
|
mockRow.EXPECT().Scan(gomock.Any()).SetArg(0, float64(100.1)).Return(nil)
|
|
mockConn.EXPECT().
|
|
QueryRow(gomock.Any(),
|
|
`SELECT COUNT(*)/300 AS rate FROM flows WHERE TimeReceived > date_sub(minute, 5, now())`).
|
|
Return(mockRow)
|
|
|
|
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
|
{
|
|
URL: "/api/v0/console/widget/flow-rate",
|
|
JSONOutput: gin.H{
|
|
"period": "second",
|
|
"rate": 100.1,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWidgetExporters(t *testing.T) {
|
|
_, h, mockConn, _ := NewMock(t, DefaultConfiguration())
|
|
|
|
expected := []struct {
|
|
ExporterName string
|
|
}{
|
|
{"exporter1"},
|
|
{"exporter2"},
|
|
{"exporter3"},
|
|
}
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(),
|
|
`SELECT ExporterName FROM exporters GROUP BY ExporterName ORDER BY ExporterName`).
|
|
SetArg(1, expected).
|
|
Return(nil)
|
|
|
|
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
|
{
|
|
URL: "/api/v0/console/widget/exporters",
|
|
JSONOutput: gin.H{
|
|
"exporters": []string{
|
|
"exporter1",
|
|
"exporter2",
|
|
"exporter3",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWidgetTop(t *testing.T) {
|
|
_, h, mockConn, _ := NewMock(t, DefaultConfiguration())
|
|
|
|
gomock.InOrder(
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).
|
|
SetArg(1, []topResult{
|
|
{"TCP/443", float64(51)},
|
|
{"UDP/443", float64(20)},
|
|
{"TCP/80", float64(18)},
|
|
}),
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).
|
|
SetArg(1, []topResult{
|
|
{"TCP", float64(75)},
|
|
{"UDP", float64(24)},
|
|
{"ESP", float64(1)},
|
|
}),
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).
|
|
SetArg(1, []topResult{
|
|
{"exporter1", float64(20)},
|
|
{"exporter3", float64(10)},
|
|
{"exporter5", float64(3)},
|
|
}),
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).
|
|
SetArg(1, []topResult{
|
|
{"2906: Netflix", float64(12)},
|
|
{"36040: Youtube", float64(10)},
|
|
{"20940: Akamai", float64(9)},
|
|
}),
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).
|
|
SetArg(1, []topResult{
|
|
{"TCP/443", float64(80)},
|
|
{"TCP/80", float64(18)},
|
|
}),
|
|
)
|
|
|
|
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
|
{
|
|
URL: "/api/v0/console/widget/top/src-port",
|
|
JSONOutput: gin.H{
|
|
"top": []gin.H{
|
|
{"name": "TCP/443", "percent": 51},
|
|
{"name": "UDP/443", "percent": 20},
|
|
{"name": "TCP/80", "percent": 18},
|
|
},
|
|
},
|
|
}, {
|
|
URL: "/api/v0/console/widget/top/protocol",
|
|
JSONOutput: gin.H{
|
|
"top": []gin.H{
|
|
{"name": "TCP", "percent": 75},
|
|
{"name": "UDP", "percent": 24},
|
|
{"name": "ESP", "percent": 1},
|
|
},
|
|
},
|
|
}, {
|
|
URL: "/api/v0/console/widget/top/exporter",
|
|
JSONOutput: gin.H{
|
|
"top": []gin.H{
|
|
{"name": "exporter1", "percent": 20},
|
|
{"name": "exporter3", "percent": 10},
|
|
{"name": "exporter5", "percent": 3},
|
|
},
|
|
},
|
|
}, {
|
|
URL: "/api/v0/console/widget/top/src-as",
|
|
JSONOutput: gin.H{
|
|
"top": []gin.H{
|
|
{"name": "2906: Netflix", "percent": 12},
|
|
{"name": "36040: Youtube", "percent": 10},
|
|
{"name": "20940: Akamai", "percent": 9},
|
|
},
|
|
},
|
|
}, {
|
|
URL: "/api/v0/console/widget/top/dst-port",
|
|
JSONOutput: gin.H{
|
|
"top": []gin.H{
|
|
{"name": "TCP/443", "percent": 80},
|
|
{"name": "TCP/80", "percent": 18},
|
|
},
|
|
},
|
|
}, {
|
|
URL: "/api/v0/console/widget/top/notexist",
|
|
StatusCode: 400,
|
|
JSONOutput: gin.H{
|
|
"message": "Notexist does not belong to HomepageTopWidget values",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWidgetGraph(t *testing.T) {
|
|
testcases := []struct {
|
|
config Configuration
|
|
query string
|
|
}{
|
|
{
|
|
config: DefaultConfiguration(),
|
|
query: `
|
|
SELECT
|
|
toStartOfInterval(TimeReceived + INTERVAL 144 second, INTERVAL 432 second) - INTERVAL 144 second AS Time,
|
|
SUM(Bytes*SamplingRate*8/432)/1000/1000/1000 AS Gbps
|
|
FROM flows
|
|
WHERE TimeReceived BETWEEN toDateTime('2009-11-10 23:00:00', 'UTC') AND toDateTime('2009-11-11 23:00:00', 'UTC')
|
|
AND InIfBoundary = 'external'
|
|
GROUP BY Time
|
|
ORDER BY Time WITH FILL
|
|
FROM toDateTime('2009-11-10 23:00:00', 'UTC')
|
|
TO toDateTime('2009-11-11 23:00:00', 'UTC') + INTERVAL 1 second
|
|
STEP 432`,
|
|
},
|
|
{
|
|
config: func() Configuration {
|
|
c := DefaultConfiguration()
|
|
c.HomepageGraphFilter = ""
|
|
return c
|
|
}(),
|
|
query: `
|
|
SELECT
|
|
toStartOfInterval(TimeReceived + INTERVAL 144 second, INTERVAL 432 second) - INTERVAL 144 second AS Time,
|
|
SUM(Bytes*SamplingRate*8/432)/1000/1000/1000 AS Gbps
|
|
FROM flows
|
|
WHERE TimeReceived BETWEEN toDateTime('2009-11-10 23:00:00', 'UTC') AND toDateTime('2009-11-11 23:00:00', 'UTC')
|
|
|
|
GROUP BY Time
|
|
ORDER BY Time WITH FILL
|
|
FROM toDateTime('2009-11-10 23:00:00', 'UTC')
|
|
TO toDateTime('2009-11-11 23:00:00', 'UTC') + INTERVAL 1 second
|
|
STEP 432`,
|
|
},
|
|
{
|
|
config: func() Configuration {
|
|
c := DefaultConfiguration()
|
|
c.HomepageGraphFilter = "OutIfBoundary = 'external'"
|
|
return c
|
|
}(),
|
|
query: `
|
|
SELECT
|
|
toStartOfInterval(TimeReceived + INTERVAL 144 second, INTERVAL 432 second) - INTERVAL 144 second AS Time,
|
|
SUM(Bytes*SamplingRate*8/432)/1000/1000/1000 AS Gbps
|
|
FROM flows
|
|
WHERE TimeReceived BETWEEN toDateTime('2009-11-10 23:00:00', 'UTC') AND toDateTime('2009-11-11 23:00:00', 'UTC')
|
|
AND OutIfBoundary = 'external'
|
|
GROUP BY Time
|
|
ORDER BY Time WITH FILL
|
|
FROM toDateTime('2009-11-10 23:00:00', 'UTC')
|
|
TO toDateTime('2009-11-11 23:00:00', 'UTC') + INTERVAL 1 second
|
|
STEP 432`,
|
|
},
|
|
}
|
|
for _, tcase := range testcases {
|
|
_, h, mockConn, mockClock := NewMock(t, tcase.config)
|
|
|
|
base := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
|
mockClock.Set(base.Add(24 * time.Hour))
|
|
expected := []struct {
|
|
Time time.Time `json:"t"`
|
|
Gbps float64 `json:"gbps"`
|
|
}{
|
|
{base, 25.3},
|
|
{base.Add(time.Minute), 27.8},
|
|
{base.Add(2 * time.Minute), 26.4},
|
|
{base.Add(3 * time.Minute), 29.2},
|
|
{base.Add(4 * time.Minute), 0},
|
|
{base.Add(5 * time.Minute), 24.7},
|
|
}
|
|
mockConn.EXPECT().
|
|
Select(gomock.Any(), gomock.Any(), strings.TrimSpace(tcase.query)).
|
|
SetArg(1, expected).
|
|
Return(nil)
|
|
|
|
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
|
{
|
|
URL: "/api/v0/console/widget/graph",
|
|
JSONOutput: gin.H{
|
|
"data": []gin.H{
|
|
{"t": "2009-11-10T23:00:00Z", "gbps": 25.3},
|
|
{"t": "2009-11-10T23:01:00Z", "gbps": 27.8},
|
|
{"t": "2009-11-10T23:02:00Z", "gbps": 26.4},
|
|
{"t": "2009-11-10T23:03:00Z", "gbps": 29.2},
|
|
{"t": "2009-11-10T23:04:00Z", "gbps": 0},
|
|
{"t": "2009-11-10T23:05:00Z", "gbps": 24.7},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
}
|