bygdata/pkg/devtodev/devtodev_test.go

250 lines
5.5 KiB
Go

package devtodev
import (
"bytes"
"compress/gzip"
"context"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/klauspost/compress/zstd"
)
func TestSend_NoCompression(t *testing.T) {
payload := samplePayload()
expected, err := json.Marshal(payload)
if err != nil {
t.Fatalf("marshal payload: %v", err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Fatalf("method: %s", r.Method)
}
if ct := r.Header.Get("Content-Type"); ct != "application/json" {
t.Fatalf("content-type: %s", ct)
}
if r.URL.Query().Get("appId") != "app-123" {
t.Fatalf("appId: %s", r.URL.Query().Get("appId"))
}
got, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("read body: %v", err)
}
if !bytes.Equal(got, expected) {
t.Fatalf("body mismatch")
}
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
client := NewClient("app-123")
client.Endpoint = server.URL
if err := client.Send(context.Background(), payload); err != nil {
t.Fatalf("send: %v", err)
}
}
func TestSend_GzipCompression(t *testing.T) {
payload := samplePayload()
expected, err := json.Marshal(payload)
if err != nil {
t.Fatalf("marshal payload: %v", err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if ct := r.Header.Get("Content-Type"); ct != "application/gzip" {
t.Fatalf("content-type: %s", ct)
}
zr, err := gzip.NewReader(r.Body)
if err != nil {
t.Fatalf("gzip reader: %v", err)
}
got, err := io.ReadAll(zr)
if err != nil {
t.Fatalf("read body: %v", err)
}
zr.Close()
if !bytes.Equal(got, expected) {
t.Fatalf("body mismatch")
}
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
client := NewClient("app-123")
client.Endpoint = server.URL
client.Compression = CompressionGzip
if err := client.Send(context.Background(), payload); err != nil {
t.Fatalf("send: %v", err)
}
}
func TestSend_ZstdCompression(t *testing.T) {
payload := samplePayload()
expected, err := json.Marshal(payload)
if err != nil {
t.Fatalf("marshal payload: %v", err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if ct := r.Header.Get("Content-Type"); ct != "application/zstd" {
t.Fatalf("content-type: %s", ct)
}
decoder, err := zstd.NewReader(r.Body)
if err != nil {
t.Fatalf("zstd reader: %v", err)
}
got, err := io.ReadAll(decoder)
if err != nil {
t.Fatalf("read body: %v", err)
}
decoder.Close()
if !bytes.Equal(got, expected) {
t.Fatalf("body mismatch")
}
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
client := NewClient("app-123")
client.Endpoint = server.URL
client.Compression = CompressionZstd
if err := client.Send(context.Background(), payload); err != nil {
t.Fatalf("send: %v", err)
}
}
func TestValidatePayload(t *testing.T) {
cases := []struct {
name string
payload Payload
ok bool
}{
{
name: "empty",
payload: Payload{},
ok: false,
},
{
name: "missing device id",
payload: Payload{
Reports: []Report{{}},
},
ok: false,
},
{
name: "missing packages",
payload: Payload{
Reports: []Report{{DeviceID: "d1"}},
},
ok: false,
},
{
name: "missing events",
payload: Payload{
Reports: []Report{{DeviceID: "d1", Packages: []Package{{}}}},
},
ok: false,
},
{
name: "missing code",
payload: Payload{
Reports: []Report{{DeviceID: "d1", Packages: []Package{{Events: []Event{{"timestamp": int64(1)}}}}}},
},
ok: false,
},
{
name: "missing timestamp",
payload: Payload{
Reports: []Report{{DeviceID: "d1", Packages: []Package{{Events: []Event{{"code": "ce"}}}}}},
},
ok: false,
},
{
name: "ok",
payload: Payload{
Reports: []Report{{DeviceID: "d1", Packages: []Package{{Events: []Event{{"code": "ce", "timestamp": int64(1)}}}}}},
},
ok: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := ValidatePayload(tc.payload)
if tc.ok && err != nil {
t.Fatalf("expected ok, got %v", err)
}
if !tc.ok && err == nil {
t.Fatalf("expected error")
}
})
}
}
func TestSendWithResponse_ParsesJSONError(t *testing.T) {
payload := samplePayload()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(`{"message":"bad request"}`))
}))
defer server.Close()
client := NewClient("app-123")
client.Endpoint = server.URL
client.Retry.MaxAttempts = 1
resp, err := client.SendWithResponse(context.Background(), payload)
if err == nil {
t.Fatalf("expected error")
}
if resp == nil || resp.StatusCode != http.StatusBadRequest {
t.Fatalf("unexpected response: %+v", resp)
}
var apiErr *APIError
if !errors.As(err, &apiErr) {
t.Fatalf("expected APIError, got %T", err)
}
if apiErr.Message != "bad request" {
t.Fatalf("unexpected message: %q", apiErr.Message)
}
}
func samplePayload() Payload {
return Payload{
Reports: []Report{
{
DeviceID: "device-123",
Packages: []Package{
{
Language: "en",
Country: "US",
Events: []Event{
{
"code": "ce",
"timestamp": time.Now().UnixMilli(),
"level": 5,
"name": "custom_event",
"parameters": map[string]interface{}{
"level": 5,
},
},
},
},
},
},
},
}
}