Files
olons_server/internal/imclient/client_test.go
T
richjohnrambo 74785f948b
Deploy olons-server production / deploy (push) Successful in 2m48s
fix: use personal login for default IM tenant
2026-10-19 13:25:32 +08:00

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[:])
}