Files
yutu/pkg/liveStream/liveStream_test.go

490 lines
11 KiB
Go

// Copyright 2026 eat-pray-ai & OpenWaygate
// SPDX-License-Identifier: Apache-2.0
package liveStream
import (
"bytes"
"encoding/json/v2"
"io"
"math"
"net/http"
"reflect"
"testing"
"github.com/eat-pray-ai/yutu/pkg/common"
"google.golang.org/api/youtube/v3"
)
func TestNewLiveStream(t *testing.T) {
type args struct {
opts []Option
}
svc := &youtube.Service{}
mine := true
tests := []struct {
name string
args args
want ILiveStream[youtube.LiveStream]
}{
{
name: "with all options",
args: args{
opts: []Option{
WithIds([]string{"stream1", "stream2"}),
WithTitle("Test Stream"),
WithDescription(new("A test stream")),
WithMine(&mine),
WithFrameRate("60fps"),
WithIngestionType("rtmp"),
WithResolution("1080p"),
WithMaxResults(50),
WithParts([]string{"snippet", "cdn", "status"}),
WithOutput("json"),
WithService(svc),
WithOnBehalfOfContentOwner("owner123"),
WithOnBehalfOfContentOwnerChannel("channel123"),
},
},
want: &LiveStream{
Service: svc,
Parts: []string{"snippet", "cdn", "status"},
Output: "json",
MaxResults: 50,
Ids: []string{"stream1", "stream2"},
OnBehalfOfContentOwner: "owner123",
Title: "Test Stream",
Description: new("A test stream"),
Mine: &mine,
FrameRate: "60fps",
IngestionType: "rtmp",
Resolution: "1080p",
OnBehalfOfContentOwnerChannel: "channel123",
},
},
{
name: "with no options",
args: args{
opts: []Option{},
},
want: &LiveStream{Fields: common.Fields{}},
},
{
name: "with zero max results",
args: args{
opts: []Option{
WithMaxResults(0),
},
},
want: &LiveStream{
MaxResults: math.MaxInt64,
},
},
{
name: "with negative max results",
args: args{
opts: []Option{
WithMaxResults(-10),
},
},
want: &LiveStream{
MaxResults: 1,
},
},
{
name: "with empty string values",
args: args{
opts: []Option{
WithTitle(""),
WithDescription(new("")),
WithFrameRate(""),
WithIngestionType(""),
WithResolution(""),
},
},
want: &LiveStream{
Fields: common.Fields{},
Title: "",
Description: new(""),
FrameRate: "",
IngestionType: "",
Resolution: "",
},
},
{
name: "with partial options",
args: args{
opts: []Option{
WithTitle("My Stream"),
WithIngestionType("rtmp"),
WithMaxResults(25),
WithParts([]string{"snippet"}),
},
},
want: &LiveStream{
Parts: []string{"snippet"},
MaxResults: 25,
Title: "My Stream",
IngestionType: "rtmp",
},
},
}
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
if got := NewLiveStream(tt.args.opts...); !reflect.DeepEqual(
got, tt.want,
) {
t.Errorf("%s\nNewLiveStream() = %v\nwant %v", tt.name, got, tt.want)
}
},
)
}
}
func TestLiveStream_Get(t *testing.T) {
tests := []struct {
name string
opts []Option
verify func(*http.Request)
wantLen int
wantErr bool
}{
{
name: "get live streams by id",
opts: []Option{
WithIds([]string{"stream-1"}),
WithMaxResults(1),
},
verify: func(r *http.Request) {
q := r.URL.Query()
if q.Get("id") != "stream-1" {
t.Errorf(
"expected id=stream-1, got %s",
q.Get("id"),
)
}
},
wantLen: 1,
wantErr: false,
},
{
name: "get live streams with mine",
opts: func() []Option {
mine := true
return []Option{
WithMine(&mine),
WithMaxResults(1),
}
}(),
verify: func(r *http.Request) {
q := r.URL.Query()
if q.Get("mine") != "true" {
t.Errorf(
"expected mine=true, got %s",
q.Get("mine"),
)
}
},
wantLen: 1,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "stream-1",
"snippet": {
"title": "Test Stream",
"description": "A test stream"
},
"status": {
"streamStatus": "active"
}
}
]
}`),
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewLiveStream(opts...)
got, err := s.Get()
if (err != nil) != tt.wantErr {
t.Errorf("LiveStream.Get() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.wantLen {
t.Errorf(
"LiveStream.Get() got length = %v, want %v", len(got), tt.wantLen,
)
}
},
)
}
}
func TestLiveStream_Get_Pagination(t *testing.T) {
svc := common.NewTestService(t, common.PaginationHandler("stream"))
mine := true
s := NewLiveStream(
WithService(svc),
WithMine(&mine),
WithMaxResults(22),
)
got, err := s.Get()
if err != nil {
t.Errorf("LiveStream.Get() error = %v", err)
}
if len(got) != 22 {
t.Errorf("LiveStream.Get() got length = %v, want 22", len(got))
}
}
func TestLiveStream_List(t *testing.T) {
mockResponse := `{
"items": [
{
"id": "stream-1",
"snippet": {
"title": "Test Stream",
"description": "A test stream"
},
"status": {
"streamStatus": "active"
}
}
]
}`
mine := true
common.RunListTest(
t, mockResponse,
func(svc *youtube.Service, output string) func(io.Writer) error {
s := NewLiveStream(
WithService(svc),
WithMine(&mine),
WithOutput(output),
WithMaxResults(1),
)
return s.List
},
)
}
func TestLiveStream_Insert(t *testing.T) {
tests := []struct {
name string
opts []Option
verify func(*http.Request)
wantErr bool
}{
{
name: "insert live stream",
opts: []Option{
WithTitle("New Stream"),
WithDescription(new("A new live stream")),
WithFrameRate("60fps"),
WithIngestionType("rtmp"),
WithResolution("1080p"),
WithParts([]string{"snippet", "cdn"}),
},
verify: func(r *http.Request) {
if r.Method != "POST" {
t.Errorf("expected POST, got %s", r.Method)
}
defer func() { _ = r.Body.Close() }()
var body struct {
Snippet struct {
Title string `json:"title"`
Description string `json:"description"`
} `json:"snippet"`
Cdn struct {
FrameRate string `json:"frameRate"`
IngestionType string `json:"ingestionType"`
Resolution string `json:"resolution"`
} `json:"cdn"`
}
if err := json.UnmarshalRead(r.Body, &body); err != nil {
t.Fatalf("failed to decode request body: %v", err)
}
if body.Snippet.Title != "New Stream" {
t.Errorf(
"expected snippet.title=New Stream, got %s", body.Snippet.Title,
)
}
if body.Snippet.Description != "A new live stream" {
t.Errorf(
"expected snippet.description=A new live stream, got %s",
body.Snippet.Description,
)
}
if body.Cdn.FrameRate != "60fps" {
t.Errorf("expected cdn.frameRate=60fps, got %s", body.Cdn.FrameRate)
}
if body.Cdn.IngestionType != "rtmp" {
t.Errorf(
"expected cdn.ingestionType=rtmp, got %s", body.Cdn.IngestionType,
)
}
if body.Cdn.Resolution != "1080p" {
t.Errorf("expected cdn.resolution=1080p, got %s", body.Cdn.Resolution)
}
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-stream-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewLiveStream(opts...)
var buf bytes.Buffer
if err := s.Insert(&buf); (err != nil) != tt.wantErr {
t.Errorf(
"LiveStream.Insert() error = %v, wantErr %v", err, tt.wantErr,
)
}
},
)
}
}
func TestLiveStream_Update(t *testing.T) {
tests := []struct {
name string
opts []Option
wantErr bool
}{
{
name: "update live stream",
opts: []Option{
WithIds([]string{"stream-1"}),
WithTitle("Updated Stream"),
WithDescription(new("Updated description")),
WithMaxResults(1),
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
callCount := 0
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
callCount++
if callCount == 1 {
_, _ = w.Write(
[]byte(`{
"items": [{
"id": "stream-1",
"snippet": {"title": "Old Stream", "description": "Old description"},
"cdn": {"frameRate": "30fps", "ingestionType": "rtmp", "resolution": "720p"}
}]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "stream-1"}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewLiveStream(opts...)
var buf bytes.Buffer
if err := s.Update(&buf); (err != nil) != tt.wantErr {
t.Errorf(
"LiveStream.Update() error = %v, wantErr %v", err, tt.wantErr,
)
}
},
)
}
}
func TestLiveStream_Delete(t *testing.T) {
tests := []struct {
name string
opts []Option
verify func(*http.Request)
wantErr bool
}{
{
name: "delete live stream",
opts: []Option{
WithIds([]string{"stream-id"}),
},
verify: func(r *http.Request) {
if r.Method != "DELETE" {
t.Errorf("expected DELETE, got %s", r.Method)
}
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewLiveStream(opts...)
var buf bytes.Buffer
if err := s.Delete(&buf); (err != nil) != tt.wantErr {
t.Errorf(
"LiveStream.Delete() error = %v, wantErr %v", err, tt.wantErr,
)
}
},
)
}
}