103 lines
5.0 KiB
Go
103 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.cin.su/m/Assistant/contracts/capability"
|
|
"git.cin.su/m/Assistant/contracts/continuity"
|
|
"git.cin.su/m/Assistant/contracts/identity"
|
|
"git.cin.su/m/Assistant/contracts/interaction"
|
|
"git.cin.su/m/Assistant/contracts/work"
|
|
)
|
|
|
|
func TestBuildReminderRequestParsesTomorrowAtTen(t *testing.T) {
|
|
now := time.Date(2026, 8, 17, 12, 0, 0, 0, time.FixedZone("MSK", 3*60*60))
|
|
request, err := buildReminderRequest("Напомни завтра в 10 позвонить врачу.", identity.CreatorSession{PrincipalID: "principal-1"}, interaction.OriginalHumanIntent{ID: "intent-1"}, interaction.HumanUtterance{ID: "utterance-1"}, now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
trigger := time.UnixMilli(request.TriggerAtUnixMillis).In(now.Location())
|
|
if trigger.Year() != 2026 || trigger.Month() != 8 || trigger.Day() != 18 || trigger.Hour() != 10 || trigger.Minute() != 0 {
|
|
t.Fatalf("unexpected trigger: %s", trigger)
|
|
}
|
|
if request.Summary != "позвонить врачу" || request.SourceIntentID != "intent-1" || request.SourceUtteranceID != "utterance-1" {
|
|
t.Fatalf("unexpected request: %#v", request)
|
|
}
|
|
}
|
|
|
|
func TestBuildReminderRequestParsesRelativeMinutes(t *testing.T) {
|
|
now := time.Date(2026, 8, 17, 12, 0, 0, 0, time.FixedZone("MSK", 3*60*60))
|
|
request, err := buildReminderRequest("Напомни мне через 3 минуты проверить уведомление.", identity.CreatorSession{PrincipalID: "principal-1"}, interaction.OriginalHumanIntent{ID: "intent-1"}, interaction.HumanUtterance{ID: "utterance-1"}, now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
trigger := time.UnixMilli(request.TriggerAtUnixMillis).In(now.Location())
|
|
if !trigger.Equal(now.Add(3 * time.Minute)) {
|
|
t.Fatalf("unexpected trigger: %s", trigger)
|
|
}
|
|
if request.Summary != "проверить уведомление" {
|
|
t.Fatalf("unexpected summary: %q", request.Summary)
|
|
}
|
|
}
|
|
|
|
func TestContinuityReminderClientDoesNotAcceptFalseSuccess(t *testing.T) {
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/creator/reminders" || r.Header.Get("Authorization") != "Bearer session" {
|
|
t.Fatal("unexpected reminder request")
|
|
}
|
|
var request continuity.ScheduleReminderRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&request); err != nil || request.Summary != "позвонить врачу" {
|
|
t.Fatalf("bad payload: %#v err=%v", request, err)
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
_ = json.NewEncoder(w).Encode(continuity.ScheduleReminderResult{Item: continuity.ProspectiveItem{ID: "reminder-1", State: continuity.ProspectiveArmed, Summary: request.Summary}})
|
|
}))
|
|
defer upstream.Close()
|
|
client, err := newContinuityReminderClient(upstream.URL, upstream.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result, err := client.Schedule(context.Background(), "Bearer session", continuity.ScheduleReminderRequest{Summary: "позвонить врачу"})
|
|
if err != nil || result.Item.ID != "reminder-1" {
|
|
t.Fatalf("result=%#v err=%v", result, err)
|
|
}
|
|
}
|
|
|
|
func TestReminderSummaryRemovesSchedulingWords(t *testing.T) {
|
|
if summary := reminderSummary("Напомни завтра в 10 позвонить врачу."); summary != "позвонить врачу" || strings.Contains(summary, "10") {
|
|
t.Fatalf("summary = %q", summary)
|
|
}
|
|
}
|
|
|
|
func TestBuildSoftwareAgentInstructionKeepsHumanIntentSeparateFromDerivedContext(t *testing.T) {
|
|
instruction := buildSoftwareAgentInstruction(
|
|
work.PlanningResult{
|
|
Goal: work.Goal{DesiredOutcome: "fix chat wording"},
|
|
Candidate: work.PlanCandidate{Summary: "accepted deterministic software change plan", Steps: []work.PlanStep{{Description: "Inspect chat surface"}, {Description: "Change copy"}, {Description: "Run tests"}}},
|
|
},
|
|
interaction.OriginalHumanIntent{ID: "intent-1", Text: "Исправь текст в чате Assistant."},
|
|
interaction.HumanUtterance{ID: "utterance-1", Content: "Исправь текст в чате Assistant."},
|
|
)
|
|
if !strings.Contains(instruction, "Human asked: Исправь текст в чате Assistant.") {
|
|
t.Fatalf("instruction must preserve human intent: %q", instruction)
|
|
}
|
|
if !strings.Contains(instruction, "Verification: run relevant tests") || !strings.Contains(instruction, "Plan summary: accepted deterministic software change plan") {
|
|
t.Fatalf("instruction must include derived execution context: %q", instruction)
|
|
}
|
|
}
|
|
|
|
func TestAllowCanaryForChatCapabilityIsLimitedToSoftwareEngineeringPath(t *testing.T) {
|
|
if !allowCanaryForChatCapability(capability.CapabilityKindModifyCode) || !allowCanaryForChatCapability(capability.CapabilityKindRunTests) || !allowCanaryForChatCapability(capability.CapabilityKindInspectProject) {
|
|
t.Fatal("software-engineering capabilities must allow canary implementations")
|
|
}
|
|
if allowCanaryForChatCapability(capability.CapabilityKindScheduleReminder) {
|
|
t.Fatal("non software-engineering capability must not implicitly allow canary")
|
|
}
|
|
}
|