224 lines
6.9 KiB
Go
224 lines
6.9 KiB
Go
package rtpconn
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.stormux.org/storm/skald/hall"
|
|
)
|
|
|
|
func TestParseDiceExpression(t *testing.T) {
|
|
tests := []struct {
|
|
expression string
|
|
count int64
|
|
sides int64
|
|
modifier int64
|
|
}{
|
|
{"2d4", 2, 4, 0},
|
|
{"10d8 + 5", 10, 8, 5},
|
|
{"3 d 2+5", 3, 2, 5},
|
|
{"2 D 20 - 1", 2, 20, -1},
|
|
{" 1d6 ", 1, 6, 0},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.expression, func(t *testing.T) {
|
|
got, err := parseDiceExpression(test.expression)
|
|
if err != nil {
|
|
t.Fatalf("parseDiceExpression(%q): %v", test.expression, err)
|
|
}
|
|
if got.Count != test.count || got.Sides != test.sides || got.Modifier != test.modifier {
|
|
t.Fatalf("parseDiceExpression(%q) = %#v", test.expression, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseDiceExpressionRejectsInvalidAndOversizedRolls(t *testing.T) {
|
|
tests := []string{
|
|
"", "d20", "2d", "2d1", "0d6", "101d6", "2d1000001",
|
|
"2d6 + 1000001", "2 0d6", "2d6 trailing",
|
|
}
|
|
for _, expression := range tests {
|
|
t.Run(expression, func(t *testing.T) {
|
|
if _, err := parseDiceExpression(expression); err == nil {
|
|
t.Fatalf("parseDiceExpression(%q) unexpectedly succeeded", expression)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFormatDiceAnnouncement(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
roll diceRoll
|
|
expected string
|
|
}{
|
|
{
|
|
"two dice",
|
|
diceRoll{Count: 2, Sides: 4, Results: []int64{1, 2}, Total: 3},
|
|
"Storm rolls 2 4-sided dice: 1 and 2, for a total of 3.",
|
|
},
|
|
{
|
|
"many dice with modifier",
|
|
diceRoll{Count: 10, Sides: 10, Modifier: 5, Results: []int64{2, 3, 4, 3, 4, 1, 2, 1, 2, 3}, Total: 30},
|
|
"Storm rolls 10 10-sided dice: 2, 3, 4, 3, 4, and 5 more, plus 5, for a total of 30.",
|
|
},
|
|
{
|
|
"one die with negative modifier",
|
|
diceRoll{Count: 1, Sides: 20, Modifier: -1, Results: []int64{17}, Total: 16},
|
|
"Storm rolls 1 20-sided die: 17, minus 1, for a total of 16.",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := formatDiceAnnouncement("Storm", test.roll); got != test.expected {
|
|
t.Fatalf("formatDiceAnnouncement() = %q, want %q", got, test.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateDiceRollStaysWithinBounds(t *testing.T) {
|
|
roll, err := generateDiceRoll(diceRoll{Count: 100, Sides: 6, Modifier: -50})
|
|
if err != nil {
|
|
t.Fatalf("generateDiceRoll: %v", err)
|
|
}
|
|
if len(roll.Results) != 100 {
|
|
t.Fatalf("generated %d results, want 100", len(roll.Results))
|
|
}
|
|
wantTotal := int64(-50)
|
|
for _, result := range roll.Results {
|
|
if result < 1 || result > 6 {
|
|
t.Fatalf("generated result %d outside 1..6", result)
|
|
}
|
|
wantTotal += result
|
|
}
|
|
if roll.Total != wantTotal {
|
|
t.Fatalf("total = %d, want %d", roll.Total, wantTotal)
|
|
}
|
|
}
|
|
|
|
func TestDiceRollIsServerGeneratedAndStoredInHistory(t *testing.T) {
|
|
hallName := newChalkboardTestHall(t)
|
|
roller := addTestWebClient(t, hallName, "Participant", "message")
|
|
observer := addTestWebClient(t, hallName, "Operator", "op")
|
|
|
|
if err := handleClientMessage(roller, clientMessage{
|
|
Type: "hallaction",
|
|
Kind: "roll",
|
|
Value: "2d4 + 5",
|
|
}); err != nil {
|
|
t.Fatalf("handleClientMessage(roll): %v", err)
|
|
}
|
|
|
|
for name, messages := range map[string][]clientMessage{
|
|
"roller": drainMessages(roller),
|
|
"observer": drainMessages(observer),
|
|
} {
|
|
if len(messages) != 1 {
|
|
t.Fatalf("%s received %d messages, want 1: %#v", name, len(messages), messages)
|
|
}
|
|
message := messages[0]
|
|
if message.Type != "chat" || message.Kind != "dice" || !message.Privileged {
|
|
t.Fatalf("%s received non-authoritative dice message: %#v", name, message)
|
|
}
|
|
if message.Source != "" || message.Username == nil || *message.Username != "Dice" {
|
|
t.Fatalf("%s received dice message with unexpected identity: %#v", name, message)
|
|
}
|
|
text, ok := message.Value.(string)
|
|
if !ok || !regexp.MustCompile(`^Participant rolls 2 4-sided dice: [1-4] and [1-4], plus 5, for a total of (7|8|9|10|11|12|13)\.$`).MatchString(text) {
|
|
t.Fatalf("%s received malformed dice announcement %q", name, text)
|
|
}
|
|
}
|
|
|
|
history := roller.hall.GetChatHistory()
|
|
if len(history) != 1 || history[0].Kind != "dice" {
|
|
t.Fatalf("dice history = %#v", history)
|
|
}
|
|
|
|
newcomer := testWebClient("admin-id")
|
|
adminName := "Admin"
|
|
if _, err := hall.AddClient(hallName, newcomer, hall.ClientCredentials{
|
|
Username: &adminName,
|
|
Password: "pw",
|
|
}); err != nil {
|
|
t.Fatalf("AddClient(Admin): %v", err)
|
|
}
|
|
newcomer.hall = hall.Get(hallName)
|
|
drainActions(t, newcomer)
|
|
var historicalDice *clientMessage
|
|
for _, message := range drainMessages(newcomer) {
|
|
if message.Type == "chathistory" && message.Kind == "dice" {
|
|
message := message
|
|
historicalDice = &message
|
|
break
|
|
}
|
|
}
|
|
if historicalDice == nil || !historicalDice.Privileged {
|
|
t.Fatalf("new participant did not receive authoritative dice history: %#v", historicalDice)
|
|
}
|
|
}
|
|
|
|
func TestDiceRollRequiresMessagePermission(t *testing.T) {
|
|
hallName := newChalkboardTestHall(t)
|
|
roller := addTestWebClient(t, hallName, "Participant", "message")
|
|
roller.permissions = remove("message", roller.permissions)
|
|
|
|
if err := handleClientMessage(roller, clientMessage{
|
|
Type: "hallaction", Kind: "roll", Value: "1d6",
|
|
}); err != nil {
|
|
t.Fatalf("handleClientMessage(roll): %v", err)
|
|
}
|
|
if got := roller.hall.GetChatHistory(); len(got) != 0 {
|
|
t.Fatalf("unauthorised roll entered history: %#v", got)
|
|
}
|
|
messages := drainMessages(roller)
|
|
if len(messages) != 1 || messages[0].Kind != "error" ||
|
|
!strings.Contains(messages[0].Value.(string), "not authorised") {
|
|
t.Fatalf("unauthorised roll response = %#v", messages)
|
|
}
|
|
}
|
|
|
|
func TestClientsCannotSubmitServerGeneratedMessages(t *testing.T) {
|
|
hallName := newChalkboardTestHall(t)
|
|
roller := addTestWebClient(t, hallName, "Participant", "message")
|
|
|
|
for _, messageKind := range []string{"dice", "eightball", "coin"} {
|
|
for _, messageType := range []string{"chat", "usermessage"} {
|
|
drainMessages(roller)
|
|
if err := handleClientMessage(roller, clientMessage{
|
|
Type: messageType, Kind: messageKind, Value: "whatever result I wanted",
|
|
}); err != nil {
|
|
t.Fatalf("handleClientMessage(%s, %s): %v", messageType, messageKind, err)
|
|
}
|
|
messages := drainMessages(roller)
|
|
if len(messages) != 1 || messages[0].Kind != "error" {
|
|
t.Fatalf("reserved %s/%s response = %#v", messageType, messageKind, messages)
|
|
}
|
|
}
|
|
}
|
|
if got := roller.hall.GetChatHistory(); len(got) != 0 {
|
|
t.Fatalf("forged dice message entered history: %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestChatIdentityComesFromConnection(t *testing.T) {
|
|
hallName := newChalkboardTestHall(t)
|
|
sender := addTestWebClient(t, hallName, "Participant", "message")
|
|
observer := addTestWebClient(t, hallName, "Operator", "op")
|
|
|
|
if err := handleClientMessage(sender, clientMessage{
|
|
Type: "chat", Value: "hello",
|
|
}); err != nil {
|
|
t.Fatalf("handleClientMessage(chat): %v", err)
|
|
}
|
|
messages := drainMessages(observer)
|
|
if len(messages) != 1 || messages[0].Source != sender.id ||
|
|
messages[0].Username == nil || *messages[0].Username != "Participant" {
|
|
t.Fatalf("chat did not use connection identity: %#v", messages)
|
|
}
|
|
}
|