package router import "testing" func TestParseTaskCapture(t *testing.T) { cases := []struct { in string text string weight int ok bool }{ {"добавь в задачи купить молоко", "купить молоко", 0, true}, {"Добавь в список дел: позвонить в банк", "позвонить в банк", 0, true}, {"запиши задачу починить кран.", "починить кран", 0, true}, {"новая задача — оплатить интернет", "оплатить интернет", 0, true}, {"add a task buy milk", "buy milk", 0, true}, // Urgency he stated out loud, leading or trailing, stripped from the text. {"добавь в задачи срочно оплатить интернет", "оплатить интернет", 3, true}, {"добавь в задачи оплатить интернет срочно", "оплатить интернет", 3, true}, {"новая задача важно позвонить маме", "позвонить маме", 2, true}, // The stem inside the task text is part of the task, not a marker. {"добавь в задачи позвонить в срочную помощь", "позвонить в срочную помощь", 0, true}, // Whisper punctuates dictated Russian. The marker used to be missed as // soon as anything sat next to it, and then it stayed in the task text // and in the dedupe key — the exact task he was trying to flag. {"добавь в задачи оплатить интернет, срочно", "оплатить интернет", 3, true}, {"добавь в задачи очень срочно оплатить интернет", "оплатить интернет", 3, true}, {"добавь в задачи оплатить интернет — важно", "оплатить интернет", 2, true}, // A dictated question mark is not part of the task. {"добавь в задачи позвонить в банк?", "позвонить в банк", 0, true}, // The phrasings he uses that the prefix list did not have. {"поставь задачу вынести мусор", "вынести мусор", 0, true}, {"добавь в тудушки купить лампочки", "купить лампочки", 0, true}, // A marker with nothing after it files nothing. {"добавь в задачи", "", 0, false}, {"новая задача", "", 0, false}, {"добавь в задачи срочно", "", 0, false}, // Not a capture: he is talking, not filing. {"надо бы поспать", "", 0, false}, {"я не добавил молоко в список", "", 0, false}, {"какие у меня задачи?", "", 0, false}, {"", "", 0, false}, } for _, c := range cases { got, ok := ParseTaskCapture(c.in) if ok != c.ok || got.Text != c.text || got.Weight != c.weight { t.Errorf("ParseTaskCapture(%q) = (%+v, %v), want (%q, w=%d, %v)", c.in, got, ok, c.text, c.weight, c.ok) } } } func TestIsTaskListQuery(t *testing.T) { yes := []string{ "какие у меня задачи?", "что мне нужно сделать?", "покажи список дел", "сколько у меня задач?", "задачи", "мои задачи", "what should I do", "что мне делать?", } for _, s := range yes { if !IsTaskListQuery(s) { t.Errorf("IsTaskListQuery(%q) = false, want true", s) } } no := []string{ "как дела?", // No task noun and no pronoun: these fired ahead of recall and the // model, and answered a question about a file or a server with // "задач нет." "что нужно сделать чтобы перезапустить сервер?", "что мне сделать с этим файлом?", "what does docker do?", "what do you do?", "какая погода?", "напомни мне позвонить маме в шесть", "я сделал зарядку", "", } for _, s := range no { if IsTaskListQuery(s) { t.Errorf("IsTaskListQuery(%q) = true, want false", s) } } } // TestTaskCaptureGrammarClaimsTheMarker — the capture marker is answered at // stage 0, so the model never gets to call it an act (Vikunja #467). func TestTaskCaptureGrammarClaimsTheMarker(t *testing.T) { g := TaskCaptureGrammar() captures := map[string]string{ "добавь в задачи купить молоко": "купить молоко", "запиши в список дел купить хлеб": "купить хлеб", "поставь задачу вынести мусор": "вынести мусор", "добавь в задачи срочно оплатить дом": "оплатить дом", } for in, want := range captures { m := g.Pattern.FindStringSubmatch(in) if m == nil { t.Fatalf("%q did not match the grammar pattern", in) } d, ok := g.Build(m) if !ok { t.Fatalf("%q must be claimed as a capture", in) } if d.Intent != IntentNote || d.Slots.Text != want { t.Errorf("%q → intent=%s text=%q, want note/%q", in, d.Intent, d.Slots.Text, want) } } // Everything without a marker falls through, including a marker with no // task after it and a question about the list. for _, in := range []string{"надо бы поспать", "добавь в задачи", "какие у меня задачи?", "перезапусти nginx"} { if m := g.Pattern.FindStringSubmatch(in); m != nil { if _, ok := g.Build(m); ok { t.Errorf("%q must fall through to the cascade", in) } } } } // TestTaskListGrammarClaimsTheAsk — a list question answered before the model, // including the phrasings with no possessive that used to route elsewhere. func TestTaskListGrammarClaimsTheAsk(t *testing.T) { g := TaskListGrammar() claimed := []string{"какие у меня задачи", "список дел", "что мне нужно сделать"} for _, u := range claimed { m := g.Pattern.FindStringSubmatch(u) if m == nil { t.Fatalf("%q did not match the grammar pattern", u) } d, ok := g.Build(m) if !ok || d.Intent != IntentQuery { t.Errorf("%q built %+v ok=%v; want a query", u, d, ok) } } passed := []string{"как дела", "напомни купить хлеб", "что docker делает"} for _, u := range passed { m := g.Pattern.FindStringSubmatch(u) if _, ok := g.Build(m); ok { t.Errorf("%q was claimed as a task list", u) } } }