110 lines
3.8 KiB
Go
110 lines
3.8 KiB
Go
package imadmin
|
|
|
|
import (
|
|
"context"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type testInviteListRequest struct {
|
|
Page int `json:"page,omitempty"`
|
|
PerPage int `json:"per_page,omitempty"`
|
|
InviteCode string `json:"invite_code,omitempty"`
|
|
BatchID int64 `json:"batch_id,omitempty"`
|
|
}
|
|
|
|
func TestListCodesUsesSignedGetWithSnakeQuery(t *testing.T) {
|
|
server := signedTestServer(t, http.MethodGet, "/admin/v1/im/registration-invite-codes", func(t *testing.T, r *http.Request, body []byte) {
|
|
if len(body) != 0 {
|
|
t.Fatalf("GET list should not send body, got %q", string(body))
|
|
}
|
|
query := r.URL.Query()
|
|
if query.Get("page") != "2" || query.Get("per_page") != "20" || query.Get("batch_id") != "123" {
|
|
t.Fatalf("unexpected query: %s", r.URL.RawQuery)
|
|
}
|
|
})
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseUrl: server.URL, ServiceId: "hotgo-admin", ServiceSecret: "secret", Timeout: time.Second})
|
|
var out Page[struct{}]
|
|
err := client.ListCodes(context.Background(), AdminScope{TenantId: 9, OperatorId: 7, RequestId: "req-1"}, testInviteListRequest{Page: 2, PerPage: 20, BatchID: 123}, &out)
|
|
if err != nil {
|
|
t.Fatalf("ListCodes returned error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestListCodesWithInviteCodeUsesSignedSearchPost(t *testing.T) {
|
|
server := signedTestServer(t, http.MethodPost, "/admin/v1/im/registration-invite-codes/search", func(t *testing.T, r *http.Request, body []byte) {
|
|
if !strings.Contains(string(body), `"invite_code":"Ab123"`) {
|
|
t.Fatalf("POST search should send snake_case invite code, got %s", string(body))
|
|
}
|
|
})
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseUrl: server.URL, ServiceId: "hotgo-admin", ServiceSecret: "secret", Timeout: time.Second})
|
|
var out Page[struct{}]
|
|
err := client.ListCodes(context.Background(), AdminScope{TenantId: 9, OperatorId: 7, RequestId: "req-2"}, testInviteListRequest{Page: 1, PerPage: 20, InviteCode: "Ab123"}, &out)
|
|
if err != nil {
|
|
t.Fatalf("ListCodes returned error: %v", err)
|
|
}
|
|
}
|
|
|
|
func signedTestServer(t *testing.T, method string, path string, inspect func(t *testing.T, r *http.Request, body []byte)) *httptest.Server {
|
|
t.Helper()
|
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != method {
|
|
t.Fatalf("method = %s, want %s", r.Method, method)
|
|
}
|
|
if r.URL.Path != path {
|
|
t.Fatalf("path = %s, want %s", r.URL.Path, path)
|
|
}
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Fatalf("read body: %v", err)
|
|
}
|
|
inspect(t, r, body)
|
|
assertSignedRequest(t, r, body, "secret")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"data":{"items":[],"page":1,"per_page":20,"total":0}}`))
|
|
}))
|
|
}
|
|
|
|
func assertSignedRequest(t *testing.T, r *http.Request, body []byte, secret string) {
|
|
t.Helper()
|
|
for _, name := range []string{"Authorization", "X-IM-Service-ID", "X-IM-Tenant-ID", "X-IM-Operator-ID", "X-IM-Timestamp", "X-IM-Nonce", "X-Request-ID"} {
|
|
if r.Header.Get(name) == "" {
|
|
t.Fatalf("missing header %s", name)
|
|
}
|
|
}
|
|
if _, err := strconv.ParseInt(r.Header.Get("X-IM-Timestamp"), 10, 64); err != nil {
|
|
t.Fatalf("timestamp is not unix seconds: %v", err)
|
|
}
|
|
bodyHash := sha256.Sum256(body)
|
|
canonical := strings.Join([]string{
|
|
r.Method,
|
|
r.URL.RequestURI(),
|
|
r.Header.Get("X-IM-Service-ID"),
|
|
r.Header.Get("X-IM-Tenant-ID"),
|
|
r.Header.Get("X-IM-Operator-ID"),
|
|
r.Header.Get("X-IM-Timestamp"),
|
|
r.Header.Get("X-IM-Nonce"),
|
|
r.Header.Get("X-Request-ID"),
|
|
hex.EncodeToString(bodyHash[:]),
|
|
}, "\n")
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
|
_, _ = mac.Write([]byte(canonical))
|
|
expected := fmt.Sprintf("IM-HMAC %s", hex.EncodeToString(mac.Sum(nil)))
|
|
if r.Header.Get("Authorization") != expected {
|
|
t.Fatalf("bad signature\nwant: %s\n got: %s", expected, r.Header.Get("Authorization"))
|
|
}
|
|
}
|