package domain import ( "testing" "time" "git.cin.su/m/Assistant/contracts/identity" "git.cin.su/m/Assistant/contracts/interaction" ) func TestNewHumanUtteranceClassifiesQuestionAndRequiresCognition(t *testing.T) { conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen} utterance, intent, relations, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: "Кто ты?"}, time.Unix(10, 0)) if err != nil { t.Fatal(err) } if utterance.SpeechAct != interaction.SpeechActQuestion || !NeedsCognition(utterance.SpeechAct) { t.Fatalf("expected cognitive question, got %s", utterance.SpeechAct) } if intent.Text != "Кто ты?" || len(relations) != 0 { t.Fatalf("unexpected intent or relations") } } func TestNewHumanUtteranceAppendsCorrectionRelation(t *testing.T) { conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen} utterance, _, relations, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: "исправь прошлое", CorrectsUtteranceID: "utterance-0"}, time.Unix(10, 0)) if err != nil { t.Fatal(err) } if utterance.SpeechAct != interaction.SpeechActCorrection { t.Fatalf("expected correction, got %s", utterance.SpeechAct) } if len(relations) != 1 || relations[0].Kind != interaction.RelationCorrects || relations[0].ToUtteranceID != "utterance-0" { t.Fatalf("unexpected correction relation: %#v", relations) } } func TestNewHumanUtteranceClassifiesReminderAndCapabilityConnectionAsDirective(t *testing.T) { conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen} for _, message := range []string{"Напомни завтра позвонить врачу.", "Подключи интеграцию с неизвестным сервисом."} { utterance, _, _, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: message}, time.Unix(10, 0)) if err != nil { t.Fatal(err) } if utterance.SpeechAct != interaction.SpeechActDirective { t.Fatalf("expected directive for %q, got %s", message, utterance.SpeechAct) } } } func TestDeterministicCandidateIsInvariantToMissingQuestionMarkForIdentityAndArithmetic(t *testing.T) { cases := []struct { message string act interaction.SpeechAct }{ {"ты кто", interaction.SpeechActQuestion}, {"Ты кто?", interaction.SpeechActQuestion}, {"ТЫ КТО", interaction.SpeechActQuestion}, {"а ты кто", interaction.SpeechActQuestion}, {"сколько будет 2 + 2", interaction.SpeechActQuestion}, {"сколько будет 2 + 2?", interaction.SpeechActQuestion}, {"2 плюс 2 сколько", interaction.SpeechActQuestion}, {"как тебя зовут", interaction.SpeechActQuestion}, } for _, tc := range cases { candidate := BuildDeterministicCandidate(tc.message, "", time.Unix(10, 0).UnixMilli()) if candidate.PrimarySpeechAct != tc.act { t.Fatalf("expected %s for %q, got %s (%+v)", tc.act, tc.message, candidate.PrimarySpeechAct, candidate) } if candidate.Applicability == interaction.SemanticApplicable && candidate.NeedsCognition { continue } if !candidate.NeedsCognition { t.Fatalf("expected cognition-worthy candidate for %q, got %+v", tc.message, candidate) } } } func TestDeterministicCandidateTreatsModalExplanationRequestsAsCognitionWorthWithoutQuestionMark(t *testing.T) { for _, message := range []string{ "можешь рассказать об утре", "можешь расказать об утре", "можете объяснить это проще", "расскажи об утре", "про утро расскажешь", "РАССКАЖИ ПОХОЖЕ ЧТО ПРОИСХОДИТ", "можете обяснить почему база растет", "ты не видишь ии", "домовой не отвечает", "домовой не работает", "домовой не реагирует", } { candidate := BuildDeterministicCandidate(message, "", time.Unix(10, 0).UnixMilli()) if !candidate.NeedsCognition { t.Fatalf("expected cognition for %q, got %+v", message, candidate) } } } func TestDeterministicCandidateTreatsModelRequestAsDirectiveNeedingCognition(t *testing.T) { candidate := BuildDeterministicCandidate("обратись к модели gemma4 и спроси сколько звезд на небе", "", time.Unix(10, 0).UnixMilli()) if candidate.PrimarySpeechAct != interaction.SpeechActDirective && candidate.PrimarySpeechAct != interaction.SpeechActRequestToInvestigate && candidate.PrimarySpeechAct != interaction.SpeechActQuestion { t.Fatalf("expected action-like or inquiry intent, got %+v", candidate) } if !candidate.NeedsCognition { t.Fatalf("expected cognition-worthy model request, got %+v", candidate) } } func TestDeterministicCandidateKeepsFirstPersonCompletedWorkAsAssertion(t *testing.T) { candidate := BuildDeterministicCandidate("Я всё уже настроил.", "", time.Unix(10, 0).UnixMilli()) if candidate.PrimarySpeechAct != interaction.SpeechActAssertion { t.Fatalf("expected assertion, got %+v", candidate) } } func TestNewConversationRequiresActorAndPrincipal(t *testing.T) { _, err := NewConversation(identity.ActorID(""), identity.PrincipalID("principal-1"), time.Unix(10, 0)) if err == nil { t.Fatal("expected invalid principal") } }