package kook import ( "context" "net/http" "sync" "testing" ) func TestListFriends_ParsesAndSendsType(t *testing.T) { t.Parallel() var mu sync.Mutex var query string h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != friendPath { http.Error(w, "unexpected path: "+r.URL.Path, http.StatusNotFound) return } mu.Lock() query = r.URL.RawQuery mu.Unlock() kookOK(w, map[string]any{ "friend": []any{ map[string]any{"id": 1, "friend_info": map[string]any{"id": "u1", "username": "alice", "nickname": "Ali", "online": true}}, }, "request": []any{ map[string]any{"id": 77, "friend_info": map[string]any{"id": "u2", "username": "bob"}}, }, "blocked": []any{ map[string]any{"id": 2, "friend_info": map[string]any{"id": "u3", "username": "carol"}}, }, }) }) a, _ := newTestAdapter(t, h) fl, err := a.ListFriends(context.Background(), FriendTypeRequest) if err != nil { t.Fatalf("ListFriends: %v", err) } mu.Lock() defer mu.Unlock() if query != "type=request" { t.Errorf("query = %q, want type=request", query) } if len(fl.Friends) != 1 || fl.Friends[0].UserID != "u1" || !fl.Friends[0].Online || fl.Friends[0].Nickname != "Ali" { t.Errorf("friends = %+v, want [u1 Ali online]", fl.Friends) } if len(fl.Requests) != 1 || fl.Requests[0].ID != 77 || fl.Requests[0].Friend.UserID != "u2" { t.Errorf("requests = %+v, want [77 u2]", fl.Requests) } if len(fl.Blocked) != 1 || fl.Blocked[0].UserID != "u3" { t.Errorf("blocked = %+v, want [u3]", fl.Blocked) } } func TestHandleFriendRequest_Body(t *testing.T) { t.Parallel() var mu sync.Mutex var body map[string]any h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != friendHandlePath { http.Error(w, "unexpected path", http.StatusNotFound) return } mu.Lock() body = decodeBody(r) mu.Unlock() kookOK(w, map[string]any{}) }) a, _ := newTestAdapter(t, h) if err := a.HandleFriendRequest(context.Background(), 42, true); err != nil { t.Fatalf("HandleFriendRequest: %v", err) } mu.Lock() defer mu.Unlock() if body["id"] != float64(42) || body["accept"] != true { t.Errorf("body = %v, want id 42 accept true", body) } } func TestListUserChats(t *testing.T) { t.Parallel() h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != userChatListPath { http.Error(w, "unexpected path", http.StatusNotFound) return } kookOK(w, map[string]any{ "items": []any{ map[string]any{ "code": "c1", "last_read_time": 1, "latest_msg_time": 2, "unread_count": 3, "target_info": map[string]any{"id": "u9", "username": "zed"}, }, }, }) }) a, _ := newTestAdapter(t, h) chats, err := a.ListUserChats(context.Background(), 1, 50) if err != nil { t.Fatalf("ListUserChats: %v", err) } if len(chats) != 1 || chats[0].Code != "c1" || chats[0].TargetID != "u9" || chats[0].TargetName != "zed" { t.Errorf("chats = %+v, want [c1 u9 zed]", chats) } } func TestListGuildsAndChannels(t *testing.T) { t.Parallel() h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case guildListPath: kookOK(w, map[string]any{ "items": []any{map[string]any{"id": "g1", "name": "server", "icon": "i", "topic": "t"}}, }) case channelListPath: if r.URL.Query().Get("guild_id") != "g1" || r.URL.Query().Get("type") != "1" { http.Error(w, "missing guild/type", http.StatusBadRequest) return } kookOK(w, map[string]any{ "items": []any{map[string]any{"id": "c1", "guild_id": "g1", "name": "general", "type": 1, "is_category": false}}, }) default: http.Error(w, "unexpected path", http.StatusNotFound) } }) a, _ := newTestAdapter(t, h) guilds, err := a.ListGuilds(context.Background(), 1, 50) if err != nil { t.Fatalf("ListGuilds: %v", err) } if len(guilds) != 1 || guilds[0].ID != "g1" || guilds[0].Name != "server" { t.Fatalf("guilds = %+v, want [g1 server]", guilds) } channels, err := a.ListGuildChannels(context.Background(), "g1", 1) if err != nil { t.Fatalf("ListGuildChannels: %v", err) } if len(channels) != 1 || channels[0].ID != "c1" || channels[0].Name != "general" || channels[0].IsCategory { t.Errorf("channels = %+v, want [c1 general]", channels) } } func TestSendTestMessage(t *testing.T) { t.Parallel() var mu sync.Mutex var body map[string]any h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != guildChannelMsgPath { http.Error(w, "unexpected path: "+r.URL.Path, http.StatusNotFound) return } mu.Lock() body = decodeBody(r) mu.Unlock() kookOK(w, map[string]any{"msg_id": "m1", "msg_timestamp": 123}) }) a, _ := newTestAdapter(t, h) res, err := a.SendTestMessage(context.Background(), "chan-1", "hello") if err != nil { t.Fatalf("SendTestMessage: %v", err) } if res.MsgID != "m1" || res.MsgTimestamp != 123 { t.Errorf("res = %+v, want m1/123", res) } mu.Lock() defer mu.Unlock() if body["target_id"] != "chan-1" || body["content"] != "hello" { t.Errorf("body = %v, want target_id chan-1 content hello", body) } } func TestSendTestMessage_NoTarget(t *testing.T) { t.Parallel() a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "unexpected", http.StatusNotFound) })) if _, err := a.SendTestMessage(context.Background(), "", "hi"); err == nil { t.Fatal("expected error for empty target") } }