- Introduced CORS configuration options in config.example.yaml, allowing specification of allowed origins for cross-origin requests. - Enhanced the aggregate handler to support response caching with a configurable TTL, improving performance for repeated requests. - Updated the aggregate API to return a structured response indicating whether any upstream requests failed, enhancing error handling and response clarity. - Modified documentation in AGGREGATE.md and README.md to reflect the new CORS and caching features. - Added tests to validate the new functionality in the aggregate handler.
181 lines
4.8 KiB
Go
181 lines
4.8 KiB
Go
package aggregate
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestBuildTraffic(t *testing.T) {
|
|
results := []ServerFetchResult{
|
|
{
|
|
Alias: "a", OK: true, Revision: "r1",
|
|
Users: []UserInfo{
|
|
{Username: "u1", TotalOctets: 100, CurrentConnections: 2},
|
|
{Username: "u2", TotalOctets: 50, CurrentConnections: 0},
|
|
},
|
|
},
|
|
{
|
|
Alias: "b", OK: true, Revision: "r2",
|
|
Users: []UserInfo{
|
|
{Username: "u1", TotalOctets: 30, CurrentConnections: 1},
|
|
},
|
|
},
|
|
}
|
|
rows := BuildTraffic(results)
|
|
if len(rows) != 2 {
|
|
t.Fatalf("len rows: %d", len(rows))
|
|
}
|
|
var u1 *TrafficRow
|
|
for i := range rows {
|
|
if rows[i].Username == "u1" {
|
|
u1 = &rows[i]
|
|
break
|
|
}
|
|
}
|
|
if u1 == nil {
|
|
t.Fatal("missing u1")
|
|
}
|
|
if u1.Servers["a"].TotalMegabytes != octetsToMegabytes(100) || u1.Servers["b"].TotalMegabytes != octetsToMegabytes(30) {
|
|
t.Fatalf("u1 servers: %+v", u1.Servers)
|
|
}
|
|
}
|
|
|
|
func TestBuildUniqueIPs(t *testing.T) {
|
|
results := []ServerFetchResult{
|
|
{
|
|
Alias: "s1", OK: true,
|
|
Users: []UserInfo{
|
|
{
|
|
Username: "alice",
|
|
ActiveUniqueIPsList: []string{"10.0.0.1"},
|
|
RecentUniqueIPsList: []string{"10.0.0.2"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Alias: "s2", OK: true,
|
|
Users: []UserInfo{
|
|
{
|
|
Username: "alice",
|
|
ActiveUniqueIPsList: []string{"10.0.0.1", "10.0.0.3"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
rows := BuildUniqueIPs(results)
|
|
if len(rows) != 1 || rows[0].Username != "alice" {
|
|
t.Fatalf("rows: %+v", rows)
|
|
}
|
|
var ip1 *IPAssignments
|
|
for i := range rows[0].IPs {
|
|
if rows[0].IPs[i].IP == "10.0.0.1" {
|
|
ip1 = &rows[0].IPs[i]
|
|
break
|
|
}
|
|
}
|
|
if ip1 == nil {
|
|
t.Fatal("missing 10.0.0.1")
|
|
}
|
|
if !reflect.DeepEqual(ip1.ActiveOnServers, []string{"s1", "s2"}) {
|
|
t.Fatalf("active: %v", ip1.ActiveOnServers)
|
|
}
|
|
if ip1.PrimaryServer != nil {
|
|
t.Fatalf("expected no primary, got %v", *ip1.PrimaryServer)
|
|
}
|
|
}
|
|
|
|
func TestBuildUniqueIPsPrimary(t *testing.T) {
|
|
results := []ServerFetchResult{
|
|
{Alias: "only", OK: true, Users: []UserInfo{
|
|
{Username: "bob", ActiveUniqueIPsList: []string{"192.168.1.1"}},
|
|
}},
|
|
}
|
|
rows := BuildUniqueIPs(results)
|
|
if len(rows[0].IPs) != 1 || rows[0].IPs[0].PrimaryServer == nil || *rows[0].IPs[0].PrimaryServer != "only" {
|
|
t.Fatalf("primary: %+v", rows[0].IPs)
|
|
}
|
|
}
|
|
|
|
func TestBuildSummary(t *testing.T) {
|
|
results := []ServerFetchResult{
|
|
{
|
|
Alias: "a", OK: true,
|
|
Users: []UserInfo{
|
|
{Username: "x", TotalOctets: 100, CurrentConnections: 2, ActiveUniqueIPs: 3},
|
|
{Username: "y", TotalOctets: 500, CurrentConnections: 0, ActiveUniqueIPs: 1},
|
|
},
|
|
},
|
|
{Alias: "b", OK: false, Error: "down"},
|
|
}
|
|
s := BuildSummary(results, 5)
|
|
if s.ServersOK != 1 || s.ServersFailed != 1 || s.FleetTotalMegabytes != octetsToMegabytes(600) || s.FleetTotalConnections != 2 {
|
|
t.Fatalf("summary: %+v", s)
|
|
}
|
|
if len(s.TopUsers) != 2 || s.TopUsers[0].Username != "y" || s.TopUsers[0].TotalMegabytes != octetsToMegabytes(500) {
|
|
t.Fatalf("top traffic: %+v", s.TopUsers)
|
|
}
|
|
if len(s.TopUsersByUniqueIPs) != 2 || s.TopUsersByUniqueIPs[0].Username != "x" || s.TopUsersByUniqueIPs[0].UniqueIPs != 3 {
|
|
t.Fatalf("top unique ips: %+v", s.TopUsersByUniqueIPs)
|
|
}
|
|
}
|
|
|
|
func TestBuildUsersMergeLimits(t *testing.T) {
|
|
tag1 := "aa"
|
|
tag2 := "bb"
|
|
expLater := "2030-01-02T00:00:00Z"
|
|
expEarlier := "2020-01-02T00:00:00Z"
|
|
m20 := uint64(20)
|
|
m10 := uint64(10)
|
|
q500 := uint64(500)
|
|
q100 := uint64(100)
|
|
results := []ServerFetchResult{
|
|
{
|
|
Alias: "b", OK: true,
|
|
Users: []UserInfo{{
|
|
Username: "u", TotalOctets: 1, CurrentConnections: 0,
|
|
UserAdTag: &tag2, MaxTCPConns: &m20, ExpirationRFC3339: &expLater,
|
|
DataQuotaBytes: &q500, MaxUniqueIPs: &m10,
|
|
}},
|
|
},
|
|
{
|
|
Alias: "a", OK: true,
|
|
Users: []UserInfo{{
|
|
Username: "u", TotalOctets: 1, CurrentConnections: 0,
|
|
UserAdTag: &tag1, MaxTCPConns: &m10, ExpirationRFC3339: &expEarlier,
|
|
DataQuotaBytes: &q100, MaxUniqueIPs: &m20,
|
|
}},
|
|
},
|
|
}
|
|
rows := BuildUsers(results, false, 0)
|
|
if len(rows) != 1 {
|
|
t.Fatalf("rows: %+v", rows)
|
|
}
|
|
r := rows[0]
|
|
if r.UserAdTag == nil || *r.UserAdTag != tag1 {
|
|
t.Fatalf("ad tag want first alias order a: got %v", r.UserAdTag)
|
|
}
|
|
if r.MaxTCPConns == nil || *r.MaxTCPConns != 10 {
|
|
t.Fatalf("max tcp want min 10: %v", r.MaxTCPConns)
|
|
}
|
|
if r.ExpirationRFC3339 == nil || *r.ExpirationRFC3339 != expEarlier {
|
|
t.Fatalf("expiration want earliest: %v", r.ExpirationRFC3339)
|
|
}
|
|
if r.DataQuotaBytes == nil || *r.DataQuotaBytes != 100 {
|
|
t.Fatalf("quota want min: %v", r.DataQuotaBytes)
|
|
}
|
|
if r.MaxUniqueIPs == nil || *r.MaxUniqueIPs != 10 {
|
|
t.Fatalf("max unique ips want min: %v", r.MaxUniqueIPs)
|
|
}
|
|
}
|
|
|
|
func TestBuildUsersMinOctets(t *testing.T) {
|
|
results := []ServerFetchResult{
|
|
{Alias: "a", OK: true, Users: []UserInfo{{Username: "low", TotalOctets: 5}}},
|
|
{Alias: "b", OK: true, Users: []UserInfo{{Username: "high", TotalOctets: 100}}},
|
|
}
|
|
rows := BuildUsers(results, false, 50)
|
|
if len(rows) != 1 || rows[0].Username != "high" {
|
|
t.Fatalf("rows: %+v", rows)
|
|
}
|
|
}
|