mirror of
https://github.com/diamondburned/cchat-mock.git
synced 2025-01-14 22:06:42 +00:00
Added test typing capabilities
This commit is contained in:
parent
04bd23b05a
commit
dd6b0669f2
105
channel.go
105
channel.go
|
@ -31,6 +31,7 @@ type Channel struct {
|
||||||
send chan cchat.SendableMessage // ideally this should be another type
|
send chan cchat.SendableMessage // ideally this should be another type
|
||||||
edit chan Message // id
|
edit chan Message // id
|
||||||
del chan MessageHeader
|
del chan MessageHeader
|
||||||
|
typ chan Author
|
||||||
|
|
||||||
messageMutex sync.Mutex
|
messageMutex sync.Mutex
|
||||||
messages map[uint32]Message
|
messages map[uint32]Message
|
||||||
|
@ -44,13 +45,14 @@ type Channel struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ cchat.Server = (*Channel)(nil)
|
_ cchat.Server = (*Channel)(nil)
|
||||||
_ cchat.ServerMessage = (*Channel)(nil)
|
_ cchat.ServerMessage = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageSender = (*Channel)(nil)
|
_ cchat.ServerMessageSender = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
|
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
|
||||||
_ cchat.ServerNickname = (*Channel)(nil)
|
_ cchat.ServerNickname = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageEditor = (*Channel)(nil)
|
_ cchat.ServerMessageEditor = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageActioner = (*Channel)(nil)
|
_ cchat.ServerMessageActioner = (*Channel)(nil)
|
||||||
|
_ cchat.ServerMessageTypingIndicator = (*Channel)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ch *Channel) ID() string {
|
func (ch *Channel) ID() string {
|
||||||
|
@ -93,6 +95,7 @@ func (ch *Channel) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (
|
||||||
ch.send = make(chan cchat.SendableMessage)
|
ch.send = make(chan cchat.SendableMessage)
|
||||||
ch.edit = make(chan Message)
|
ch.edit = make(chan Message)
|
||||||
ch.del = make(chan MessageHeader)
|
ch.del = make(chan MessageHeader)
|
||||||
|
ch.typ = make(chan Author)
|
||||||
|
|
||||||
// Generate the backlog.
|
// Generate the backlog.
|
||||||
for i := 0; i < FetchBacklog; i++ {
|
for i := 0; i < FetchBacklog; i++ {
|
||||||
|
@ -330,9 +333,10 @@ func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DeleteAction = "Delete"
|
DeleteAction = "Delete"
|
||||||
NoopAction = "No-op"
|
NoopAction = "No-op"
|
||||||
BestTrapAction = "What's the best trap?"
|
BestTrapAction = "What's the best trap?"
|
||||||
|
TriggerTypingAction = "Trigger Typing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ch *Channel) MessageActions(id string) []string {
|
func (ch *Channel) MessageActions(id string) []string {
|
||||||
|
@ -347,7 +351,7 @@ func (ch *Channel) MessageActions(id string) []string {
|
||||||
// takes a container: the frontend should call this in a goroutine.
|
// takes a container: the frontend should call this in a goroutine.
|
||||||
func (ch *Channel) DoMessageAction(action, messageID string) error {
|
func (ch *Channel) DoMessageAction(action, messageID string) error {
|
||||||
switch action {
|
switch action {
|
||||||
case DeleteAction:
|
case DeleteAction, TriggerTypingAction:
|
||||||
i, err := strconv.Atoi(messageID)
|
i, err := strconv.Atoi(messageID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Invalid ID")
|
return errors.Wrap(err, "Invalid ID")
|
||||||
|
@ -355,7 +359,14 @@ func (ch *Channel) DoMessageAction(action, messageID string) error {
|
||||||
|
|
||||||
// Simulate IO.
|
// Simulate IO.
|
||||||
simulateAustralianInternet()
|
simulateAustralianInternet()
|
||||||
ch.del <- MessageHeader{uint32(i), time.Now()}
|
|
||||||
|
switch action {
|
||||||
|
case DeleteAction:
|
||||||
|
ch.del <- MessageHeader{uint32(i), time.Now()}
|
||||||
|
case TriggerTypingAction:
|
||||||
|
// Find the message.
|
||||||
|
ch.typ <- Author{name: ch.messages[uint32(i)].author}
|
||||||
|
}
|
||||||
|
|
||||||
case NoopAction:
|
case NoopAction:
|
||||||
// do nothing.
|
// do nothing.
|
||||||
|
@ -439,6 +450,49 @@ func lookbackCheck(words []string, i int, prev, this string) bool {
|
||||||
return strings.HasPrefix(this, words[i]) && i > 0 && words[i-1] == prev
|
return strings.HasPrefix(this, words[i]) && i > 0 && words[i-1] == prev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Typing sleeps and returns possibly an error.
|
||||||
|
func (ch *Channel) Typing() error {
|
||||||
|
return simulateAustralianInternet()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypingTimeout returns 5 seconds.
|
||||||
|
func (ch *Channel) TypingTimeout() time.Duration {
|
||||||
|
return 5 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
type Typer struct {
|
||||||
|
Author
|
||||||
|
time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ cchat.Typer = (*Typer)(nil)
|
||||||
|
|
||||||
|
func newTyper(a Author) *Typer { return &Typer{a, time.Now()} }
|
||||||
|
func randomTyper() *Typer { return &Typer{randomAuthor(), time.Now()} }
|
||||||
|
func (t *Typer) Time() time.Time { return t.time }
|
||||||
|
|
||||||
|
func (ch *Channel) TypingSubscribe(ti cchat.TypingIndicator) (stop func(), err error) {
|
||||||
|
var stopch = make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var ticker = time.NewTicker(8 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopch:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
ti.AddTyper(randomTyper())
|
||||||
|
case author := <-ch.typ:
|
||||||
|
ti.AddTyper(newTyper(author))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return func() { close(stopch) }, nil
|
||||||
|
}
|
||||||
|
|
||||||
func generateChannels(s *Session, amount int) []cchat.Server {
|
func generateChannels(s *Session, amount int) []cchat.Server {
|
||||||
var channels = make([]cchat.Server, amount)
|
var channels = make([]cchat.Server, amount)
|
||||||
for i := range channels {
|
for i := range channels {
|
||||||
|
@ -459,30 +513,3 @@ func generateChannels(s *Session, amount int) []cchat.Server {
|
||||||
func randClamp(min, max int) int {
|
func randClamp(min, max int) int {
|
||||||
return rand.Intn(max-min) + min
|
return rand.Intn(max-min) + min
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrTimedOut is returned when the simulated IO decides to fail.
|
|
||||||
var ErrTimedOut = errors.New("Australian Internet unsupported.")
|
|
||||||
|
|
||||||
// simulate network latency
|
|
||||||
func simulateAustralianInternet() error {
|
|
||||||
return simulateAustralianInternetCtx(context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
func simulateAustralianInternetCtx(ctx context.Context) (err error) {
|
|
||||||
var ms = randClamp(internetMinLatency, internetMaxLatency)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Duration(ms) * time.Millisecond):
|
|
||||||
// noop
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// because australia, drop packet 20% of the time if internetCanFail is
|
|
||||||
// true.
|
|
||||||
if internetCanFail && rand.Intn(100) < 20 {
|
|
||||||
return ErrTimedOut
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
44
internet.go
Normal file
44
internet.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// channel.go @ simulateAustralianInternet
|
||||||
|
internetCanFail = true
|
||||||
|
// 500ms ~ 3s
|
||||||
|
internetMinLatency = 500
|
||||||
|
internetMaxLatency = 3000
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrTimedOut is returned when the simulated IO decides to fail.
|
||||||
|
var ErrTimedOut = errors.New("Australian Internet unsupported.")
|
||||||
|
|
||||||
|
// simulate network latency
|
||||||
|
func simulateAustralianInternet() error {
|
||||||
|
return simulateAustralianInternetCtx(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateAustralianInternetCtx(ctx context.Context) (err error) {
|
||||||
|
var ms = randClamp(internetMinLatency, internetMaxLatency)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Duration(ms) * time.Millisecond):
|
||||||
|
// noop
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// because australia, drop packet 20% of the time if internetCanFail is
|
||||||
|
// true.
|
||||||
|
if internetCanFail && rand.Intn(100) < 20 {
|
||||||
|
return ErrTimedOut
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
17
message.go
17
message.go
|
@ -79,12 +79,7 @@ func echoMessage(sendable cchat.SendableMessage, id uint32, author text.Rich) Me
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomMessage(id uint32) Message {
|
func randomMessage(id uint32) Message {
|
||||||
var author = randomdata.SillyName()
|
return randomMessageWithAuthor(id, randomAuthor().name)
|
||||||
|
|
||||||
return randomMessageWithAuthor(id, text.Rich{
|
|
||||||
Content: author,
|
|
||||||
Segments: []text.Segment{segments.NewRandomColored(author)},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomMessageWithAuthor(id uint32, author text.Rich) Message {
|
func randomMessageWithAuthor(id uint32, author text.Rich) Message {
|
||||||
|
@ -122,6 +117,16 @@ var (
|
||||||
_ cchat.MessageAuthorAvatar = (*Author)(nil)
|
_ cchat.MessageAuthorAvatar = (*Author)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func randomAuthor() Author {
|
||||||
|
var author = randomdata.SillyName()
|
||||||
|
return Author{
|
||||||
|
name: text.Rich{
|
||||||
|
Content: author,
|
||||||
|
Segments: []text.Segment{segments.NewRandomColored(author)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a Author) ID() string {
|
func (a Author) ID() string {
|
||||||
return a.name.Content
|
return a.name.Content
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,16 +82,9 @@ func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
|
||||||
return newSession(form[0]), nil
|
return newSession(form[0]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
// channel.go @ simulateAustralianInternet
|
|
||||||
internetCanFail = true
|
|
||||||
// 500ms ~ 3s
|
|
||||||
internetMinLatency = 500
|
|
||||||
internetMaxLatency = 3000
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s Service) Configuration() (map[string]string, error) {
|
func (s Service) Configuration() (map[string]string, error) {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
|
// refer to internet.go
|
||||||
"internet.canFail": strconv.FormatBool(internetCanFail),
|
"internet.canFail": strconv.FormatBool(internetCanFail),
|
||||||
"internet.minLatency": strconv.Itoa(internetMinLatency),
|
"internet.minLatency": strconv.Itoa(internetMinLatency),
|
||||||
"internet.maxLatency": strconv.Itoa(internetMaxLatency),
|
"internet.maxLatency": strconv.Itoa(internetMaxLatency),
|
||||||
|
|
Loading…
Reference in a new issue