fossteams-api/client_test.go
copilot-swe-agent[bot] 631c192034
feat: add Bun JS port
2026-06-04 13:44:27 +00:00

136 lines
5.9 KiB
Go

package teams_api
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/dgrijalva/jwt-go"
api "github.com/fossteams/teams-api/pkg"
"github.com/fossteams/teams-api/pkg/csa"
"github.com/fossteams/teams-api/pkg/mt"
)
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func TestTeamsClientMethods(t *testing.T) {
defaultClient := http.DefaultClient
t.Cleanup(func() { http.DefaultClient = defaultClient })
http.DefaultClient = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
switch req.URL.String() {
case csa.ChatSvcAgg + "v1/teams/users/me?enableMembershipSummary=true&isPrefetch=false":
return response(http.StatusOK, loadFixture(t, "resources/chatsvcagg/conversations/conversations-1.json")), nil
case csa.ChatSvcAgg + "v1/teams/users/me/pinnedChannels":
return response(http.StatusOK, `{"OrderVersion":1,"PinChannelOrder":["19:one"]}`), nil
case csa.MessagesHost + "v1/users/ME/conversations/19:10dd444580a348eea3a3a335035aee3d@thread.tacv2/messages?pageSize=200&startTime=1&view=msnp24Equivalent%7CsupportsMessageProperties":
return response(http.StatusOK, loadFixture(t, "resources/chatsvcagg/messages/messages-1.json")), nil
case mt.MiddleTier + "emea/beta/users/user@example.com/?enableGuest=true&includeIBBarredUsers=true&isMailAddress=true&skypeTeamsInfo=true&throwIfNotFound=false":
return response(http.StatusOK, loadFixture(t, "resources/mt/user/user-1.json")), nil
case mt.MiddleTier + "emea/beta/users/fetchShortProfile?enableGuest=true&includeIBBarredUsers=false&isMailAddress=false&skypeTeamsInfo=true":
return response(http.StatusOK, `{"value":[{"displayName":"Denys Vitali","email":"teams-cli@outlook.com","givenName":"Denys","surname":"Vitali","isShortProfile":true,"jobTitle":"","objectId":"fa814989-41d0-4d4b-a365-e5f44e406847","tenantName":"FossTeams","type":"ADUser","userLocation":"Remote","userPrincipalName":"teams-cli@outlook.com"}],"type":"Users"}`), nil
case mt.MiddleTier + "emea/beta/users/user@example.com/profilepicture?displayname=aaa":
return response(http.StatusOK, "anBlZy1ieXRlcw=="), nil
case mt.MiddleTier + "emea/beta/teams/user@example.com/profilepicturev2":
return response(http.StatusOK, "jpeg-bytes"), nil
case mt.MiddleTier + "emea/beta/users/tenants":
return response(http.StatusOK, loadFixture(t, "resources/mt/tenants/tenants-1.json")), nil
default:
t.Fatalf("unexpected request: %s", req.URL.String())
return nil, nil
}
})}
chatSvc, err := csa.NewCSAService(
&api.TeamsToken{Inner: mustParseJWT(t, "csa-token", map[string]any{"email": "user@example.com"}), Type: api.TokenBearer},
&api.SkypeToken{Inner: mustParseJWT(t, "skype-token", map[string]any{"email": "user@example.com"}), Type: api.TokenSkype},
)
if err != nil {
t.Fatalf("unable to create chat service: %v", err)
}
mtSvc, err := mt.NewMiddleTierService(
api.Emea,
&api.TeamsToken{Inner: mustParseJWT(t, "root-token", map[string]any{"email": "user@example.com"}), Type: api.TokenBearer},
&api.TeamsToken{Inner: mustParseJWT(t, "teams-token", map[string]any{"email": "user@example.com"}), Type: api.TokenBearer},
)
if err != nil {
t.Fatalf("unable to create MT service: %v", err)
}
client := &TeamsClient{chatSvc: chatSvc, mtSvc: mtSvc}
client.Debug(true)
conversations, err := client.GetConversations()
if err != nil || len(conversations.Teams) != 1 {
t.Fatalf("unexpected conversations result: %#v %v", conversations, err)
}
messages, err := client.GetMessages(&conversations.Teams[0].Channels[0])
if err != nil || len(messages) != 2 {
t.Fatalf("unexpected messages result: %#v %v", messages, err)
}
me, err := client.GetMe()
if err != nil || me.Email != "teams-cli@outlook.com" {
t.Fatalf("unexpected me result: %#v %v", me, err)
}
shortProfiles, err := client.FetchShortProfile([]string{"8:orgid:fa814989-41d0-4d4b-a365-e5f44e406847"})
if err != nil || len(shortProfiles) != 1 {
t.Fatalf("unexpected short profiles: %#v %v", shortProfiles, err)
}
profilePicture, err := client.GetProfilePicture("user@example.com")
if err != nil || string(profilePicture) != "jpeg-bytes" {
t.Fatalf("unexpected profile picture: %q %v", string(profilePicture), err)
}
teamsProfilePicture, err := client.GetTeamsProfilePicture("user@example.com")
if err != nil || string(teamsProfilePicture) != "jpeg-bytes" {
t.Fatalf("unexpected teams profile picture: %q %v", string(teamsProfilePicture), err)
}
tenants, err := client.GetTenants()
if err != nil || len(tenants) != 1 {
t.Fatalf("unexpected tenants result: %#v %v", tenants, err)
}
pinnedChannels, err := client.GetPinnedChannels()
if err != nil || len(pinnedChannels) != 1 || pinnedChannels[0] != "19:one" {
t.Fatalf("unexpected pinned channels: %#v %v", pinnedChannels, err)
}
if client.ChatSvc() == nil {
t.Fatal("expected chat service")
}
}
func response(status int, body string) *http.Response {
return &http.Response{StatusCode: status, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(body))}
}
func loadFixture(t *testing.T, relativePath string) string {
t.Helper()
_, filename, _, _ := runtime.Caller(0)
root := filepath.Clean(filepath.Join(filepath.Dir(filename)))
body, err := os.ReadFile(filepath.Join(root, relativePath))
if err != nil {
t.Fatalf("unable to read fixture %s: %v", relativePath, err)
}
return string(body)
}
func mustParseJWT(t *testing.T, raw string, claims map[string]any) *jwt.Token {
t.Helper()
encodedClaims, err := json.Marshal(claims)
if err != nil {
t.Fatalf("unable to marshal claims: %v", err)
}
encoded := fmt.Sprintf("%s.%s.signature", base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none"}`)), base64.RawURLEncoding.EncodeToString(encodedClaims))
if raw == "" {
raw = encoded
}
return &jwt.Token{Raw: raw, Claims: jwt.MapClaims(claims)}
}