mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
198 lines
5.5 KiB
Go
198 lines
5.5 KiB
Go
// SPDX-FileCopyrightText: 2025 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package schema_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ClickHouse/ch-go"
|
|
"github.com/ClickHouse/clickhouse-go/v2"
|
|
|
|
"akvorado/common/helpers"
|
|
"akvorado/common/schema"
|
|
)
|
|
|
|
func TestInsertMemory(t *testing.T) {
|
|
c := schema.NewMock(t)
|
|
bf := c.NewFlowMessage()
|
|
exporterAddress := netip.MustParseAddr("::ffff:203.0.113.14")
|
|
|
|
bf.TimeReceived = 1000
|
|
bf.SamplingRate = 20000
|
|
bf.ExporterAddress = exporterAddress
|
|
bf.AppendString(schema.ColumnExporterName, "router1.example.net")
|
|
bf.AppendUint(schema.ColumnSrcAS, 65000)
|
|
bf.AppendUint(schema.ColumnDstAS, 12322)
|
|
bf.AppendUint(schema.ColumnBytes, 20)
|
|
bf.AppendUint(schema.ColumnPackets, 3)
|
|
bf.AppendUint(schema.ColumnInIfBoundary, uint64(schema.InterfaceBoundaryInternal))
|
|
bf.AppendUint(schema.ColumnOutIfBoundary, uint64(schema.InterfaceBoundaryExternal))
|
|
bf.AppendUint(schema.ColumnInIfSpeed, 10000)
|
|
bf.AppendUint(schema.ColumnEType, helpers.ETypeIPv4)
|
|
bf.Finalize()
|
|
|
|
bf.TimeReceived = 1001
|
|
bf.SamplingRate = 20000
|
|
bf.ExporterAddress = exporterAddress
|
|
bf.AppendString(schema.ColumnExporterName, "router1.example.net")
|
|
bf.AppendUint(schema.ColumnSrcAS, 12322)
|
|
bf.AppendUint(schema.ColumnDstAS, 65000)
|
|
bf.AppendUint(schema.ColumnBytes, 200)
|
|
bf.AppendUint(schema.ColumnPackets, 3)
|
|
bf.AppendUint(schema.ColumnInIfBoundary, uint64(schema.InterfaceBoundaryExternal))
|
|
bf.AppendUint(schema.ColumnOutIfSpeed, 10000)
|
|
bf.AppendUint(schema.ColumnEType, helpers.ETypeIPv4)
|
|
bf.AppendArrayUInt32(schema.ColumnDstASPath, []uint32{65400, 65500, 65001})
|
|
bf.AppendArrayUInt128(schema.ColumnDstLargeCommunities, []schema.UInt128{
|
|
{
|
|
High: 65401,
|
|
Low: (100 << 32) + 200,
|
|
},
|
|
{
|
|
High: 65401,
|
|
Low: (100 << 32) + 201,
|
|
},
|
|
})
|
|
bf.Finalize()
|
|
|
|
server := helpers.CheckExternalService(t, "ClickHouse", []string{"clickhouse:9000", "127.0.0.1:9000"})
|
|
ctx := t.Context()
|
|
|
|
conn, err := ch.Dial(ctx, ch.Options{
|
|
Address: server,
|
|
Database: "test",
|
|
DialTimeout: 100 * time.Millisecond,
|
|
Settings: []ch.Setting{
|
|
{Key: "allow_suspicious_low_cardinality_types", Value: "1"},
|
|
{Key: "output_format_json_quote_64bit_integers", Value: "0", Important: true},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Dial() error:\n%+v", err)
|
|
}
|
|
|
|
// Create the table
|
|
q := fmt.Sprintf(
|
|
`CREATE OR REPLACE TABLE test_schema_insert (%s) ENGINE = Memory`,
|
|
c.ClickHouseCreateTable(schema.ClickHouseSkipAliasedColumns, schema.ClickHouseSkipGeneratedColumns),
|
|
)
|
|
t.Logf("Query: %s", q)
|
|
if err := conn.Do(ctx, ch.Query{
|
|
Body: q,
|
|
}); err != nil {
|
|
t.Fatalf("Do() error:\n%+v", err)
|
|
}
|
|
|
|
// Insert
|
|
input := bf.ClickHouseProtoInput()
|
|
if err := conn.Do(ctx, ch.Query{
|
|
Body: input.Into("test_schema_insert"),
|
|
Input: input,
|
|
OnInput: func(context.Context) error {
|
|
bf.Clear()
|
|
// No more data to send!
|
|
return io.EOF
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("Do() error:\n%+v", err)
|
|
}
|
|
|
|
// Check the result (with the full-featured client)
|
|
{
|
|
conn, err := clickhouse.Open(&clickhouse.Options{
|
|
Addr: []string{server},
|
|
Auth: clickhouse.Auth{
|
|
Database: "test",
|
|
},
|
|
DialTimeout: 100 * time.Millisecond,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("clickhouse.Open() error:\n%+v", err)
|
|
}
|
|
// Use formatRow to get JSON representation
|
|
rows, err := conn.Query(ctx, "SELECT formatRow('JSONEachRow', *) FROM test_schema_insert ORDER BY TimeReceived")
|
|
if err != nil {
|
|
t.Fatalf("clickhouse.Query() error:\n%+v", err)
|
|
}
|
|
|
|
var got []map[string]any
|
|
for rows.Next() {
|
|
var jsonRow string
|
|
if err := rows.Scan(&jsonRow); err != nil {
|
|
t.Fatalf("rows.Scan() error:\n%+v", err)
|
|
}
|
|
|
|
var row map[string]any
|
|
if err := json.Unmarshal([]byte(jsonRow), &row); err != nil {
|
|
t.Fatalf("json.Unmarshal() error:\n%+v", err)
|
|
}
|
|
|
|
// Remove fields with default values
|
|
for k, v := range row {
|
|
switch val := v.(type) {
|
|
case string:
|
|
if val == "" || val == "::" {
|
|
delete(row, k)
|
|
}
|
|
case float64:
|
|
if val == 0 {
|
|
delete(row, k)
|
|
}
|
|
case []any:
|
|
if len(val) == 0 {
|
|
delete(row, k)
|
|
}
|
|
}
|
|
}
|
|
got = append(got, row)
|
|
}
|
|
rows.Close()
|
|
|
|
expected := []map[string]any{
|
|
{
|
|
"TimeReceived": "1970-01-01 00:16:40",
|
|
"SamplingRate": float64(20000),
|
|
"ExporterAddress": "::ffff:203.0.113.14",
|
|
"ExporterName": "router1.example.net",
|
|
"SrcAS": float64(65000),
|
|
"DstAS": float64(12322),
|
|
"Bytes": float64(20),
|
|
"Packets": float64(3),
|
|
"InIfBoundary": "internal",
|
|
"OutIfBoundary": "external",
|
|
"InIfSpeed": float64(10000),
|
|
"EType": float64(helpers.ETypeIPv4),
|
|
}, {
|
|
"TimeReceived": "1970-01-01 00:16:41",
|
|
"SamplingRate": float64(20000),
|
|
"ExporterAddress": "::ffff:203.0.113.14",
|
|
"ExporterName": "router1.example.net",
|
|
"SrcAS": float64(12322),
|
|
"DstAS": float64(65000),
|
|
"Bytes": float64(200),
|
|
"Packets": float64(3),
|
|
"InIfBoundary": "external",
|
|
"OutIfBoundary": "undefined",
|
|
"OutIfSpeed": float64(10000),
|
|
"EType": float64(helpers.ETypeIPv4),
|
|
"DstASPath": []any{float64(65400), float64(65500), float64(65001)},
|
|
"DstLargeCommunities": []any{
|
|
float64(1206435509165107881967816), // 65401:100:200
|
|
float64(1206435509165107881967817), // 65401:100:201
|
|
},
|
|
},
|
|
}
|
|
|
|
if diff := helpers.Diff(got, expected); diff != "" {
|
|
t.Errorf("Insert (-got, +want):\n%s", diff)
|
|
}
|
|
}
|
|
}
|