package livekit
import (
"context"
"encoding/base64"
"fmt"
"slices"
"testing"
"github.com/dennwc/iters"
"github.com/stretchr/testify/require"
proto "google.golang.org/protobuf/proto"
"gopkg.in/yaml.v3"
)
func TestUnmarshallRoomConfiguration(t *testing.T) {
y := `
a:
name: room_name
egress:
room:
room_name: room_name
agents:
- {}
- agent_name: ag
metadata: mm
min_playout_delay: 42
`
obj := make(map[string]*RoomConfiguration)
err := yaml.Unmarshal([]byte(y), &obj)
require.NoError(t, err)
require.Equal(t, 1, len(obj))
re := obj["a"]
require.NotNil(t, re)
require.Equal(t, re.Name, "room_name")
require.Equal(t, re.MinPlayoutDelay, uint32(42))
require.Equal(t, re.Egress.Room.RoomName, "room_name")
require.Equal(t, 2, len(re.Agents))
require.Equal(t, "ag", re.Agents[1].AgentName)
require.Equal(t, "mm", re.Agents[1].Metadata)
}
func TestMarshallRoomConfiguration(t *testing.T) {
r := &RoomConfiguration{
Name: "name",
MaxParticipants: 42,
EmptyTimeout: 12,
DepartureTimeout: 13,
MinPlayoutDelay: 14,
MaxPlayoutDelay: 15,
Egress: &RoomEgress{
Room: &RoomCompositeEgressRequest{
AudioOnly: true,
RoomName: "room name",
},
},
Agents: []*RoomAgentDispatch{
{
AgentName: "agent name",
},
},
}
b, err := yaml.Marshal(r)
require.NoError(t, err)
var ur RoomConfiguration
err = yaml.Unmarshal(b, &ur)
require.NoError(t, err)
require.True(t, proto.Equal(r, &ur))
}
func TestUnmarshallRoomEgress(t *testing.T) {
y := `
a:
room:
room_name: room name
b:
participant:
file_outputs:
- s3:
access_key: key
`
obj := make(map[string]*RoomEgress)
err := yaml.Unmarshal([]byte(y), &obj)
require.NoError(t, err)
require.Equal(t, 2, len(obj))
re := obj["a"]
require.NotNil(t, re)
require.Equal(t, re.Room.RoomName, "room name")
re = obj["b"]
require.NotNil(t, re)
require.Equal(t, 1, len(re.Participant.FileOutputs))
require.Equal(t, "key", re.Participant.FileOutputs[0].Output.(*EncodedFileOutput_S3).S3.AccessKey)
}
func TestMarshallRoomEgress(t *testing.T) {
e := &RoomEgress{
Room: &RoomCompositeEgressRequest{
AudioOnly: true,
RoomName: "room name",
},
}
b, err := yaml.Marshal(e)
require.NoError(t, err)
var ue RoomEgress
err = yaml.Unmarshal(b, &ue)
require.NoError(t, err)
require.True(t, proto.Equal(e, &ue))
}
func TestUnmarshallRoomAgent(t *testing.T) {
y := `
a:
dispatches:
- {}
- agent_name: ag
metadata: mm
`
obj := make(map[string]*RoomAgent)
err := yaml.Unmarshal([]byte(y), &obj)
require.NoError(t, err)
require.Equal(t, 1, len(obj))
re := obj["a"]
require.NotNil(t, re)
require.Equal(t, len(re.Dispatches), 2)
require.Equal(t, "ag", re.Dispatches[1].AgentName)
require.Equal(t, "mm", re.Dispatches[1].Metadata)
}
func TestMarshallRoomAgent(t *testing.T) {
a := &RoomAgent{
Dispatches: []*RoomAgentDispatch{
&RoomAgentDispatch{
AgentName: "agent name",
},
},
}
b, err := yaml.Marshal(a)
require.NoError(t, err)
var ua RoomAgent
err = yaml.Unmarshal(b, &ua)
require.NoError(t, err)
require.True(t, proto.Equal(a, &ua))
}
var _ PageItem = testPageItem("")
type testPageItem string
func (t testPageItem) ID() string {
return string(t)
}
type testPageReq struct {
Page *Pagination
ReturnEmpty bool
}
func (t *testPageReq) GetPage() *Pagination {
return t.Page
}
func (t *testPageReq) Filter(v testPageItem) bool {
return true
}
type testPageResp []testPageItem
func (t testPageResp) GetItems() []testPageItem {
return t
}
func TestListPageIter(t *testing.T) {
const page = 3
var (
all []testPageItem
)
for i := 0; i < 10; i++ {
all = append(all, testPageItem(fmt.Sprintf("%03d", i)))
}
pageFunc := func(ctx context.Context, req *testPageReq) (testPageResp, error) {
limit := -1
if req.Page != nil {
limit = int(req.Page.Limit)
if limit == 0 {
limit = page
}
}
if req.ReturnEmpty {
return make(testPageResp, page), nil
}
var out testPageResp
for _, v := range all {
if req.Filter(v) && req.Page.Filter(v) {
out = append(out, v)
if limit > 0 && len(out) >= limit {
break
}
}
}
return out, nil
}
exp := all
testList := func(t testing.TB, req *testPageReq) {
it := ListPageIter(pageFunc, req)
got, err := iters.AllPages(context.Background(), it)
require.NoError(t, err)
require.Equal(t, exp, got)
}
t.Run("legacy", func(t *testing.T) {
testList(t, &testPageReq{})
})
t.Run("pagination", func(t *testing.T) {
testList(t, &testPageReq{Page: &Pagination{}})
})
t.Run("limit", func(t *testing.T) {
testList(t, &testPageReq{Page: &Pagination{Limit: 5}})
})
t.Run("no ids", func(t *testing.T) {
it := ListPageIter(pageFunc, &testPageReq{Page: &Pagination{}, ReturnEmpty: true})
got, err := iters.AllPages(context.Background(), it)
require.NoError(t, err)
require.Equal(t, []testPageItem(nil), got)
})
}
func TestListUpdate(t *testing.T) {
var cases = []struct {
Name string
Arr []string
Update *ListUpdate
Exp []string
Err bool
}{
{
Name: "empty update",
Update: &ListUpdate{},
Err: true,
},
{
Name: "clear and set",
Update: &ListUpdate{Clear: true, Set: []string{"a"}},
Err: true,
},
{
Name: "clear and add",
Update: &ListUpdate{Clear: true, Add: []string{"a"}},
Err: true,
},
{
Name: "set and add",
Update: &ListUpdate{Set: []string{"a"}, Add: []string{"b"}},
Err: true,
},
{
Name: "set and remove",
Update: &ListUpdate{Set: []string{"a"}, Remove: []string{"b"}},
Err: true,
},
{
Name: "clear",
Arr: []string{"a", "b"},
Update: &ListUpdate{Clear: true},
Exp: nil,
},
{
Name: "set",
Arr: []string{"a"},
Update: &ListUpdate{Set: []string{"b"}},
Exp: []string{"b"},
},
{
Name: "add",
Arr: []string{"a", "b"},
Update: &ListUpdate{Add: []string{"b", "c"}},
Exp: []string{"a", "b", "c"},
},
{
Name: "remove",
Arr: []string{"a", "b"},
Update: &ListUpdate{Remove: []string{"b", "c"}},
Exp: []string{"a"},
},
{
Name: "add and remove",
Arr: []string{"a", "b", "c"},
Update: &ListUpdate{Add: []string{"b", "d"}, Remove: []string{"c", "e"}},
Exp: []string{"a", "b", "d"},
},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
prev := slices.Clone(c.Arr)
out, err := c.Update.Apply(c.Arr)
if c.Err {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, c.Exp, out)
require.Equal(t, prev, c.Arr)
})
}
}
func TestEncodeTokenPagination(t *testing.T) {
t.Run("encode and decode", func(t *testing.T) {
offset := int32(10)
limit := int32(50)
tokenPagination, err := EncodeTokenPagination(offset, limit)
require.NoError(t, err)
require.NotNil(t, tokenPagination)
require.NotEmpty(t, tokenPagination.Token)
decodedOffset, decodedLimit, err := DecodeTokenPagination(tokenPagination)
require.NoError(t, err)
require.Equal(t, offset, decodedOffset)
require.Equal(t, limit, decodedLimit)
})
t.Run("encode zero values", func(t *testing.T) {
tokenPagination, err := EncodeTokenPagination(0, 0)
require.NoError(t, err)
require.NotNil(t, tokenPagination)
require.NotEmpty(t, tokenPagination.Token)
decodedOffset, decodedLimit, err := DecodeTokenPagination(tokenPagination)
require.NoError(t, err)
require.Equal(t, int32(0), decodedOffset)
require.Equal(t, int32(0), decodedLimit)
})
t.Run("decode nil token pagination", func(t *testing.T) {
offset, limit, err := DecodeTokenPagination(nil)
require.NoError(t, err)
require.Equal(t, int32(0), offset)
require.Equal(t, int32(0), limit)
})
t.Run("decode empty token", func(t *testing.T) {
tp := &TokenPagination{Token: ""}
offset, limit, err := DecodeTokenPagination(tp)
require.NoError(t, err)
require.Equal(t, int32(0), offset)
require.Equal(t, int32(0), limit)
})
t.Run("decode invalid token", func(t *testing.T) {
tp := &TokenPagination{Token: "invalid-token"}
_, _, err := DecodeTokenPagination(tp)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to decode token")
})
t.Run("decode invalid json", func(t *testing.T) {
invalidJSON := base64.URLEncoding.EncodeToString([]byte("not json"))
tp := &TokenPagination{Token: invalidJSON}
_, _, err := DecodeTokenPagination(tp)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to unmarshal")
})
}