162 lines
8.6 KiB
Go
162 lines
8.6 KiB
Go
package domain
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.cin.su/m/Assistant/contracts/authority"
|
|
"git.cin.su/m/Assistant/contracts/continuity"
|
|
"git.cin.su/m/Assistant/contracts/identity"
|
|
)
|
|
|
|
func TestInterpretHumanResponseCompletesSingleDeliveredReminder(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Готово", []ResponseCandidate{responseCandidate("a1", "p1", "позвонить врачу")})
|
|
if !result.Handled || result.Response.Operation != continuity.ResponseComplete || result.Response.Outcome != continuity.ResponseOutcomeUserReportedComplete || !result.Response.UserReportedNotVerified {
|
|
t.Fatalf("unexpected complete response: %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseSnoozesSingleReminder(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Отложи на 10 минут", []ResponseCandidate{responseCandidate("a1", "p1", "проверить базу")})
|
|
if !result.Handled || result.Response.Operation != continuity.ResponseSnooze || result.Response.Outcome != continuity.ResponseOutcomeSnoozed {
|
|
t.Fatalf("unexpected snooze response: %#v", result)
|
|
}
|
|
if result.Response.SnoozeUntilUnixMillis != time.Unix(100, 0).Add(10*time.Minute).UnixMilli() {
|
|
t.Fatalf("unexpected snooze time: %d", result.Response.SnoozeUntilUnixMillis)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseCancelsSingleReminder(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Отмени", []ResponseCandidate{responseCandidate("a1", "p1", "встретить курьера")})
|
|
if !result.Handled || result.Response.Operation != continuity.ResponseCancel || result.Response.Outcome != continuity.ResponseOutcomeCancelled {
|
|
t.Fatalf("unexpected cancel response: %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseCancelsArmedProspectiveBeforeAttention(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Отмени встретить курьера", []ResponseCandidate{{
|
|
Prospective: continuity.ProspectiveItem{ID: "p1", OwnerPrincipalID: "principal-1", ScopeID: "scope-1", Summary: "встретить курьера", State: continuity.ProspectiveArmed, Revision: 1},
|
|
}})
|
|
if !result.Handled || result.Response.TargetKind != continuity.ResponseTargetProspectiveItem || result.Response.TargetProspectiveID != "p1" || result.Response.Outcome != continuity.ResponseOutcomeCancelled {
|
|
t.Fatalf("unexpected prospective cancel response: %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseResolvesArmedProspectiveByNaturalSummaryReference(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Отмени встретить курьера", []ResponseCandidate{
|
|
{Prospective: continuity.ProspectiveItem{ID: "p1", OwnerPrincipalID: "principal-1", ScopeID: "scope-1", Summary: "позвонить врачу", State: continuity.ProspectiveArmed, Revision: 1}},
|
|
{Prospective: continuity.ProspectiveItem{ID: "p2", OwnerPrincipalID: "principal-1", ScopeID: "scope-1", Summary: "встретить курьера", State: continuity.ProspectiveArmed, Revision: 1}},
|
|
})
|
|
if !result.Handled || result.Response.TargetKind != continuity.ResponseTargetProspectiveItem || result.Response.TargetProspectiveID != "p2" || result.Response.Outcome != continuity.ResponseOutcomeCancelled {
|
|
t.Fatalf("unexpected prospective natural target response: %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseResolvesSummaryReferenceBeyondFirstThreeCandidates(t *testing.T) {
|
|
candidates := []ResponseCandidate{
|
|
responseCandidate("a1", "p1", "первое ожидание"),
|
|
responseCandidate("a2", "p2", "второе ожидание"),
|
|
responseCandidate("a3", "p3", "третье ожидание"),
|
|
{Prospective: continuity.ProspectiveItem{ID: "p4", OwnerPrincipalID: "principal-1", ScopeID: "scope-1", Summary: "встретить курьера", State: continuity.ProspectiveArmed, Revision: 1}},
|
|
}
|
|
result := interpretResponseForTest(t, "Отмени встретить курьера", candidates)
|
|
if !result.Handled || result.Response.ResolutionState != continuity.ResponseResolved || result.Response.TargetProspectiveID != "p4" {
|
|
t.Fatalf("expected target resolution beyond first three candidates, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseAmbiguousTargetAsksClarificationWithoutResolvedMutation(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Готово", []ResponseCandidate{responseCandidate("a1", "p1", "первое"), responseCandidate("a2", "p2", "второе")})
|
|
if !result.Handled || result.Response.ResolutionState != continuity.ResponseAmbiguous || result.Response.Outcome != continuity.ResponseOutcomeClarificationNeeded || !strings.Contains(result.Reply, "несколько") {
|
|
t.Fatalf("expected ambiguity clarification, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseResolvesAmbiguousTargetByNaturalSummaryReference(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Готово, проверить базу сделано", []ResponseCandidate{responseCandidate("a1", "p1", "позвонить врачу"), responseCandidate("a2", "p2", "проверить базу")})
|
|
if !result.Handled || result.Response.ResolutionState != continuity.ResponseResolved || result.Response.TargetAttentionID != "a2" {
|
|
t.Fatalf("expected natural summary target resolution, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseNoTargetAsksClarification(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Готово", nil)
|
|
if !result.Handled || result.Response.ResolutionState != continuity.ResponseNoTarget || result.Response.Outcome != continuity.ResponseOutcomeClarificationNeeded {
|
|
t.Fatalf("expected no-target clarification, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseContinuesAgentDelegationAttention(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Продолжай", []ResponseCandidate{{
|
|
Attention: continuity.AttentionItem{
|
|
ID: "attention-1",
|
|
OwnerPrincipalID: "principal-1",
|
|
ScopeID: "scope-1",
|
|
SourceKind: continuity.AttentionSourceAgentDelegation,
|
|
SourceExternalRef: "work:delegation:delegation-1",
|
|
State: continuity.AttentionDelivered,
|
|
Summary: "нужен твой выбор по задаче coding-agent",
|
|
Revision: 1,
|
|
},
|
|
}})
|
|
if !result.Handled || result.Response.Operation != continuity.ResponseContinue || result.Response.Outcome != continuity.ResponseOutcomeContinued || result.Response.TargetExternalRef != "work:delegation:delegation-1" {
|
|
t.Fatalf("unexpected delegation continue response: %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestInterpretHumanResponseRejectsAgentDelegationAttention(t *testing.T) {
|
|
result := interpretResponseForTest(t, "Нет, нужен другой вариант", []ResponseCandidate{{
|
|
Attention: continuity.AttentionItem{
|
|
ID: "attention-1",
|
|
OwnerPrincipalID: "principal-1",
|
|
ScopeID: "scope-1",
|
|
SourceKind: continuity.AttentionSourceAgentDelegation,
|
|
SourceExternalRef: "work:delegation:delegation-1",
|
|
State: continuity.AttentionDelivered,
|
|
Summary: "нужен твой выбор по задаче coding-agent",
|
|
Revision: 1,
|
|
},
|
|
}})
|
|
if !result.Handled || result.Response.Operation != continuity.ResponseReject || result.Response.Outcome != continuity.ResponseOutcomeRejected {
|
|
t.Fatalf("unexpected delegation reject response: %#v", result)
|
|
}
|
|
}
|
|
|
|
func interpretResponseForTest(t *testing.T, message string, candidates []ResponseCandidate) continuity.HumanResponseResult {
|
|
t.Helper()
|
|
result, err := InterpretHumanResponse(continuity.HumanResponseRequest{
|
|
SourceUtteranceID: "utterance-1",
|
|
SourceIntentID: "intent-1",
|
|
Message: message,
|
|
RequestedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
|
|
}, identity.PrincipalID("principal-1"), authority.ScopeID("scope-1"), continuity.HumanResponseID("response-1"), candidates, time.Unix(100, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func responseCandidate(attentionID continuity.AttentionItemID, prospectiveID continuity.ProspectiveItemID, summary string) ResponseCandidate {
|
|
return ResponseCandidate{
|
|
Attention: continuity.AttentionItem{
|
|
ID: attentionID,
|
|
OwnerPrincipalID: "principal-1",
|
|
ScopeID: "scope-1",
|
|
SourceKind: continuity.AttentionSourceProspectiveReminder,
|
|
SourceProspectiveID: prospectiveID,
|
|
State: continuity.AttentionDelivered,
|
|
Summary: summary,
|
|
Revision: 1,
|
|
},
|
|
Prospective: continuity.ProspectiveItem{
|
|
ID: prospectiveID,
|
|
OwnerPrincipalID: "principal-1",
|
|
ScopeID: "scope-1",
|
|
Summary: summary,
|
|
State: continuity.ProspectiveDelivered,
|
|
Revision: 1,
|
|
},
|
|
}
|
|
}
|