mirror of
https://github.com/hanzoai/world-zap.git
synced 2026-11-03 04:31:30 +00:00
Real-time Hanzo World feeds over ZAP binary protocol (9-byte header +
JSON payload) fronted by WebSockets at wss://zap.world.hanzo.ai/zap.
IAM bearer auth via Authorization header or ?token= query param.
Validator caches /oauth/userinfo for 5 minutes.
Topics (10): world.events.{all,conflicts,earthquakes,fires},
world.markets.{quotes,crypto}, world.ships.ais, world.aviation.opensky,
world.news.live, world.weather.alerts. Minimum plan enforced per topic.
Tools: subscribe, unsubscribe, list_topics. Admin-only publish for
internal service accounts.
Ingest: streams events from worldmonitor GET /v1/world/events?stream=1,
fans out via bounded-channel hub with drop-on-overflow semantics.
Per-user token bucket rate limits: free 30/min, pro 3000/min, team
15000/min, enterprise 60000/min.
Tests: all packages pass -race (proto, auth, hub, ratelimit, ingest,
handlers). Multi-arch image build wired to hanzoai/.github docker-build
workflow.
68 lines
1.3 KiB
Go
68 lines
1.3 KiB
Go
package ratelimit
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestPlanCapacity(t *testing.T) {
|
|
cases := map[string]int{
|
|
"free": 30,
|
|
"pro": 3000,
|
|
"team": 15000,
|
|
"enterprise": 60000,
|
|
"": 30,
|
|
"unknown": 30,
|
|
}
|
|
for plan, want := range cases {
|
|
if got := PlanCapacity(plan); got != want {
|
|
t.Errorf("plan %q: got %d want %d", plan, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFreeBurstThenBlock(t *testing.T) {
|
|
l := New()
|
|
for i := 0; i < 30; i++ {
|
|
d := l.TryAcquire("u", "free")
|
|
if !d.Allowed {
|
|
t.Fatalf("request %d denied: %+v", i, d)
|
|
}
|
|
}
|
|
d := l.TryAcquire("u", "free")
|
|
if d.Allowed {
|
|
t.Fatalf("31st request should be denied")
|
|
}
|
|
if d.RetryIn <= 0 {
|
|
t.Fatalf("expected retry hint")
|
|
}
|
|
}
|
|
|
|
func TestPerUserIsolation(t *testing.T) {
|
|
l := New()
|
|
for i := 0; i < 30; i++ {
|
|
l.TryAcquire("a", "free")
|
|
}
|
|
if l.TryAcquire("a", "free").Allowed {
|
|
t.Fatalf("a should be throttled")
|
|
}
|
|
if !l.TryAcquire("b", "free").Allowed {
|
|
t.Fatalf("b should be allowed")
|
|
}
|
|
}
|
|
|
|
func TestPlanUpgradeResetsBucket(t *testing.T) {
|
|
l := New()
|
|
// burn free tokens
|
|
for i := 0; i < 30; i++ {
|
|
l.TryAcquire("u", "free")
|
|
}
|
|
if l.TryAcquire("u", "free").Allowed {
|
|
t.Fatalf("free should be empty")
|
|
}
|
|
// Upgrade to pro; new bucket at pro capacity
|
|
d := l.TryAcquire("u", "pro")
|
|
if !d.Allowed {
|
|
t.Fatalf("pro upgrade should grant tokens: %+v", d)
|
|
}
|
|
}
|