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") } }