140 lines
6.2 KiB
Go
140 lines
6.2 KiB
Go
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},
|
||
{"сколько будет 2 + 2", interaction.SpeechActQuestion},
|
||
{"сколько будет 2 + 2?", interaction.SpeechActQuestion},
|
||
{"2 плюс 2 сколько", 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.PrimarySpeechAct != interaction.SpeechActQuestion && candidate.PrimarySpeechAct != interaction.SpeechActRequestToInvestigate {
|
||
t.Fatalf("expected broader cognitive speech act for %q, got %+v", message, candidate)
|
||
}
|
||
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 {
|
||
t.Fatalf("expected directive, got %+v", candidate)
|
||
}
|
||
if !candidate.NeedsCognition {
|
||
t.Fatalf("expected cognition-worthy model request, got %+v", candidate)
|
||
}
|
||
}
|
||
|
||
func TestInformationRequestsAreClassifiedOnceByInteraction(t *testing.T) {
|
||
cases := []struct {
|
||
message string
|
||
kind interaction.InformationRequestKind
|
||
}{
|
||
{"какие модели доступны?", interaction.InformationRequestCognitiveModelInventory},
|
||
{"что ты умеешь?", interaction.InformationRequestCapabilityInventory},
|
||
{"проверь состояние локального канала", interaction.InformationRequestCognitiveChannelStatus},
|
||
{"нет связи с ИИ, восстанови канал", interaction.InformationRequestCognitiveRecovery},
|
||
}
|
||
for _, test := range cases {
|
||
result, err := InterpretUtterance(interaction.HumanUtterance{Content: test.message}, nil, nil, time.Unix(10, 0))
|
||
if err != nil {
|
||
t.Fatalf("interpret %q: %v", test.message, err)
|
||
}
|
||
if result.InformationRequestKind != test.kind {
|
||
t.Fatalf("message=%q kind=%q want=%q", test.message, result.InformationRequestKind, test.kind)
|
||
}
|
||
}
|
||
}
|
||
|
||
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")
|
||
}
|
||
}
|