Files

75 lines
2.6 KiB
Go
Raw Permalink Normal View History

2026-08-26 06:41:07 +08:00
package mockalertreceiver
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
)
func TestReceiverVerifiesAndRecordsSignedDelivery(t *testing.T) {
t.Parallel()
secret := "test-alert-receiver-secret-at-least-32-bytes"
now := time.Unix(1_800_000_000, 0).UTC()
server, err := New(Options{Secret: secret, ClockSkew: 5 * time.Minute}, slog.New(slog.NewTextHandler(io.Discard, nil)))
if err != nil {
t.Fatal(err)
}
server.now = func() time.Time { return now }
httpServer := httptest.NewServer(server.Handler())
defer httpServer.Close()
body, _ := json.Marshal(envelope{EventID: "10000000-0000-0000-0000-000000000001", EventType: "alert.webhook_test_requested", Data: json.RawMessage(`{"test_ref":"10000000-0000-0000-0000-000000000099"}`)})
timestamp := strconv.FormatInt(now.Unix(), 10)
request, _ := http.NewRequest(http.MethodPost, httpServer.URL+"/webhooks/alerts", bytes.NewReader(body))
request.Header.Set("X-Voice-Event-ID", "10000000-0000-0000-0000-000000000001")
request.Header.Set("X-Voice-Timestamp", timestamp)
request.Header.Set("X-Voice-Signature", testSignature([]byte(secret), timestamp, body))
response, err := http.DefaultClient.Do(request)
if err != nil {
t.Fatal(err)
}
response.Body.Close()
if response.StatusCode != http.StatusNoContent {
t.Fatalf("delivery status=%d", response.StatusCode)
}
response, err = http.Get(httpServer.URL + "/events?test_ref=10000000-0000-0000-0000-000000000099")
if err != nil {
t.Fatal(err)
}
defer response.Body.Close()
var result struct {
Items []Event `json:"items"`
}
if err = json.NewDecoder(response.Body).Decode(&result); err != nil || len(result.Items) != 1 || result.Items[0].EventType != "alert.webhook_test_requested" {
t.Fatalf("events=%+v err=%v", result.Items, err)
}
invalid, _ := http.NewRequest(http.MethodPost, httpServer.URL+"/webhooks/alerts", bytes.NewReader(body))
invalid.Header.Set("X-Voice-Event-ID", "10000000-0000-0000-0000-000000000001")
invalid.Header.Set("X-Voice-Timestamp", timestamp)
invalid.Header.Set("X-Voice-Signature", "v1=00")
response, err = http.DefaultClient.Do(invalid)
if err != nil {
t.Fatal(err)
}
response.Body.Close()
if response.StatusCode != http.StatusUnauthorized {
t.Fatalf("invalid signature status=%d", response.StatusCode)
}
}
func testSignature(secret []byte, timestamp string, body []byte) string {
mac := hmac.New(sha256.New, secret)
_, _ = mac.Write([]byte(timestamp + "."))
_, _ = mac.Write(body)
return "v1=" + hex.EncodeToString(mac.Sum(nil))
}