156 lines
3.9 KiB
Go
156 lines
3.9 KiB
Go
package bridge
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.saqut.com/saqut/mwse/internal/notify"
|
|
)
|
|
|
|
// ---- Inbox ---------------------------------------------------------------
|
|
|
|
func TestInboxPushDrain(t *testing.T) {
|
|
inbox := NewInbox(0)
|
|
inbox.Push("a", "hello")
|
|
inbox.Push("b", 42)
|
|
|
|
msgs := inbox.Drain()
|
|
if len(msgs) != 2 {
|
|
t.Fatalf("drain = %d, want 2", len(msgs))
|
|
}
|
|
if msgs[0].From != "a" || msgs[1].From != "b" {
|
|
t.Fatalf("from order wrong: %v", msgs)
|
|
}
|
|
|
|
// Drain again should return nil (empty).
|
|
if got := inbox.Drain(); got != nil {
|
|
t.Fatalf("second drain = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestInboxCapDropsOldest(t *testing.T) {
|
|
inbox := NewInbox(3)
|
|
for i := range 5 {
|
|
inbox.Push("x", i)
|
|
}
|
|
if inbox.Len() != 3 {
|
|
t.Fatalf("len = %d, want 3 (capped)", inbox.Len())
|
|
}
|
|
msgs := inbox.Drain()
|
|
// Should contain the last 3 items: 2, 3, 4.
|
|
if msgs[0].Pack.(int) != 2 {
|
|
t.Fatalf("oldest surviving item = %v, want 2", msgs[0].Pack)
|
|
}
|
|
}
|
|
|
|
func TestInboxLen(t *testing.T) {
|
|
inbox := NewInbox(0)
|
|
if inbox.Len() != 0 {
|
|
t.Fatalf("initial len = %d, want 0", inbox.Len())
|
|
}
|
|
inbox.Push("a", nil)
|
|
if inbox.Len() != 1 {
|
|
t.Fatalf("after push len = %d, want 1", inbox.Len())
|
|
}
|
|
}
|
|
|
|
// ---- HTTPApprover --------------------------------------------------------
|
|
|
|
func TestHTTPApproverApproves(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var body map[string]any
|
|
json.NewDecoder(r.Body).Decode(&body)
|
|
if body["id"] == nil {
|
|
http.Error(w, "missing id", 400)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(map[string]any{"approve": true})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
a := NewHTTPApprover(srv.URL, 2*time.Second)
|
|
if !a.Approve("client-1", map[string]any{"ip": "1.2.3.4"}) {
|
|
t.Fatal("expected approve=true")
|
|
}
|
|
}
|
|
|
|
func TestHTTPApproverRejects(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(map[string]any{"approve": false})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
a := NewHTTPApprover(srv.URL, 2*time.Second)
|
|
if a.Approve("client-2", nil) {
|
|
t.Fatal("expected approve=false")
|
|
}
|
|
}
|
|
|
|
func TestHTTPApproverFailClosed(t *testing.T) {
|
|
// Unreachable URL must deny (fail-closed).
|
|
a := NewHTTPApprover("http://127.0.0.1:1", 100*time.Millisecond)
|
|
if a.Approve("x", nil) {
|
|
t.Fatal("expected fail-closed denial for unreachable server")
|
|
}
|
|
}
|
|
|
|
func TestHTTPApproverNon200Denies(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
a := NewHTTPApprover(srv.URL, 2*time.Second)
|
|
if a.Approve("c", nil) {
|
|
t.Fatal("expected denial on non-200")
|
|
}
|
|
}
|
|
|
|
// ---- HTTPTrigger ---------------------------------------------------------
|
|
|
|
func TestHTTPTriggerPosts(t *testing.T) {
|
|
received := make(chan map[string]any, 1)
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var body map[string]any
|
|
json.NewDecoder(r.Body).Decode(&body)
|
|
received <- body
|
|
}))
|
|
defer srv.Close()
|
|
|
|
trigger := NewHTTPTrigger(srv.URL, 2*time.Second)
|
|
n := notify.Notification{
|
|
Trace: "tr1",
|
|
From: "alice",
|
|
To: "bob",
|
|
Reply: map[string]any{"ok": true},
|
|
}
|
|
trigger.NotifyReplied(n)
|
|
|
|
select {
|
|
case body := <-received:
|
|
if body["trace"] != "tr1" || body["from"] != "alice" {
|
|
t.Fatalf("received body = %v", body)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("trigger post not received within 2s")
|
|
}
|
|
}
|
|
|
|
func TestHTTPTriggerBestEffortOnError(t *testing.T) {
|
|
// An unreachable trigger must not panic or block.
|
|
trigger := NewHTTPTrigger("http://127.0.0.1:1", 100*time.Millisecond)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
trigger.NotifyReplied(notify.Notification{Trace: "x"})
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("trigger blocked on error instead of returning quickly")
|
|
}
|
|
}
|