1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00
FrankerFaceZ/socketserver/server/subscriptions_test.go
2017-09-01 16:15:04 -04:00

417 lines
12 KiB
Go

package server
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"sync"
"syscall"
"testing"
"time"
"github.com/FrankerFaceZ/FrankerFaceZ/socketserver/server/rate"
"github.com/gorilla/websocket"
"github.com/satori/go.uuid"
)
const TestOrigin = "http://www.twitch.tv"
func TestSubscriptionAndPublish(t *testing.T) {
var doneWg sync.WaitGroup
var readyWg sync.WaitGroup
const TestChannelName1 = "room.testchannel"
const TestChannelName2 = "room.chan2"
const TestChannelName3 = "room.chan3"
const TestChannelNameUnused = "room.empty"
const TestCommandChan = "testdata_single"
const TestCommandMulti = "testdata_multi"
const TestCommandGlobal = "testdata_global"
const TestData1 = "123456789"
const TestData2 = 42
const TestData3 = false
var TestData4 = []interface{}{"str1", "str2", "str3"}
t.Log("TestSubscriptionAndPublish")
var server *httptest.Server
var urls TURLs
var backendExpected = NewTBackendRequestChecker(t,
TExpectedBackendRequest{200, bPathAnnounceStartup, &url.Values{"startup": []string{"1"}}, "", nil},
TExpectedBackendRequest{200, bPathAddTopic, &url.Values{"channels": []string{TestChannelName1}, "added": []string{"t"}}, "ok", nil},
TExpectedBackendRequest{200, bPathAddTopic, &url.Values{"channels": []string{TestChannelName2}, "added": []string{"t"}}, "ok", nil},
TExpectedBackendRequest{200, bPathAddTopic, &url.Values{"channels": []string{TestChannelName3}, "added": []string{"t"}}, "ok", nil},
)
server, _, urls = TSetup(SetupWantSocketServer|SetupWantBackendServer|SetupWantURLs, backendExpected)
defer server.CloseClientConnections()
defer unsubscribeAllClients()
defer backendExpected.Close()
var conn *websocket.Conn
var resp *http.Response
var err error
var headers http.Header = make(http.Header)
headers.Set("Origin", TestOrigin)
// client 1: sub ch1, ch2
// client 2: sub ch1, ch3
// client 3: sub none
// client 4: delayed sub ch1
// msg 1: ch1
// msg 2: ch2, ch3
// msg 3: chEmpty
// msg 4: global uncached
// Client 1
conn, resp, err = websocket.DefaultDialer.Dial(urls.Websocket, headers)
if err != nil {
t.Error(err)
return
}
// both origins need testing
headers.Set("Origin", "https://www.twitch.tv")
doneWg.Add(1)
readyWg.Add(1)
go func(conn *websocket.Conn) {
TSendMessage(t, conn, 1, HelloCommand, []interface{}{"ffz_0.0-test", uuid.NewV4().String()})
TReceiveExpectedMessage(t, conn, 1, SuccessCommand, IgnoreReceivedArguments)
TSendMessage(t, conn, 2, "sub", TestChannelName1)
TReceiveExpectedMessage(t, conn, 2, SuccessCommand, nil)
TSendMessage(t, conn, 3, "sub", TestChannelName2) // 2
TReceiveExpectedMessage(t, conn, 3, SuccessCommand, nil)
TSendMessage(t, conn, 4, "ready", 0)
TReceiveExpectedMessage(t, conn, 4, SuccessCommand, nil)
readyWg.Done()
TReceiveExpectedMessage(t, conn, -1, TestCommandChan, TestData1)
TReceiveExpectedMessage(t, conn, -1, TestCommandMulti, TestData2)
TReceiveExpectedMessage(t, conn, -1, TestCommandGlobal, TestData4)
conn.Close()
doneWg.Done()
}(conn)
// Client 2
conn, resp, err = websocket.DefaultDialer.Dial(urls.Websocket, headers)
if err != nil {
t.Error(err)
return
}
doneWg.Add(1)
readyWg.Wait() // enforce ordering
readyWg.Add(1)
go func(conn *websocket.Conn) {
TSendMessage(t, conn, 1, HelloCommand, []interface{}{"ffz_0.0-test", uuid.NewV4().String()})
TReceiveExpectedMessage(t, conn, 1, SuccessCommand, IgnoreReceivedArguments)
TSendMessage(t, conn, 2, "sub", TestChannelName1)
TReceiveExpectedMessage(t, conn, 2, SuccessCommand, nil)
TSendMessage(t, conn, 3, "sub", TestChannelName3) // 3
TReceiveExpectedMessage(t, conn, 3, SuccessCommand, nil)
TSendMessage(t, conn, 4, "ready", 0)
TReceiveExpectedMessage(t, conn, 4, SuccessCommand, nil)
readyWg.Done()
TReceiveExpectedMessage(t, conn, -1, TestCommandChan, TestData1)
TReceiveExpectedMessage(t, conn, -1, TestCommandMulti, TestData2)
TReceiveExpectedMessage(t, conn, -1, TestCommandGlobal, TestData4)
conn.Close()
doneWg.Done()
}(conn)
// Client 3
conn, resp, err = websocket.DefaultDialer.Dial(urls.Websocket, headers)
if err != nil {
t.Error(err)
return
}
doneWg.Add(1)
readyWg.Wait() // enforce ordering
time.Sleep(2 * time.Millisecond)
readyWg.Add(1)
go func(conn *websocket.Conn) {
TSendMessage(t, conn, 1, HelloCommand, []interface{}{"ffz_0.0-test", uuid.NewV4().String()})
TReceiveExpectedMessage(t, conn, 1, SuccessCommand, IgnoreReceivedArguments)
TSendMessage(t, conn, 2, "ready", 0)
TReceiveExpectedMessage(t, conn, 2, SuccessCommand, nil)
readyWg.Done()
TReceiveExpectedMessage(t, conn, -1, TestCommandGlobal, TestData4)
conn.Close()
doneWg.Done()
}(conn)
// Wait for clients 1-3
readyWg.Wait()
var form url.Values
// Publish message 1 - should go to clients 1, 2
form, err = TSealForSavePubMsg(t, TestCommandChan, TestChannelName1, TestData1, false)
if err != nil {
t.FailNow()
}
resp, err = http.PostForm(urls.SavePubMsg, form)
if !TCheckResponse(t, resp, strconv.Itoa(2), "pub msg 1") {
t.FailNow()
}
// Publish message 2 - should go to clients 1, 2
form, err = TSealForSavePubMsg(t, TestCommandMulti, TestChannelName2+","+TestChannelName3, TestData2, false)
if err != nil {
t.FailNow()
}
resp, err = http.PostForm(urls.SavePubMsg, form)
if !TCheckResponse(t, resp, strconv.Itoa(2), "pub msg 2") {
t.FailNow()
}
// Publish message 3 - should go to no clients
form, err = TSealForSavePubMsg(t, TestCommandChan, TestChannelNameUnused, TestData3, false)
if err != nil {
t.FailNow()
}
resp, err = http.PostForm(urls.SavePubMsg, form)
if !TCheckResponse(t, resp, strconv.Itoa(0), "pub msg 3") {
t.FailNow()
}
// Publish message 4 - should go to clients 1, 2, 3
form, err = TSealForUncachedPubMsg(t, TestCommandGlobal, "", TestData4, "global", false)
if err != nil {
t.FailNow()
}
resp, err = http.PostForm(urls.UncachedPubMsg, form)
if !TCheckResponse(t, resp, strconv.Itoa(3), "pub msg 4") {
t.FailNow()
}
// Start client 4
conn, resp, err = websocket.DefaultDialer.Dial(urls.Websocket, headers)
if err != nil {
t.Error(err)
return
}
doneWg.Add(1)
readyWg.Add(1)
go func(conn *websocket.Conn) {
TSendMessage(t, conn, 1, HelloCommand, []interface{}{"ffz_0.0-test", uuid.NewV4().String()})
TReceiveExpectedMessage(t, conn, 1, SuccessCommand, IgnoreReceivedArguments)
TSendMessage(t, conn, 2, "sub", TestChannelName1)
TReceiveExpectedMessage(t, conn, 2, SuccessCommand, nil)
TSendMessage(t, conn, 3, "ready", 0)
TReceiveExpectedMessage(t, conn, 3, SuccessCommand, nil)
// backlog message
TReceiveExpectedMessage(t, conn, -1, TestCommandChan, TestData1)
readyWg.Done()
conn.Close()
doneWg.Done()
}(conn)
readyWg.Wait()
doneWg.Wait()
server.Close()
clientCount := readCurrentHLL()
if clientCount < 3 || clientCount > 5 {
t.Error("clientCount outside acceptable range: expected 4, got ", clientCount)
}
}
func TestRestrictedCommands(t *testing.T) {
var doneWg sync.WaitGroup
var readyWg sync.WaitGroup
const TestCommandNeedsAuth = "needsauth"
const TestRequestData = "123456"
const TestRequestDataJSON = "\"" + TestRequestData + "\""
const TestReplyData = "success"
const TestUsername = "sirstendec"
var server *httptest.Server
var urls TURLs
t.Log("TestRestrictedCommands")
var backendExpected = NewTBackendRequestChecker(t,
TExpectedBackendRequest{200, bPathAnnounceStartup, &url.Values{"startup": []string{"1"}}, "", nil},
TExpectedBackendRequest{401, fmt.Sprintf("%s%s", bPathOtherCommand, TestCommandNeedsAuth), &url.Values{"authenticated": []string{"0"}, "username": []string{""}, "clientData": []string{TestRequestDataJSON}}, "", nil},
TExpectedBackendRequest{401, fmt.Sprintf("%s%s", bPathOtherCommand, TestCommandNeedsAuth), &url.Values{"authenticated": []string{"0"}, "username": []string{TestUsername}, "clientData": []string{TestRequestDataJSON}}, "", nil},
TExpectedBackendRequest{200, fmt.Sprintf("%s%s", bPathOtherCommand, TestCommandNeedsAuth), &url.Values{"authenticated": []string{"1"}, "username": []string{TestUsername}, "clientData": []string{TestRequestDataJSON}}, fmt.Sprintf("\"%s\"", TestReplyData), nil},
)
server, _, urls = TSetup(SetupWantSocketServer|SetupWantBackendServer|SetupWantURLs, backendExpected)
defer server.CloseClientConnections()
defer unsubscribeAllClients()
defer backendExpected.Close()
var conn *websocket.Conn
var err error
var challengeChan = make(chan string)
var headers http.Header = make(http.Header)
headers.Set("Origin", TestOrigin)
// Client 1
conn, _, err = websocket.DefaultDialer.Dial(urls.Websocket, headers)
if err != nil {
t.Error(err)
return
}
doneWg.Add(1)
readyWg.Add(1)
go func(conn *websocket.Conn) {
defer doneWg.Done()
defer conn.Close()
TSendMessage(t, conn, 1, HelloCommand, []interface{}{"ffz_0.0-test", uuid.NewV4().String()})
TReceiveExpectedMessage(t, conn, 1, SuccessCommand, IgnoreReceivedArguments)
TSendMessage(t, conn, 2, ReadyCommand, 0)
TReceiveExpectedMessage(t, conn, 2, SuccessCommand, nil)
// Should get immediate refusal because no username set
TSendMessage(t, conn, 3, TestCommandNeedsAuth, TestRequestData)
TReceiveExpectedMessage(t, conn, 3, ErrorCommand, AuthorizationNeededError)
// Set a username
TSendMessage(t, conn, 4, SetUserCommand, TestUsername)
TReceiveExpectedMessage(t, conn, 4, SuccessCommand, nil)
// Should get authorization prompt
TSendMessage(t, conn, 5, TestCommandNeedsAuth, TestRequestData)
readyWg.Done()
msg, success := TReceiveExpectedMessage(t, conn, -1, AuthorizeCommand, IgnoreReceivedArguments)
if !success {
t.Error("recieve authorize command failed, cannot continue")
return
}
challenge, err := msg.ArgumentsAsString()
if err != nil {
t.Error(err)
return
}
challengeChan <- challenge // mocked: sending challenge to IRC server, IRC server sends challenge to socket server
TReceiveExpectedMessage(t, conn, 5, SuccessCommand, TestReplyData)
}(conn)
readyWg.Wait()
challenge := <-challengeChan
PendingAuthLock.Lock()
found := false
for _, v := range PendingAuths {
if conn.LocalAddr().String() == v.Client.RemoteAddr.String() {
found = true
if v.Challenge != challenge {
t.Error("Challenge in array was not what client got")
}
break
}
}
PendingAuthLock.Unlock()
if !found {
t.Fatal("Did not find authorization challenge in the pending auths array")
}
submitAuth(TestUsername, challenge)
doneWg.Wait()
server.Close()
}
func BenchmarkUserSubscriptionSinglePublish(b *testing.B) {
var doneWg sync.WaitGroup
var readyWg sync.WaitGroup
const TestChannelName = "room.testchannel"
const TestCommand = "testdata"
const TestData = "123456789"
message := ClientMessage{MessageID: -1, Command: "testdata", Arguments: TestData}
fmt.Println()
fmt.Println(b.N)
var limit syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit)
limit.Cur = TCountOpenFDs() + uint64(b.N)*2 + 100
if limit.Cur > limit.Max {
b.Skip("Open file limit too low")
return
}
syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit)
var server *httptest.Server
var urls TURLs
server, _, urls = TSetup(SetupWantSocketServer|SetupWantURLs, nil)
defer unsubscribeAllClients()
var headers http.Header = make(http.Header)
headers.Set("Origin", TestOrigin)
b.ResetTimer()
for i := 0; i < b.N; i++ {
conn, _, err := websocket.DefaultDialer.Dial(urls.Websocket, headers)
if err != nil {
b.Error(err)
break
}
doneWg.Add(1)
readyWg.Add(1)
go func(i int, conn *websocket.Conn) {
TSendMessage(b, conn, 1, HelloCommand, []interface{}{"ffz_0.0-test", uuid.NewV4().String()})
TSendMessage(b, conn, 2, "sub", TestChannelName)
TReceiveExpectedMessage(b, conn, 1, SuccessCommand, IgnoreReceivedArguments)
TReceiveExpectedMessage(b, conn, 2, SuccessCommand, nil)
readyWg.Done()
TReceiveExpectedMessage(b, conn, -1, TestCommand, TestData)
conn.Close()
doneWg.Done()
}(i, conn)
}
readyWg.Wait()
fmt.Println("publishing...")
if PublishToChannel(TestChannelName, message, rate.Unlimited()) != b.N {
b.Error("not enough sent")
server.CloseClientConnections()
panic("halting test instead of waiting")
}
doneWg.Wait()
fmt.Println("...done.")
b.StopTimer()
server.Close()
server.CloseClientConnections()
}