74 lines
3.3 KiB
Go
74 lines
3.3 KiB
Go
package rpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.ipao.vip/rogee/go-sip/internal/configread"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func TestApprovedAgentReceivesFrozenASROnlySnapshot(t *testing.T) {
|
|
now := time.Date(2026, 9, 20, 10, 0, 0, 0, time.UTC)
|
|
req := approvedTestRequest(t, now)
|
|
req.TaskConfigJson = bytes.Replace(req.TaskConfigJson, []byte(`"params":{}`), []byte(`"params":{"reqid":"ignored-id","sequence":99,"model_name":"ignored-model","opaque":false}`), 1)
|
|
hash, err := configread.ExecutionBindingSHA256(req.TaskConfigJson, req.ProvidersJson, req.SipRevision)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.BindingSha256 = hash
|
|
attempts := 0
|
|
s := activatedApprovedServer(t, now, filepath.Join(t.TempDir(), "session.json"), req.DispatcherId, 1,
|
|
func(_ context.Context, call ApprovedExecution) error {
|
|
attempts++
|
|
if call.DispatcherID != req.DispatcherId || call.TenantID != req.TenantId || call.TaskID != req.TaskId || call.SIPRevision != 8 || call.SelectedTrunkID != req.SelectedTrunkId {
|
|
t.Fatal("Agent did not receive Dispatcher-authorized call binding")
|
|
}
|
|
if call.AI.Mode != "asr_only" || call.AI.LLM != nil || call.AI.TTS != nil || call.AI.ASR.Provider.Credential != "sk-******" {
|
|
t.Fatal("Agent did not receive the exact approved ASR-only provider snapshot")
|
|
}
|
|
if string(call.AI.ASR.Params["reqid"]) != `"ignored-id"` || string(call.AI.ASR.Params["opaque"]) != "false" {
|
|
t.Fatal("Agent changed the original approved params before request construction")
|
|
}
|
|
return nil
|
|
})
|
|
result, err := s.ExecuteApproved(context.Background(), req)
|
|
if err != nil || result == nil || !result.Accepted || result.CallId != req.CallId || attempts != 1 {
|
|
t.Fatalf("Agent did not accept exactly one mock call: %v %v attempts=%d", result, err, attempts)
|
|
}
|
|
if _, err := s.ExecuteApproved(context.Background(), req); status.Code(err) != codes.FailedPrecondition || attempts != 1 {
|
|
t.Fatalf("accepted call cannot be replayed: err=%v attempts=%d", err, attempts)
|
|
}
|
|
}
|
|
|
|
func TestApprovedAgentRejectsMalformedParamsWithoutPersistingAttempt(t *testing.T) {
|
|
now := time.Date(2026, 9, 20, 10, 0, 0, 0, time.UTC)
|
|
req := approvedTestRequest(t, now)
|
|
req.TaskConfigJson = bytes.Replace(req.TaskConfigJson, []byte(`"params":{}`), []byte(`"params":{"nested":{"unexpected":true}}`), 1)
|
|
if !strings.Contains(string(req.TaskConfigJson), `"nested":{"unexpected":true}`) {
|
|
t.Fatal("malformed params were not inserted into fixture")
|
|
}
|
|
hash, err := configread.ExecutionBindingSHA256(req.TaskConfigJson, req.ProvidersJson, req.SipRevision)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.BindingSha256 = hash
|
|
state := filepath.Join(t.TempDir(), "session.json")
|
|
attempts := 0
|
|
s := activatedApprovedServer(t, now, state, req.DispatcherId, 1,
|
|
func(context.Context, ApprovedExecution) error { attempts++; return nil })
|
|
_, err = s.ExecuteApproved(context.Background(), req)
|
|
if status.Code(err) != codes.InvalidArgument || attempts != 0 || strings.Contains(err.Error(), "example-only-not-a-real-secret") {
|
|
t.Fatalf("malformed AI must fail before dialing or leaking credentials: attempts=%d err=%v", attempts, err)
|
|
}
|
|
if _, statErr := os.Stat(state + ".approved"); !os.IsNotExist(statErr) {
|
|
t.Fatalf("no attempt should be durable before AI snapshot validation: %v", statErr)
|
|
}
|
|
}
|