Files

97 lines
4.1 KiB
Go
Raw Permalink Normal View History

package callmemory
import (
"encoding/json"
"github.com/google/uuid"
"strings"
"testing"
"time"
)
func testManifest() Manifest {
now := time.Now().UnixMilli()
return Manifest{ContractVersion: ContractVersion, ManifestID: uuid.NewString(), SessionID: uuid.NewString(), DeviceID: "device-test", BootID: uuid.NewString(), HookID: "hook-1", CallGeneration: 1, BindingGeneration: 1, IssuedAt: now, ExpiresAt: now + ManifestMS, Mode: "RTC", BotID: "test-bot", ProfileID: "test-profile", Purpose: "NORMAL", FirstTurnMode: "AI_WELCOME", MemoryMode: "HOT_ONLY", AudioProfileID: "test-audio", VendorParams: `{"explicit":true}`, LeaseMS: LeaseMS, LeaseExpiresAt: now + LeaseMS, MaxCallMS: 3600000}
}
func TestManifestAuthenticationAndSemanticBounds(t *testing.T) {
key := []byte(strings.Repeat("k", 32))
m := testManifest()
b, mac, e := EncodeManifest(key, m)
if e != nil {
t.Fatal(e)
}
got, e := DecodeManifest(key, b, mac)
if e != nil || got.SessionID != m.SessionID {
t.Fatalf("roundtrip: %v", e)
}
if _, e = DecodeManifest(key, b+"A", mac); e == nil {
t.Fatal("accepted tampering")
}
m.LeaseExpiresAt = m.IssuedAt + LeaseMS + 1
b, mac, _ = EncodeManifest(key, m)
if _, e = DecodeManifest(key, b, mac); e == nil {
t.Fatal("accepted signed but unsafe lease")
}
}
func TestStrictJSONRejectsAmbiguity(t *testing.T) {
for _, raw := range []string{`{"action":"ADD","action":"DELETE"}`, `{"patch":{"content":"a","content":"b"}}`, `{} {}`, `{"unknown":true}`, `{"action":"ADD"} trailing`} {
var in MutationInput
if e := StrictJSON([]byte(raw), &in); e == nil {
t.Fatalf("accepted %s", raw)
}
}
}
func TestContextLatestWholeFactsAndClosedItems(t *testing.T) {
now := time.Now()
facts := []ContextFact{
{ID: "old", EntityKey: "trip", Text: "明天去医院", Type: "OPEN_ITEM", ItemStatus: "OPEN", Policy: "ALLOWED", SourceTime: now.Add(-time.Hour)},
{ID: "new", EntityKey: "trip", Text: "已完成就医", Type: "OPEN_ITEM", ItemStatus: "COMPLETED", Policy: "ALLOWED", SourceTime: now},
{ID: "secret", Text: "私密事实", Policy: "RESPOND_ONLY"},
{ID: "long", Text: strings.Repeat("不", 20), Policy: "ALLOWED"},
{ID: "short", Text: "不喜欢咖啡", Policy: "ALLOWED"},
}
if got := BuildContext(facts, 10); got != "不喜欢咖啡" {
t.Fatalf("context lost policy or negation: %q", got)
}
}
func TestProviderGatesAndExplicitOff(t *testing.T) {
p := Profile{ID: "p", SDKVersion: "s", BotID: "b", ModelVersion: "m", Transport: "RTC", Template: map[string]any{}, Mapping: map[string]string{}, Capabilities: map[string]bool{}, Evidence: map[string]string{}}
if _, e := p.Compile("OFF", "secret", "welcome", "user", "assistant", "dialog"); e == nil {
t.Fatal("missing evidence accepted")
}
for _, g := range []string{"H01", "H02", "H04", "H05", "H06", "H07", "H09", "H11"} {
p.Capabilities[g] = true
p.Evidence[g] = "synthetic-test-only"
}
for _, name := range []string{"memory_enabled", "hot_context", "welcome_text", "user_id", "assistant_id", "dialog_id"} {
p.Mapping[name] = "/" + name
p.Template[name] = "stale"
}
raw, e := p.Compile("OFF", "secret", "welcome", "user", "assistant", "dialog")
if e != nil {
t.Fatal(e)
}
var got map[string]any
json.Unmarshal([]byte(raw), &got)
if got["memory_enabled"] != false || got["hot_context"] != "" || got["user_id"] != "" || got["assistant_id"] != "" {
t.Fatal(got)
}
if e = p.Check("NATIVE_VIKING"); e == nil {
t.Fatal("native mode accepted without H03/H12")
}
if mode, e := SelectMemoryMode(43, 41, true, true, true); e != nil || mode != "HOT_ONLY" {
t.Fatal(mode, e)
}
}
func TestMutationConstraints(t *testing.T) {
str := func(s string) *string { return &s }
for _, in := range []MutationInput{
{ID: uuid.NewString(), Action: "DELETE", Patch: MemoryPatch{EntityKey: str("secret")}},
{ID: uuid.NewString(), Action: "ADD", Patch: MemoryPatch{Content: str("done"), Type: str("OPEN_ITEM"), ItemStatus: str("COMPLETED")}},
{ID: uuid.NewString(), Action: "SET_USAGE", Patch: MemoryPatch{Policy: str("NEVER"), Content: str("hidden correction")}},
} {
if e := validateMutation(in); e == nil {
t.Fatal("accepted invalid mutation", in.Action)
}
}
}