package router import "testing" func TestParseListCaptureReadsListAndItem(t *testing.T) { cases := []struct { utterance string list string item string }{ {"добавь в список покупок молоко", "покупки", "молоко"}, {"добавь в список молоко", "покупки", "молоко"}, {"Добавь в покупки хлеб и яйца", "покупки", "хлеб и яйца"}, {"запиши в список аптеки бинт", "аптека", "бинт"}, {"добавь в список хозяйства лампочки.", "хозяйство", "лампочки"}, {"add to the shopping list milk", "покупки", "milk"}, } for _, c := range cases { got, ok := ParseListCapture(c.utterance) if !ok { t.Errorf("ParseListCapture(%q) did not claim it", c.utterance) continue } if got.List != c.list || got.Item != c.item { t.Errorf("ParseListCapture(%q) = %+v; want list %q item %q", c.utterance, got, c.list, c.item) } } } // A marker with no item is not a capture, and an utterance that only mentions // shopping is not one either. func TestParseListCapturePasses(t *testing.T) { for _, u := range []string{ "добавь в список покупок", "добавь в список", "молоко закончилось", "надо бы съездить в магазин", "добавь в задачи купить молоко", } { if got, ok := ParseListCapture(u); ok { t.Errorf("ParseListCapture(%q) claimed it as %+v", u, got) } } } func TestParseListQueryNamesTheList(t *testing.T) { cases := []struct{ utterance, list string }{ {"что в списке покупок?", "покупки"}, {"что в списке", "покупки"}, {"что мне купить", "покупки"}, {"покажи список аптеки", "аптека"}, {"what's on the list", "покупки"}, } for _, c := range cases { list, ok := ParseListQuery(c.utterance) if !ok { t.Errorf("ParseListQuery(%q) did not claim it", c.utterance) continue } if list != c.list { t.Errorf("ParseListQuery(%q) = %q; want %q", c.utterance, list, c.list) } } if _, ok := ParseListQuery("какие у меня задачи"); ok { t.Error("ParseListQuery claimed a task question") } } func TestParseListClearAndRemove(t *testing.T) { if list, ok := ParseListClear("всё купил"); !ok || list != "покупки" { t.Errorf("ParseListClear = %q, %v; want покупки, true", list, ok) } if list, ok := ParseListClear("очисти список аптеки"); !ok || list != "аптека" { t.Errorf("ParseListClear = %q, %v; want аптека, true", list, ok) } if _, ok := ParseListClear("купил молоко"); ok { t.Error("ParseListClear claimed a single item") } got, ok := ParseListRemove("вычеркни молоко") if !ok || got.Item != "молоко" || got.List != "покупки" { t.Errorf("ParseListRemove = %+v, %v; want молоко on покупки", got, ok) } if got, ok := ParseListRemove("убери из списка аптеки бинт"); !ok || got.Item != "бинт" || got.List != "аптека" { t.Errorf("ParseListRemove = %+v, %v; want бинт on аптека", got, ok) } if _, ok := ParseListRemove("вычеркни"); ok { t.Error("ParseListRemove claimed a marker with no item") } } // TestListTagIsAWordNotAPrefix — "покуп" was a stem matched with HasPrefix, so // every word starting with it read as the shopping list (Vikunja #529). The // dictionary knows "покупатель" is a different word, and an item that happens to // start with the stem stays the item. func TestListTagIsAWordNotAPrefix(t *testing.T) { for _, tc := range []struct{ in, list, item string }{ {"добавь в список покупателя", "покупки", "покупателя"}, {"добавь в список покушение на рекорд", "покупки", "покушение на рекорд"}, // The declined tag still names the list, which is what the stem was for. {"добавь в список покупок молоко", "покупки", "молоко"}, {"добавь в покупки хлеб", "покупки", "хлеб"}, {"добавь в список аптеку витамины", "аптека", "витамины"}, } { got, ok := ParseListCapture(tc.in) if !ok || got.List != tc.list || got.Item != tc.item { t.Errorf("ParseListCapture(%q) = (%q, %q, %v), want (%q, %q, true)", tc.in, got.List, got.Item, ok, tc.list, tc.item) } } } // TestNamedTaskListIsNotAGroceryItem — "добавь в список" is a marker on both // sides, and ListGrammars is wired before TaskCaptureGrammar, so the list used // to claim every one of them (Vikunja #520). takeListTag does not know "дел" as // a list name, so the item filed was "дел хлеб". The bare marker stays a // grocery item; a named task list falls through to task capture. func TestNamedTaskListIsNotAGroceryItem(t *testing.T) { for _, in := range []string{ "добавь в список дел хлеб", "добавь в список задач позвонить врачу", "запиши в список дел купить молоко", "add to the list of todos milk", } { if got, ok := ParseListCapture(in); ok { t.Errorf("ParseListCapture(%q) claimed it as list %q item %q, want a pass to task capture", in, got.List, got.Item) } } // The bare form is still the grocery list, and the tagged forms are untouched. for _, tc := range []struct{ in, list, item string }{ {"добавь в список хлеб", "покупки", "хлеб"}, {"добавь в список покупок молоко", "покупки", "молоко"}, {"добавь в список аптеку витамины", "аптека", "витамины"}, } { got, ok := ParseListCapture(tc.in) if !ok || got.List != tc.list || got.Item != tc.item { t.Errorf("ParseListCapture(%q) = (%q, %q, %v), want (%q, %q, true)", tc.in, got.List, got.Item, ok, tc.list, tc.item) } } // A read-back and a cross-off name the same lists and must split the same way. if _, ok := ParseListQuery("покажи список дел"); ok { t.Error(`ParseListQuery("покажи список дел") claimed a task list`) } if got, ok := ParseListRemove("вычеркни из списка дел хлеб"); ok { t.Errorf("ParseListRemove claimed a task list: %q / %q", got.List, got.Item) } }