diff --git a/data/nodeinfo_test.go b/data/nodeinfo_test.go new file mode 100644 index 0000000..40f7889 --- /dev/null +++ b/data/nodeinfo_test.go @@ -0,0 +1,26 @@ +package data + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNodeinfoBatAddresses(t *testing.T) { + assert := assert.New(t) + batIface := &BatInterface{ + Interfaces: struct { + Wireless []string `json:"wireless,omitempty"` + Other []string `json:"other,omitempty"` + Tunnel []string `json:"tunnel,omitempty"` + }{ + Wireless: nil, + Other: []string{"aa:aa:aa:aa:aa", "aa:aa:aa:aa:ab"}, + Tunnel: []string{}, + }, + } + + addr := batIface.Addresses() + assert.NotNil(addr) + assert.Equal([]string{"aa:aa:aa:aa:aa", "aa:aa:aa:aa:ab"}, addr) +} diff --git a/database/all/internal.go b/database/all/internal.go index f1121d0..8a58b66 100644 --- a/database/all/internal.go +++ b/database/all/internal.go @@ -1,6 +1,7 @@ package all import ( + "log" "time" "github.com/FreifunkBremen/yanic/database" @@ -12,12 +13,23 @@ type Connection struct { list []database.Connection } -func Connect(configuration interface{}) (database.Connection, error) { +func Connect(allConnection map[string]interface{}) (database.Connection, error) { var list []database.Connection - allConnection := configuration.(map[string][]interface{}) for dbType, conn := range database.Adapters { - dbConfigs := allConnection[dbType] + configForType := allConnection[dbType] + if configForType == nil { + log.Printf("the output type '%s' has no configuration\n", dbType) + continue + } + dbConfigs, ok := configForType.([]map[string]interface{}) + if !ok { + log.Panicf("the output type '%s' has the wrong format\n", dbType) + } + for _, config := range dbConfigs { + if c, ok := config["enable"].(bool); ok && !c { + continue + } connected, err := conn(config) if err != nil { return nil, err diff --git a/database/all/internel_test.go b/database/all/internel_test.go index 1a6c647..019c2c0 100644 --- a/database/all/internel_test.go +++ b/database/all/internel_test.go @@ -1 +1,156 @@ package all + +import ( + "errors" + "sync" + "testing" + "time" + + "github.com/FreifunkBremen/yanic/database" + "github.com/FreifunkBremen/yanic/runtime" + "github.com/stretchr/testify/assert" +) + +type testConn struct { + database.Connection + countNode int + countLink int + countGlobals int + countPrune int + countClose int + sync.Mutex +} + +func (c *testConn) InsertNode(node *runtime.Node) { + c.Lock() + c.countNode++ + c.Unlock() +} +func (c *testConn) GetNode() int { + c.Lock() + defer c.Unlock() + return c.countNode +} +func (c *testConn) InsertLink(link *runtime.Link, time time.Time) { + c.Lock() + c.countLink++ + c.Unlock() +} +func (c *testConn) GetLink() int { + c.Lock() + defer c.Unlock() + return c.countLink +} +func (c *testConn) InsertGlobals(stats *runtime.GlobalStats, time time.Time, site string) { + c.Lock() + c.countGlobals++ + c.Unlock() +} +func (c *testConn) GetGlobal() int { + c.Lock() + defer c.Unlock() + return c.countGlobals +} +func (c *testConn) PruneNodes(time.Duration) { + c.Lock() + c.countPrune++ + c.Unlock() +} +func (c *testConn) GetPrune() int { + c.Lock() + defer c.Unlock() + return c.countPrune +} +func (c *testConn) Close() { + c.Lock() + c.countClose++ + c.Unlock() +} +func (c *testConn) GetClose() int { + c.Lock() + defer c.Unlock() + return c.countClose +} + +func TestStart(t *testing.T) { + assert := assert.New(t) + + globalConn := &testConn{} + database.RegisterAdapter("a", func(config map[string]interface{}) (database.Connection, error) { + return globalConn, nil + }) + database.RegisterAdapter("b", func(config map[string]interface{}) (database.Connection, error) { + return globalConn, nil + }) + database.RegisterAdapter("c", func(config map[string]interface{}) (database.Connection, error) { + return globalConn, nil + }) + database.RegisterAdapter("d", func(config map[string]interface{}) (database.Connection, error) { + return nil, nil + }) + database.RegisterAdapter("e", func(config map[string]interface{}) (database.Connection, error) { + return nil, errors.New("blub") + }) + allConn, err := Connect(map[string]interface{}{ + "a": []map[string]interface{}{ + map[string]interface{}{ + "enable": false, + "path": "a1", + }, + map[string]interface{}{ + "path": "a2", + }, + map[string]interface{}{ + "enable": true, + "path": "a3", + }, + }, + "b": nil, + "c": []map[string]interface{}{ + map[string]interface{}{ + "path": "c1", + }, + }, + // fetch continue command in Connect + "d": []map[string]interface{}{ + map[string]interface{}{ + "path": "d0", + }, + }, + }) + assert.NoError(err) + + assert.Equal(0, globalConn.GetNode()) + allConn.InsertNode(nil) + assert.Equal(3, globalConn.GetNode()) + + assert.Equal(0, globalConn.GetLink()) + allConn.InsertLink(nil, time.Now()) + assert.Equal(3, globalConn.GetLink()) + + assert.Equal(0, globalConn.GetGlobal()) + allConn.InsertGlobals(nil, time.Now(), runtime.GLOBAL_SITE) + assert.Equal(3, globalConn.GetGlobal()) + + assert.Equal(0, globalConn.GetPrune()) + allConn.PruneNodes(time.Second) + assert.Equal(3, globalConn.GetPrune()) + + assert.Equal(0, globalConn.GetClose()) + allConn.Close() + assert.Equal(3, globalConn.GetClose()) + + _, err = Connect(map[string]interface{}{ + "e": []map[string]interface{}{ + map[string]interface{}{}, + }, + }) + assert.Error(err) + + // wrong format -> the only panic in Register + assert.Panics(func() { + Connect(map[string]interface{}{ + "e": true, + }) + }) +} diff --git a/database/database.go b/database/database.go index e4b1381..c40768d 100644 --- a/database/database.go +++ b/database/database.go @@ -25,7 +25,7 @@ type Connection interface { } // Connect function with config to get DB connection interface -type Connect func(config interface{}) (Connection, error) +type Connect func(config map[string]interface{}) (Connection, error) // Adapters is the list of registered database adapters var Adapters = map[string]Connect{} diff --git a/database/database_test.go b/database/database_test.go index f6f39b7..84e762e 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -6,11 +6,11 @@ import ( "github.com/stretchr/testify/assert" ) -func TestNow(t *testing.T) { +func TestRegister(t *testing.T) { assert := assert.New(t) assert.Len(Adapters, 0) - RegisterAdapter("blub", func(config interface{}) (Connection, error) { + RegisterAdapter("blub", func(config map[string]interface{}) (Connection, error) { return nil, nil }) diff --git a/database/graphite/database.go b/database/graphite/database.go index bab9c06..c37cbc1 100644 --- a/database/graphite/database.go +++ b/database/graphite/database.go @@ -33,18 +33,10 @@ func (c Config) Prefix() string { return c["prefix"].(string) } -func (c Config) Enable() bool { - return c["enable"].(bool) -} - -func Connect(configuration interface{}) (database.Connection, error) { +func Connect(configuration map[string]interface{}) (database.Connection, error) { var config Config - config = configuration.(map[string]interface{}) - - if !config.Enable() { - return nil, nil - } + config = configuration con := &Connection{ client: graphigo.Client{ diff --git a/database/influxdb/database.go b/database/influxdb/database.go index b8e99d1..a0de00c 100644 --- a/database/influxdb/database.go +++ b/database/influxdb/database.go @@ -32,9 +32,6 @@ type Connection struct { type Config map[string]interface{} -func (c Config) Enable() bool { - return c["enable"].(bool) -} func (c Config) Address() string { return c["address"].(string) } @@ -57,12 +54,10 @@ func (c Config) Tags() map[string]interface{} { func init() { database.RegisterAdapter("influxdb", Connect) } -func Connect(configuration interface{}) (database.Connection, error) { +func Connect(configuration map[string]interface{}) (database.Connection, error) { var config Config - config = configuration.(map[string]interface{}) - if !config.Enable() { - return nil, nil - } + config = configuration + // Make client c, err := client.NewHTTPClient(client.HTTPConfig{ Addr: config.Address(), diff --git a/database/influxdb/database_test.go b/database/influxdb/database_test.go index e56e42c..5c2598c 100644 --- a/database/influxdb/database_test.go +++ b/database/influxdb/database_test.go @@ -10,6 +10,27 @@ import ( "github.com/stretchr/testify/assert" ) +func TestConnect(t *testing.T) { + assert := assert.New(t) + + conn, err := Connect(map[string]interface{}{ + "address": "", + "username": "", + "password": "", + }) + assert.Nil(conn) + assert.Error(err) + + conn, err = Connect(map[string]interface{}{ + "address": "http://localhost", + "database": "", + "username": "", + "password": "", + }) + assert.NotNil(conn) + assert.NoError(err) +} + func TestAddPoint(t *testing.T) { assert := assert.New(t) diff --git a/database/influxdb/global_test.go b/database/influxdb/global_test.go index 2bd0d2e..72d2dec 100644 --- a/database/influxdb/global_test.go +++ b/database/influxdb/global_test.go @@ -1,8 +1,11 @@ package influxdb import ( + "sync" "testing" + "time" + "github.com/influxdata/influxdb/client/v2" "github.com/stretchr/testify/assert" "github.com/FreifunkBremen/yanic/data" @@ -20,9 +23,68 @@ func TestGlobalStats(t *testing.T) { fields := GlobalStatsFields(stats[runtime.GLOBAL_SITE]) assert.EqualValues(3, fields["nodes"]) - // check TEST_SITE fields fields = GlobalStatsFields(stats[TEST_SITE]) - assert.EqualValues(2, fields["nodes"]) + assert.EqualValues(1, fields["nodes"]) + + conn := &Connection{ + points: make(chan *client.Point), + } + + global := 0 + globalSite := 0 + model := 0 + modelSite := 0 + firmware := 0 + firmwareSite := 0 + autoupdater := 0 + autoupdaterSite := 0 + wg := sync.WaitGroup{} + wg.Add(9) + go func() { + for p := range conn.points { + switch p.Name() { + case MeasurementGlobal: + global++ + break + case "global_site": + globalSite++ + break + case CounterMeasurementModel: + model++ + break + case "model_site": + modelSite++ + break + case CounterMeasurementFirmware: + firmware++ + break + case "firmware_site": + firmwareSite++ + break + case CounterMeasurementAutoupdater: + autoupdater++ + break + case "autoupdater_site": + autoupdaterSite++ + break + default: + assert.Equal("invalid p.Name found", p.Name()) + } + wg.Done() + } + }() + for site, stat := range stats { + conn.InsertGlobals(stat, time.Now(), site) + } + wg.Wait() + assert.Equal(1, global) + assert.Equal(1, globalSite) + assert.Equal(2, model) + assert.Equal(1, modelSite) + assert.Equal(1, firmware) + assert.Equal(0, firmwareSite) + assert.Equal(2, autoupdater) + assert.Equal(1, autoupdaterSite) } func createTestNodes() *runtime.Nodes { @@ -40,9 +102,7 @@ func createTestNodes() *runtime.Nodes { Hardware: data.Hardware{ Model: "TP-Link 841", }, - System: data.System{ - SiteCode: TEST_SITE, - }, + System: data.System{}, }, } nodeData.Nodeinfo.Software.Firmware.Release = "2016.1.6+entenhausen1" diff --git a/database/influxdb/node_test.go b/database/influxdb/node_test.go index 916c6fb..9e45d86 100644 --- a/database/influxdb/node_test.go +++ b/database/influxdb/node_test.go @@ -82,7 +82,9 @@ func TestToInflux(t *testing.T) { }, }, }, - LLDP: map[string]data.LLDPNeighbours{}, + LLDP: map[string]data.LLDPNeighbours{ + "b-interface": data.LLDPNeighbours{}, + }, }, } diff --git a/database/internal_test.go b/database/internal_test.go new file mode 100644 index 0000000..0280fb7 --- /dev/null +++ b/database/internal_test.go @@ -0,0 +1,69 @@ +package database + +import ( + "sync" + "testing" + "time" + + "github.com/FreifunkBremen/yanic/runtime" + "github.com/stretchr/testify/assert" +) + +type testConn struct { + Connection + countClose int + countPrune int + sync.Mutex +} + +func (c *testConn) Close() { + c.Lock() + c.countClose++ + c.Unlock() +} +func (c *testConn) GetClose() int { + c.Lock() + defer c.Unlock() + return c.countClose +} +func (c *testConn) PruneNodes(time.Duration) { + c.Lock() + c.countPrune++ + c.Unlock() +} +func (c *testConn) GetPruneNodes() int { + c.Lock() + defer c.Unlock() + return c.countPrune +} + +func TestStart(t *testing.T) { + assert := assert.New(t) + + conn := &testConn{} + config := &runtime.Config{ + Database: struct { + DeleteInterval runtime.Duration `toml:"delete_interval"` + DeleteAfter runtime.Duration `toml:"delete_after"` + Connection map[string]interface{} + }{ + DeleteInterval: runtime.Duration{Duration: time.Millisecond * 10}, + }, + } + assert.Nil(quit) + + Start(conn, config) + assert.NotNil(quit) + + assert.Equal(0, conn.GetPruneNodes()) + time.Sleep(time.Millisecond * 12) + assert.Equal(1, conn.GetPruneNodes()) + + assert.Equal(0, conn.GetClose()) + Close(conn) + assert.NotNil(quit) + assert.Equal(1, conn.GetClose()) + + time.Sleep(time.Millisecond * 12) // to reach timer.Stop() line + +} diff --git a/database/logging/file.go b/database/logging/file.go index c383578..328e307 100644 --- a/database/logging/file.go +++ b/database/logging/file.go @@ -23,9 +23,6 @@ type Connection struct { type Config map[string]interface{} -func (c Config) Enable() bool { - return c["enable"].(bool) -} func (c Config) Path() string { return c["path"].(string) } @@ -34,12 +31,9 @@ func init() { database.RegisterAdapter("logging", Connect) } -func Connect(configuration interface{}) (database.Connection, error) { +func Connect(configuration map[string]interface{}) (database.Connection, error) { var config Config - config = configuration.(map[string]interface{}) - if !config.Enable() { - return nil, nil - } + config = configuration file, err := os.OpenFile(config.Path(), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { diff --git a/database/logging/file_test.go b/database/logging/file_test.go index 2b43acc..13c0408 100644 --- a/database/logging/file_test.go +++ b/database/logging/file_test.go @@ -1 +1,60 @@ package logging + +import ( + "io/ioutil" + "os" + "testing" + "time" + + "github.com/FreifunkBremen/yanic/data" + "github.com/FreifunkBremen/yanic/runtime" + "github.com/stretchr/testify/assert" +) + +func TestStart(t *testing.T) { + assert := assert.New(t) + + conn, err := Connect(map[string]interface{}{ + "path": "/dev/notexists/file", + }) + assert.Nil(conn) + assert.Error(err) + + path := "/tmp/testlogfile" + + conn, err = Connect(map[string]interface{}{ + "path": path, + }) + + dat, _ := ioutil.ReadFile(path) + assert.NotContains(string(dat), "InsertNode") + + conn.InsertNode(&runtime.Node{ + Statistics: &data.Statistics{}, + }) + + dat, _ = ioutil.ReadFile(path) + assert.Contains(string(dat), "InsertNode") + + assert.NotContains(string(dat), "InsertLink") + conn.InsertLink(&runtime.Link{}, time.Now()) + dat, _ = ioutil.ReadFile(path) + assert.Contains(string(dat), "InsertLink") + + assert.NotContains(string(dat), "InsertGlobals") + conn.InsertGlobals(&runtime.GlobalStats{}, time.Now(), runtime.GLOBAL_SITE) + dat, _ = ioutil.ReadFile(path) + assert.Contains(string(dat), "InsertGlobals") + + assert.NotContains(string(dat), "PruneNodes") + conn.PruneNodes(time.Second) + dat, _ = ioutil.ReadFile(path) + assert.Contains(string(dat), "PruneNodes") + + assert.NotContains(string(dat), "Close") + conn.Close() + dat, _ = ioutil.ReadFile(path) + assert.Contains(string(dat), "Close") + + os.Remove(path) +} diff --git a/output/meshviewer-ffrgb/meshviewer_test.go b/output/meshviewer-ffrgb/meshviewer_test.go index 72866c5..efd622a 100644 --- a/output/meshviewer-ffrgb/meshviewer_test.go +++ b/output/meshviewer-ffrgb/meshviewer_test.go @@ -172,12 +172,14 @@ func TestTransform(t *testing.T) { assert.Equal("node:b:mac:wifi", link.TargetMAC) assert.Equal(float32(0.6), link.SourceTQ) assert.Equal(float32(0.8), link.TargetTQ) - default: + case "node:b:mac:lan": assert.Equal("other", link.Type) assert.Equal("node:c:mac:lan", link.TargetMAC) assert.Equal(float32(0.8), link.SourceTQ) assert.Equal(float32(0.4), link.TargetTQ) break + default: + assert.False(true, "invalid link.SourceMAC found") } } } diff --git a/runtime/config.go b/runtime/config.go index 23e9325..d4578a2 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -37,7 +37,7 @@ type Config struct { Database struct { DeleteInterval Duration `toml:"delete_interval"` // Delete stats of nodes every n minutes DeleteAfter Duration `toml:"delete_after"` // Delete stats of nodes till now-deletetill n minutes - Connection map[string][]interface{} + Connection map[string]interface{} } } diff --git a/runtime/config_test.go b/runtime/config_test.go index 434e175..ae6269d 100644 --- a/runtime/config_test.go +++ b/runtime/config_test.go @@ -31,15 +31,15 @@ func TestReadConfig(t *testing.T) { assert.Equal("/var/www/html/meshviewer/data/nodes.json", meshviewer["nodes_path"]) var influxdb map[string]interface{} - dbs := config.Database.Connection["influxdb"] + dbs := config.Database.Connection["influxdb"].([]map[string]interface{}) assert.Len(dbs, 1, "more influxdb are given") - influxdb = dbs[0].(map[string]interface{}) + influxdb = dbs[0] assert.Equal(influxdb["database"], "ffhb") var graphitedb map[string]interface{} - dbs = config.Database.Connection["graphite"] + dbs = config.Database.Connection["graphite"].([]map[string]interface{}) assert.Len(dbs, 1, "more graphitedb are given") - graphitedb = dbs[0].(map[string]interface{}) + graphitedb = dbs[0] assert.Equal(graphitedb["address"], "localhost:2003") _, err = ReadConfigFile("testdata/config_failed.toml")