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.
114 lines
4.6 KiB
Go
114 lines
4.6 KiB
Go
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)
|
|
}
|
|
}
|