161 lines
6.1 KiB
Go
161 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// "отметь второй пункт" names a position, and only the daemon knows which item
|
|
// that is. The router fills the value slot with "2"; this is where it becomes an
|
|
// item id (Vikunja #516).
|
|
func TestPositionResolvesAgainstTheLastSpokenList(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[
|
|
{"id":"item_a","title":"диск заканчивается"},
|
|
{"id":"item_b","title":"бэкап не прошёл"},
|
|
{"id":"item_c","title":"сертификат истекает"}
|
|
]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
|
|
if reply := h.handlePraxisAct(context.Background(), praxisActDec("list_attention"), routeCandidate("list_attention")); reply == "" {
|
|
t.Fatal("attention returned nothing")
|
|
}
|
|
|
|
cases := []struct{ ref, wantItem string }{
|
|
{"2", "item_b"},
|
|
{"1", "item_a"},
|
|
{"last", "item_c"},
|
|
}
|
|
for _, c := range cases {
|
|
praxis.ResetRequests()
|
|
reply := h.handlePraxisAct(context.Background(), praxisItemDec("acknowledge_item", c.ref), routeCandidate("acknowledge_item"))
|
|
if !strings.Contains(reply, "принято") {
|
|
t.Errorf("ref %q: reply %q", c.ref, reply)
|
|
}
|
|
if !requestedPathContaining(praxis, c.wantItem) {
|
|
t.Errorf("ref %q did not acknowledge %s; paths %v", c.ref, c.wantItem, paths(praxis))
|
|
}
|
|
}
|
|
}
|
|
|
|
// A position past the end must not acknowledge the wrong item. It asks.
|
|
func TestPositionPastTheEndAsksInsteadOfGuessing(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[{"id":"item_a","title":"диск заканчивается"}]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
h.handlePraxisAct(context.Background(), praxisActDec("list_attention"), routeCandidate("list_attention"))
|
|
|
|
praxis.ResetRequests()
|
|
reply := h.handlePraxisAct(context.Background(), praxisItemDec("resolve_item", "4"), routeCandidate("resolve_item"))
|
|
if !strings.Contains(reply, "какой пункт") {
|
|
t.Errorf("a position with no item should ask, got %q", reply)
|
|
}
|
|
if requestedPathContaining(praxis, "item_a") {
|
|
t.Error("the only surfaced item was resolved for a position that did not name it")
|
|
}
|
|
}
|
|
|
|
// No digest yet means no positions. Nothing is mutated.
|
|
func TestPositionWithNoSpokenListAsks(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
|
|
reply := h.handlePraxisAct(context.Background(), praxisItemDec("acknowledge_item", "1"), routeCandidate("acknowledge_item"))
|
|
if !strings.Contains(reply, "какой пункт") {
|
|
t.Errorf("want the ask, got %q", reply)
|
|
}
|
|
}
|
|
|
|
// An explicit id is not a position and passes through untouched.
|
|
func TestExplicitItemIDIsNotRewritten(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[{"id":"item_a","title":"диск"}]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
h.handlePraxisAct(context.Background(), praxisActDec("list_attention"), routeCandidate("list_attention"))
|
|
|
|
praxis.ResetRequests()
|
|
h.handlePraxisAct(context.Background(), praxisItemDec("pin_item", "item_zz"), routeCandidate("pin_item"))
|
|
if !requestedPathContaining(praxis, "item_zz") {
|
|
t.Errorf("the id he gave was not the one called; paths %v", paths(praxis))
|
|
}
|
|
}
|
|
|
|
// An item Praxis sent without a title is never spoken, so it holds no position.
|
|
func TestUnspokenItemsHoldNoPosition(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[
|
|
{"id":"item_silent"},
|
|
{"id":"item_said","title":"бэкап не прошёл"}
|
|
]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
h.handlePraxisAct(context.Background(), praxisActDec("list_attention"), routeCandidate("list_attention"))
|
|
|
|
praxis.ResetRequests()
|
|
h.handlePraxisAct(context.Background(), praxisItemDec("acknowledge_item", "1"), routeCandidate("acknowledge_item"))
|
|
if !requestedPathContaining(praxis, "item_said") {
|
|
t.Errorf("position 1 is the first item she SAID; paths %v", paths(praxis))
|
|
}
|
|
}
|
|
|
|
// The item id travels in the POST body, so that is what these read.
|
|
func paths(f *fakeServer) []string {
|
|
var out []string
|
|
for _, r := range f.Requests() {
|
|
out = append(out, r.Path+" "+string(r.Body))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func requestedPathContaining(f *fakeServer, want string) bool {
|
|
for _, r := range f.Requests() {
|
|
if strings.Contains(string(r.Body), want) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// "отметь это как сделанное" after a one-item digest points at that item.
|
|
func TestDemonstrativeResolvesWhenOneItemWasSpoken(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[{"id":"item_only","title":"бэкап не прошёл"}]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
h.handlePraxisAct(context.Background(), praxisActDec("list_attention"), routeCandidate("list_attention"))
|
|
|
|
praxis.ResetRequests()
|
|
reply := h.handlePraxisAct(context.Background(), praxisItemDec("acknowledge_item", "this"), routeCandidate("acknowledge_item"))
|
|
if !strings.Contains(reply, "принято") {
|
|
t.Errorf("reply %q", reply)
|
|
}
|
|
if !requestedPathContaining(praxis, "item_only") {
|
|
t.Errorf("the one surfaced item was not acknowledged; paths %v", paths(praxis))
|
|
}
|
|
}
|
|
|
|
// Pointing at one of several is a guess, and a wrong guess transitions the wrong
|
|
// item. The turn goes back to the cascade instead.
|
|
func TestDemonstrativeWithSeveralItemsGivesTheTurnBack(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[
|
|
{"id":"item_a","title":"диск"},
|
|
{"id":"item_b","title":"бэкап"}
|
|
]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
h.handlePraxisAct(context.Background(), praxisActDec("list_attention"), routeCandidate("list_attention"))
|
|
|
|
praxis.ResetRequests()
|
|
if reply := h.handlePraxisAct(context.Background(), praxisItemDec("resolve_item", "this"), routeCandidate("resolve_item")); reply != "" {
|
|
t.Errorf("want a fall-through, got %q", reply)
|
|
}
|
|
for _, p := range paths(praxis) {
|
|
if strings.Contains(p, "resolve") {
|
|
t.Error("an ambiguous demonstrative resolved an item anyway")
|
|
}
|
|
}
|
|
}
|
|
|
|
// "я это сделал" with no digest behind it is a sentence about his day.
|
|
func TestDemonstrativeWithNoDigestGivesTheTurnBack(t *testing.T) {
|
|
praxis := newFakePraxis(t, `[]`)
|
|
h := newPraxisTestHandler(t, praxis)
|
|
|
|
if reply := h.handlePraxisAct(context.Background(), praxisItemDec("resolve_item", "this"), routeCandidate("resolve_item")); reply != "" {
|
|
t.Errorf("want a fall-through, got %q", reply)
|
|
}
|
|
}
|