185 lines
7.0 KiB
Go
185 lines
7.0 KiB
Go
package imclient
|
|
|
|
import (
|
|
"context"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.118im.com/im_team/olons_server/internal/config"
|
|
"github.com/gorilla/websocket"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func TestCreateUserAcceptsNumericIDAndSignsRequest(t *testing.T) {
|
|
const secret = "internal-secret"
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
body, _ := io.ReadAll(r.Body)
|
|
if r.URL.Path != "/internal/admin-query/tenants/allrich/users" {
|
|
t.Fatalf("unexpected path %s", r.URL.Path)
|
|
}
|
|
if r.Header.Get("X-Service-Name") != "olons-server" {
|
|
t.Fatalf("missing service header")
|
|
}
|
|
canonical := strings.Join([]string{r.Method, r.URL.RequestURI(), hashHex(body),
|
|
r.Header.Get("X-Timestamp"), r.Header.Get("X-Nonce")}, "\n")
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
|
_, _ = mac.Write([]byte(canonical))
|
|
if r.Header.Get("X-Signature") != hex.EncodeToString(mac.Sum(nil)) {
|
|
t.Fatalf("invalid signature")
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"code":0,"data":{"user_id":42,"username":"allrich_m_7","nickname":"会员7"}}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := New(config.IMConfig{BaseURL: server.URL, HTTPAPIURL: server.URL, TenantCode: "allrich",
|
|
APIKey: "public-key", InternalService: "olons-server", InternalSecret: secret, Timeout: time.Second})
|
|
created, err := client.CreateUser(context.Background(), "allrich_m_7", "会员7", "password")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := string(created.UserID); got != "42" {
|
|
t.Fatalf("user id = %q, want 42", got)
|
|
}
|
|
}
|
|
|
|
func TestProductRequestHeaders(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("Authorization") != "Bearer user-token" || r.Header.Get("X-MeetingIM-APIKey") != "public-key" {
|
|
t.Fatalf("missing product API credentials")
|
|
}
|
|
if r.Header.Get("X-IM-Schema") != "1" {
|
|
t.Fatalf("missing schema header")
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"code":0,"data":{"user_id":"usr_agent","relationship":"friend","topic":"p2p123","capabilities":{"can_send":true}}}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := New(config.IMConfig{BaseURL: server.URL, HTTPAPIURL: server.URL, TenantCode: "allrich",
|
|
APIKey: "public-key", InternalService: "olons-server", InternalSecret: "secret", Timeout: time.Second})
|
|
profile, err := client.UserProfile(context.Background(), "user-token", "usr_agent")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if profile.Relationship != "friend" || !profile.Capabilities.CanSend {
|
|
t.Fatalf("unexpected profile: %+v", profile)
|
|
}
|
|
}
|
|
|
|
func TestLoginBasicUsesExistingWebSocketProtocol(t *testing.T) {
|
|
upgrader := websocket.Upgrader{}
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Query().Get("apikey") != "public-key" {
|
|
t.Fatalf("missing API key")
|
|
}
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
var packet map[string]map[string]any
|
|
if err = conn.ReadJSON(&packet); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if packet["hi"]["tenant"] != "allrich" {
|
|
t.Fatalf("unexpected hi: %#v", packet)
|
|
}
|
|
_ = conn.WriteJSON(map[string]any{"ctrl": map[string]any{"id": packet["hi"]["id"], "code": 201, "text": "created"}})
|
|
if err = conn.ReadJSON(&packet); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
login := packet["login"]
|
|
secret, _ := base64.StdEncoding.DecodeString(login["secret"].(string))
|
|
if string(secret) != "member01:password" || login["login_type"] != float64(0) || login["tenant_code"] != "allrich" {
|
|
t.Fatalf("unexpected login: %#v", login)
|
|
}
|
|
_ = conn.WriteJSON(map[string]any{"ctrl": map[string]any{"id": login["id"], "code": 200, "text": "ok",
|
|
"params": map[string]any{"user": "usr_member", "token": "token", "expires": "2026-10-27T10:00:00Z"}}})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := New(config.IMConfig{BaseURL: server.URL, HTTPAPIURL: server.URL,
|
|
WebSocketURL: "ws" + strings.TrimPrefix(server.URL, "http"), TenantCode: "allrich",
|
|
APIKey: "public-key", InternalService: "olons-server", InternalSecret: "secret", Timeout: time.Second})
|
|
session, err := client.LoginBasic(context.Background(), "member01", "password", "web-1", "web")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if session.UserID != "usr_member" || session.Token != "token" {
|
|
t.Fatalf("unexpected session: %+v", session)
|
|
}
|
|
}
|
|
|
|
func TestLoginBasicUsesPersonalTypeForDefaultTenant(t *testing.T) {
|
|
upgrader := websocket.Upgrader{}
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
var packet map[string]map[string]any
|
|
if err = conn.ReadJSON(&packet); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = conn.WriteJSON(map[string]any{"ctrl": map[string]any{"id": packet["hi"]["id"], "code": 201, "text": "created"}})
|
|
if err = conn.ReadJSON(&packet); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
login := packet["login"]
|
|
if login["login_type"] != float64(1) || login["tenant_code"] != defaultIMTenantCode {
|
|
t.Fatalf("unexpected login: %#v", login)
|
|
}
|
|
_ = conn.WriteJSON(map[string]any{"ctrl": map[string]any{"id": login["id"], "code": 200, "text": "ok",
|
|
"params": map[string]any{"user": "usr_member", "token": "token", "expires": "2026-10-27T10:00:00Z"}}})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := New(config.IMConfig{BaseURL: server.URL, HTTPAPIURL: server.URL,
|
|
WebSocketURL: "ws" + strings.TrimPrefix(server.URL, "http"), TenantCode: defaultIMTenantCode,
|
|
APIKey: "public-key", InternalService: "olons-server", InternalSecret: "secret", Timeout: time.Second})
|
|
if _, err := client.LoginBasic(context.Background(), "member01", "password", "web-1", "web"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestResetPasswordSendsBase64BcryptHash(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var body struct {
|
|
EventType string `json:"event_type"`
|
|
PasswordHash string `json:"password_hash"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
hash, err := base64.StdEncoding.DecodeString(body.PasswordHash)
|
|
if body.EventType != "user.password.reset" || err != nil || bcrypt.CompareHashAndPassword(hash, []byte("new-password")) != nil {
|
|
t.Fatalf("unexpected reset body: %+v", body)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"code":0,"data":{"status":"succeeded"}}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := New(config.IMConfig{BaseURL: server.URL, HTTPAPIURL: server.URL, WebSocketURL: "ws://example.test",
|
|
TenantCode: "allrich", APIKey: "public-key", InternalService: "olons-server", InternalSecret: "secret", Timeout: time.Second})
|
|
if err := client.ResetPassword(context.Background(), "usr_member", "new-password"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func hashHex(body []byte) string {
|
|
digest := sha256.Sum256(body)
|
|
return hex.EncodeToString(digest[:])
|
|
}
|