package database import ( "database/sql" "path/filepath" "testing" "time" ) func testDB(t *testing.T) *sql.DB { db, err := sql.Open("sqlite3", filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { db.Close() }) if _, err := MigrateConnections(db); err != nil { t.Fatal(err) } return db } func TestMacForIPFreshness(t *testing.T) { db := testDB(t) if err := RefreshConnections(db, map[string]string{"192.168.1.5": "aa:bb:cc:dd:ee:ff"}); err != nil { t.Fatal(err) } if mac, _ := MacForIP(db, "192.168.1.5"); mac != "aa:bb:cc:dd:ee:ff" { t.Fatalf("fresh entry = %q, want the mac", mac) } // age the entry past the window: it should stop resolving db.Exec("UPDATE connections SET updated_at = ? WHERE ip = ?", time.Now().Add(-2*connectionFreshness), "192.168.1.5") if mac, _ := MacForIP(db, "192.168.1.5"); mac != "" { t.Errorf("stale entry resolved to %q, want empty", mac) } // seeing it again re-activates it if err := RefreshConnections(db, map[string]string{"192.168.1.5": "aa:bb:cc:dd:ee:ff"}); err != nil { t.Fatal(err) } if mac, _ := MacForIP(db, "192.168.1.5"); mac != "aa:bb:cc:dd:ee:ff" { t.Errorf("re-seen entry = %q, want the mac", mac) } } func TestRefreshConnectionsPrunesStale(t *testing.T) { db := testDB(t) RefreshConnections(db, map[string]string{"10.0.0.1": "aa:aa:aa:aa:aa:aa"}) db.Exec("UPDATE connections SET updated_at = ? WHERE ip = ?", time.Now().Add(-2*connectionFreshness), "10.0.0.1") // a refresh with a different device prunes the aged-out one RefreshConnections(db, map[string]string{"10.0.0.2": "bb:bb:bb:bb:bb:bb"}) conns, err := ListAliveConnections(db) if err != nil { t.Fatal(err) } if len(conns) != 1 || conns[0].IP != "10.0.0.2" { t.Errorf("after prune = %+v, want only 10.0.0.2", conns) } }