131 lines
4.8 KiB
Go
131 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"net/http/httptest"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/tinode/chat/server/store/types"
|
|
)
|
|
|
|
func TestLoginAuditAccountDoesNotTreatTokenAsAccount(t *testing.T) {
|
|
kind, account := loginAuditAccount("token", "secret-token", "")
|
|
if kind != "" || account != "" {
|
|
t.Fatalf("token must not be persisted as an account: kind=%q account=%q", kind, account)
|
|
}
|
|
|
|
kind, account = loginAuditAccount("basic", "13800138000", "")
|
|
if kind != "phone" || account != "13800138000" {
|
|
t.Fatalf("unexpected basic account classification: kind=%q account=%q", kind, account)
|
|
}
|
|
}
|
|
|
|
func TestMaskAndHashLoginAccount(t *testing.T) {
|
|
tests := map[string]string{
|
|
"13800138000": "138****8000",
|
|
"alice@example.com": "a***@example.com",
|
|
"alice": "a***e",
|
|
"ab": "**",
|
|
}
|
|
for input, want := range tests {
|
|
if got := maskLoginAccount(input); got != want {
|
|
t.Errorf("maskLoginAccount(%q)=%q want %q", input, got, want)
|
|
}
|
|
}
|
|
hash := hashLoginAccount(" Alice ")
|
|
if len(hash) != 64 || hash != hashLoginAccount("alice") || strings.Contains(hash, "alice") {
|
|
t.Fatalf("unexpected account hash %q", hash)
|
|
}
|
|
}
|
|
|
|
func TestClassifyLoginFailure(t *testing.T) {
|
|
tests := []struct {
|
|
scheme string
|
|
err error
|
|
want string
|
|
}{
|
|
{"basic", types.ErrFailed, "password_error"},
|
|
{"sms_code", types.ErrFailed, "code_error"},
|
|
{"basic", types.ErrUserNotFound, "account_not_found"},
|
|
{"basic", types.ErrPermissionDenied, "account_disabled"},
|
|
{"basic", types.ErrPolicy, "risk_blocked"},
|
|
{"basic", errors.New("too many attempts"), "rate_limited"},
|
|
}
|
|
for _, tc := range tests {
|
|
got, _, _, _ := classifyLoginFailure(tc.scheme, tc.err)
|
|
if got != tc.want {
|
|
t.Errorf("classifyLoginFailure(%q,%v)=%q want %q", tc.scheme, tc.err, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMessageQuerySpecsExposeAdminFields(t *testing.T) {
|
|
privateSpec := privateMessagesInternalQuerySpec()
|
|
groupSpec := groupMessagesInternalQuerySpec()
|
|
for label, spec := range map[string]*internalQuerySpec{"private": privateSpec, "group": groupSpec} {
|
|
joined := strings.ToLower(spec.Select + " " + spec.From)
|
|
for _, field := range []string{"from_user_id", "content_type", "content_excerpt", "status", "im_message_admin_actions"} {
|
|
if !strings.Contains(joined, field) {
|
|
t.Errorf("%s query missing %s", label, field)
|
|
}
|
|
}
|
|
}
|
|
if strings.Contains(strings.ToLower(privateSpec.Select), "m.content as content") || strings.Contains(strings.ToLower(groupSpec.Select), "m.content as content") {
|
|
t.Fatal("message list query must not expose full message content")
|
|
}
|
|
}
|
|
|
|
func TestGroupMessageQuerySupportsOperationalFilters(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/?seq_id=42&sender_username=alice&sender_nickname=%E5%B0%8F%E6%98%8E&group_name=%E6%B5%8B%E8%AF%95%E7%BE%A4", nil)
|
|
query, _, args, _, _, err := buildInternalAdminListQuery(req, groupMessagesInternalQuerySpec(), "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, clause := range []string{"m.seqid = ?", "u.username LIKE ?", "p.nickname LIKE ?", "gs.name LIKE ?"} {
|
|
if !strings.Contains(query, clause) {
|
|
t.Errorf("group message query missing %q: %s", clause, query)
|
|
}
|
|
}
|
|
want := []any{int64(42), "%alice%", "%小明%", "%测试群%"}
|
|
if !reflect.DeepEqual(args, want) {
|
|
t.Fatalf("unexpected filter arguments: got %#v, want %#v", args, want)
|
|
}
|
|
}
|
|
|
|
func TestUserQuerySupportsKeywordAndUserIDs(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/?keyword=alice&user_ids=101,202", nil)
|
|
query, countQuery, args, _, _, err := buildInternalAdminListQuery(req, usersInternalQuerySpec(), "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, clause := range []string{"CONCAT_WS(' ',u.username,p.nickname,u.phone) LIKE ?", "u.id IN (?,?)"} {
|
|
if !strings.Contains(query, clause) || !strings.Contains(countQuery, clause) {
|
|
t.Fatalf("user query missing %q: %s", clause, query)
|
|
}
|
|
}
|
|
want := []any{int64(101), int64(202), "%alice%"}
|
|
if !reflect.DeepEqual(args, want) {
|
|
t.Fatalf("unexpected filter arguments: got %#v, want %#v", args, want)
|
|
}
|
|
for _, field := range []string{"AS avatar", "AS register_type", "im_registration_invite_uses", "im_fileuploads AS avatar"} {
|
|
if !strings.Contains(usersInternalQuerySpec().Select+usersInternalQuerySpec().From, field) {
|
|
t.Fatalf("user query missing list field %q", field)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRedactClientMessageForLog(t *testing.T) {
|
|
raw := []byte(`{"login":{"id":"1","scheme":"basic","secret":"YWxpY2U6YWxpY2UxMjM="},"acc":{"secret":"account-secret","tmpsecret":"temp-secret"}}`)
|
|
redacted := string(redactClientMessageForLog(raw))
|
|
for _, secret := range []string{"YWxpY2U6YWxpY2UxMjM=", "account-secret", "temp-secret"} {
|
|
if strings.Contains(redacted, secret) {
|
|
t.Fatalf("redacted log still contains secret %q: %s", secret, redacted)
|
|
}
|
|
}
|
|
if strings.Count(redacted, "[REDACTED]") != 3 {
|
|
t.Fatalf("unexpected redacted payload: %s", redacted)
|
|
}
|
|
}
|