feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s
ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package elderagent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProviderCreateAndRecover(t *testing.T) {
|
||||
var created, updated int
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" || r.URL.Query().Get("Version") != "2025-08-01" || r.Header.Get("Authorization") == "" {
|
||||
t.Error("incorrect signed API request")
|
||||
}
|
||||
var body map[string]any
|
||||
json.NewDecoder(r.Body).Decode(&body)
|
||||
switch r.URL.Query().Get("Action") {
|
||||
case "AibotQuery":
|
||||
if body["AccessType"] != "public" {
|
||||
t.Error("name lookup requires public AccessType")
|
||||
}
|
||||
if created == 0 {
|
||||
fmt.Fprint(w, `{"Result":{"Bots":[]}}`)
|
||||
} else {
|
||||
fmt.Fprint(w, `{"Result":{"Bots":[{"Id":"bot-elder","Name":"elder-1"}]}}`)
|
||||
}
|
||||
case "AibotCreate":
|
||||
created++
|
||||
if body["AccessType"] != "public" || body["Config"] == nil || body["Name"] != "elder-1" {
|
||||
t.Error("missing creation fields")
|
||||
}
|
||||
fmt.Fprint(w, `{"Result":{"Id":"bot-elder"}}`)
|
||||
case "AibotUpdate":
|
||||
updated++
|
||||
fmt.Fprint(w, `{"Result":{}}`)
|
||||
default:
|
||||
t.Error("unexpected action")
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
c := NewClient("test-ak", "test-sk", "", server.URL, "")
|
||||
for n := 0; n < 2; n++ {
|
||||
b, err := c.Ensure(context.Background(), "elder-1", map[string]any{"S2SConfig": map[string]any{}}, map[string]any{})
|
||||
if err != nil || b.ID != "bot-elder" {
|
||||
t.Fatal(b, err)
|
||||
}
|
||||
}
|
||||
if created != 1 || updated != 1 {
|
||||
t.Fatal(created, updated)
|
||||
}
|
||||
}
|
||||
func TestConfigurationIsPerElderAndKeepsS2SOnServer(t *testing.T) {
|
||||
s := Service{Template: []byte(`{"templates":{"s2s_hybrid":{"Config":{"S2SConfig":{"Provider":"volcano","OutputMode":1,"ProviderParams":{"app":{"appid":"test-app","token":"test-token"},"dialog":{"system_role":"温暖陪伴","dialog_id":"shared"}}},"MemoryConfig":{"Enable":true}}}}}`), CallbackURL: "https://example.com/callback", CallbackSecret: []byte(strings.Repeat("k", 32))}
|
||||
a, aa, err := s.Configuration("elder-1", "张奶奶")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, ba, err := s.Configuration("elder-2", "李爷爷")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rawA, _ := json.Marshal(a)
|
||||
rawB, _ := json.Marshal(b)
|
||||
if !strings.Contains(string(rawA), "张奶奶") || strings.Contains(string(rawB), "张奶奶") || strings.Contains(string(rawA), "dialog_id") {
|
||||
t.Fatal("elder config isolation failed")
|
||||
}
|
||||
if a["S2SConfig"].(map[string]any)["OutputMode"] != 0 || aa["ServerMessageURLForRTS"] == ba["ServerMessageURLForRTS"] {
|
||||
t.Fatal("incorrect S2S/callback config")
|
||||
}
|
||||
if a["MemoryConfig"].(map[string]any)["Enable"] != false {
|
||||
t.Fatal("inherited shared memory")
|
||||
}
|
||||
}
|
||||
func TestProviderErrorDoesNotExposeConfiguration(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{"ResponseMetadata":{"Error":{"Code":"InvalidParameter","Message":"secret-token"}}}`)
|
||||
}))
|
||||
defer server.Close()
|
||||
_, err := NewClient("ak", "sk", "", server.URL, "").Ensure(context.Background(), "elder", nil, nil)
|
||||
if err == nil || strings.Contains(err.Error(), "secret-token") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersonaReadbackChecksManagedFieldsAndMasksErrors(t *testing.T) {
|
||||
config := map[string]any{"S2SConfig": map[string]any{"ProviderParams": map[string]any{"app": map[string]any{"token": "SECRET_TOKEN"}, "dialog": map[string]any{"system_role": "新角色"}}}, "MemoryConfig": map[string]any{"Enable": false}}
|
||||
agent := map[string]any{"WelcomeMessage": "开场", "ServerMessageURLForRTS": "https://example.com/callback", "ServerMessageSignatureForRTS": "signature"}
|
||||
stale := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("Action") != "AibotQuery" {
|
||||
t.Error("wrong verification action")
|
||||
}
|
||||
role := "新角色"
|
||||
if stale {
|
||||
role = "SECRET_STALE_ROLE"
|
||||
}
|
||||
actual := map[string]any{"S2SConfig": map[string]any{"ProviderParams": map[string]any{"dialog": map[string]any{"system_role": role}}}, "MemoryConfig": map[string]any{"Enable": false}}
|
||||
json.NewEncoder(w).Encode(map[string]any{"Result": map[string]any{"Bots": []Bot{{ID: "bot", Config: actual, AgentConfig: agent}}}})
|
||||
}))
|
||||
defer server.Close()
|
||||
c := NewClient("ak", "sk", "", server.URL, "")
|
||||
if err := c.VerifyConfiguration(context.Background(), "bot", config, agent); err != nil {
|
||||
t.Fatal("masked credentials failed verification", err)
|
||||
}
|
||||
stale = true
|
||||
if err := c.VerifyConfiguration(context.Background(), "bot", config, agent); err == nil || strings.Contains(err.Error(), "SECRET") {
|
||||
t.Fatal("unverified or exposed provider config", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user