Files
Assistant/cmd/continuity-runtime/human_response_test.go
m 41b0ac196e
CI / test (push) Successful in 19s
CI / test (pull_request) Successful in 16s
DEV-000069 include agent delegation response candidates
2026-08-17 20:20:32 +03:00

114 lines
6.2 KiB
Go

package main
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"git.cin.su/m/Assistant/contracts/continuity"
"git.cin.su/m/Assistant/contracts/identity"
continuitydomain "git.cin.su/m/Assistant/modules/continuity/domain"
)
func TestCreatorResponseEndpointAppliesCompleteThroughContinuitySemantics(t *testing.T) {
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v1/creator/sessions/current" || r.Header.Get("Authorization") != "Bearer ok" {
t.Fatal("unexpected identity validation")
}
_ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"})
}))
defer core.Close()
identityClient, err := newCreatorIdentityClient(core.URL, core.Client())
if err != nil {
t.Fatal(err)
}
repository := &fakeResponseRepository{candidates: []continuitydomain.ResponseCandidate{{
Attention: continuity.AttentionItem{ID: "attention-1", OwnerPrincipalID: "principal-1", ScopeID: "scope-1", SourceKind: continuity.AttentionSourceProspectiveReminder, SourceProspectiveID: "prospective-1", State: continuity.AttentionDelivered, Summary: "проверить базу", Revision: 1},
Prospective: continuity.ProspectiveItem{ID: "prospective-1", OwnerPrincipalID: "principal-1", ScopeID: "scope-1", State: continuity.ProspectiveDelivered, Summary: "проверить базу", Revision: 1},
}}}
mux := http.NewServeMux()
registerCreatorResponseRoute(mux, identityClient, repository, func() time.Time { return time.Unix(100, 0) })
request := httptest.NewRequest(http.MethodPost, "/v1/creator/responses", strings.NewReader(`{"SourceUtteranceID":"utterance-1","SourceIntentID":"intent-1","Message":"Готово","RequestedAtUnixMillis":100000}`))
request.Header.Set("Authorization", "Bearer ok")
recorder := httptest.NewRecorder()
mux.ServeHTTP(recorder, request)
if recorder.Code != http.StatusCreated || repository.applied.Operation != continuity.ResponseComplete || repository.applied.Outcome != continuity.ResponseOutcomeUserReportedComplete {
t.Fatalf("status=%d applied=%#v body=%s", recorder.Code, repository.applied, recorder.Body.String())
}
}
func TestCreatorResponseEndpointRecordsAmbiguousWithoutSilentTargetMutation(t *testing.T) {
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"})
}))
defer core.Close()
identityClient, err := newCreatorIdentityClient(core.URL, core.Client())
if err != nil {
t.Fatal(err)
}
repository := &fakeResponseRepository{candidates: []continuitydomain.ResponseCandidate{
{Attention: continuity.AttentionItem{ID: "attention-1", SourceProspectiveID: "prospective-1", State: continuity.AttentionDelivered, Summary: "первое"}},
{Attention: continuity.AttentionItem{ID: "attention-2", SourceProspectiveID: "prospective-2", State: continuity.AttentionDelivered, Summary: "второе"}},
}}
mux := http.NewServeMux()
registerCreatorResponseRoute(mux, identityClient, repository, func() time.Time { return time.Unix(100, 0) })
request := httptest.NewRequest(http.MethodPost, "/v1/creator/responses", strings.NewReader(`{"SourceUtteranceID":"utterance-1","SourceIntentID":"intent-1","Message":"Готово","RequestedAtUnixMillis":100000}`))
request.Header.Set("Authorization", "Bearer ok")
recorder := httptest.NewRecorder()
mux.ServeHTTP(recorder, request)
if recorder.Code != http.StatusCreated || repository.applied.ResolutionState != continuity.ResponseAmbiguous || repository.applied.TargetAttentionID != "" {
t.Fatalf("status=%d applied=%#v body=%s", recorder.Code, repository.applied, recorder.Body.String())
}
}
func TestCreatorResponseEndpointContinuesDelegationAttentionViaNaturalMessage(t *testing.T) {
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"})
}))
defer core.Close()
identityClient, err := newCreatorIdentityClient(core.URL, core.Client())
if err != nil {
t.Fatal(err)
}
repository := &fakeResponseRepository{candidates: []continuitydomain.ResponseCandidate{
{Attention: continuity.AttentionItem{
ID: "work:delegation:delegation-1",
OwnerPrincipalID: "principal-1",
ScopeID: "scope-1",
SourceKind: continuity.AttentionSourceAgentDelegation,
SourceExternalRef: "work:delegation:delegation-1",
State: continuity.AttentionPending,
Summary: "Нужен твой выбор по задаче coding-agent: docs/PROJECT_STATUS.md",
Revision: 1,
}},
{Attention: continuity.AttentionItem{ID: "attention-1", SourceProspectiveID: "prospective-1", State: continuity.AttentionDelivered, Summary: "старое напоминание"}},
}}
mux := http.NewServeMux()
registerCreatorResponseRoute(mux, identityClient, repository, func() time.Time { return time.Unix(100, 0) })
request := httptest.NewRequest(http.MethodPost, "/v1/creator/responses", strings.NewReader(`{"SourceUtteranceID":"utterance-1","SourceIntentID":"intent-1","Message":"Продолжай задачу coding-agent по docs/PROJECT_STATUS.md","RequestedAtUnixMillis":100000}`))
request.Header.Set("Authorization", "Bearer ok")
recorder := httptest.NewRecorder()
mux.ServeHTTP(recorder, request)
if recorder.Code != http.StatusCreated || repository.applied.Operation != continuity.ResponseContinue || repository.applied.TargetExternalRef != "work:delegation:delegation-1" || repository.applied.Outcome != continuity.ResponseOutcomeContinued {
t.Fatalf("status=%d applied=%#v body=%s", recorder.Code, repository.applied, recorder.Body.String())
}
}
type fakeResponseRepository struct {
candidates []continuitydomain.ResponseCandidate
applied continuity.HumanResponse
}
func (f *fakeResponseRepository) ResponseCandidates(context.Context, identity.PrincipalID, int) ([]continuitydomain.ResponseCandidate, error) {
return f.candidates, nil
}
func (f *fakeResponseRepository) ApplyHumanResponse(_ context.Context, response continuity.HumanResponse) error {
f.applied = response
return nil
}