simplify SaaS SIP configuration response

This commit is contained in:
2026-09-28 16:06:24 +08:00
parent bd2e5ad2b1
commit efb2b86f5e
23 changed files with 700 additions and 169 deletions
+26 -19
View File
@@ -38,10 +38,6 @@ type Snapshot struct {
TenantID string
TenantKey string
SIPRevision int64
SIPSnapshotSHA256 string
SIPCellID string
SIPArtifactRevision int64
SIPArtifactConfigSHA256 string
TaskRevision int64
TaskStatus string
TaskMaxConcurrentCalls int64
@@ -122,10 +118,23 @@ func (c *Client) ReadTask(ctx context.Context, taskID, tenantID string) (Snapsho
if err := json.Unmarshal(sipBody, &sip); err != nil {
return Snapshot{}, fmt.Errorf("decode SIP configuration identity: %w", err)
}
if sip.Resource != resourceSIPConfig || sip.DispatcherID != c.dispatcherID || sip.Revision <= 0 || sip.SnapshotSHA256 == "" ||
sip.Artifact.CellID == "" || sip.Artifact.Revision <= 0 || sip.Artifact.ConfigSHA256 == "" {
if sip.Resource != resourceSIPConfig || sip.DispatcherID != c.dispatcherID || sip.Revision <= 0 {
return Snapshot{}, errors.New("SIP configuration identity or revision does not match the request")
}
seenTrunks := make(map[string]bool, len(sip.Trunks))
for _, trunk := range sip.Trunks {
if seenTrunks[trunk.TrunkID] {
return Snapshot{}, fmt.Errorf("SIP configuration repeats trunk %q", trunk.TrunkID)
}
seenTrunks[trunk.TrunkID] = true
seenCallers := make(map[string]bool, len(trunk.CallerProfiles))
for _, caller := range trunk.CallerProfiles {
if seenCallers[caller.CallerProfileID] {
return Snapshot{}, fmt.Errorf("SIP trunk %q repeats caller profile %q", trunk.TrunkID, caller.CallerProfileID)
}
seenCallers[caller.CallerProfileID] = true
}
}
taskPath := configReadPath + "/task/" + url.PathEscape(taskID)
taskBody, err := c.getConfig(ctx, taskPath)
@@ -165,10 +174,8 @@ func (c *Client) ReadTask(ctx context.Context, taskID, tenantID string) (Snapsho
return Snapshot{
TaskID: taskID, TenantID: tenantID, TenantKey: task.TenantKey,
SIPRevision: sip.Revision, SIPSnapshotSHA256: sip.SnapshotSHA256,
SIPCellID: sip.Artifact.CellID, SIPArtifactRevision: sip.Artifact.Revision,
SIPArtifactConfigSHA256: sip.Artifact.ConfigSHA256,
TaskRevision: task.TaskRevision, TaskStatus: task.Status,
SIPRevision: sip.Revision,
TaskRevision: task.TaskRevision, TaskStatus: task.Status,
TaskMaxConcurrentCalls: int64(task.MaxConcurrentCalls), TaskRingTimeoutMS: int64(task.RingTimeoutMS),
TaskMaxCallDurationMS: int64(task.MaxCallDurationMS), TaskRoutePolicyID: task.RoutePolicyID,
TaskCallerProfileID: task.CallerProfileID, TaskAllowedTrunkIDs: append([]string(nil), task.AllowedTrunkIDs...),
@@ -307,15 +314,15 @@ func responseErrorCode(body []byte) string {
}
type sipConfigResponse struct {
Resource string `json:"resource"`
DispatcherID string `json:"dispatcher_id"`
Revision int64 `json:"revision"`
SnapshotSHA256 string `json:"snapshot_sha256"`
Artifact struct {
CellID string `json:"cell_id"`
Revision int64 `json:"revision"`
ConfigSHA256 string `json:"config_sha256"`
} `json:"artifact"`
Resource string `json:"resource"`
DispatcherID string `json:"dispatcher_id"`
Revision int64 `json:"revision"`
Trunks []struct {
TrunkID string `json:"trunk_id"`
CallerProfiles []struct {
CallerProfileID string `json:"caller_profile_id"`
} `json:"caller_profiles"`
} `json:"trunks"`
}
type taskConfigResponse struct {
+33 -1
View File
@@ -67,6 +67,38 @@ func TestClientReadTaskRequestsAndValidatesThreeConfigEndpoints(t *testing.T) {
}
}
func TestClientReadTaskRejectsDuplicateSIPTrunksAndCallers(t *testing.T) {
for _, tc := range []struct {
name string
mutate func(map[string]any)
}{
{"duplicate trunk", func(sip map[string]any) {
trunks := sip["trunks"].([]any)
sip["trunks"] = append(trunks, trunks[0])
}},
{"duplicate caller", func(sip map[string]any) {
trunk := sip["trunks"].([]any)[0].(map[string]any)
profiles := trunk["caller_profiles"].([]any)
trunk["caller_profiles"] = append(profiles, profiles[0])
}},
} {
t.Run(tc.name, func(t *testing.T) {
fixtures := validConfigFixtures(t)
var sip map[string]any
if err := json.Unmarshal(fixtures[configReadPath+"/sip"], &sip); err != nil {
t.Fatal(err)
}
tc.mutate(sip)
fixtures[configReadPath+"/sip"] = marshalDiscoveryResponse(t, sip)
server := configFixtureServer(t, fixtures)
defer server.Close()
if _, err := newMockClient(t, server).ReadTask(context.Background(), mockTaskID, mockTenantID); err == nil {
t.Fatal("duplicate SIP configuration identity accepted")
}
})
}
}
func TestClientReadTaskRejectsSchemaInvalidResponse(t *testing.T) {
fixtures := validConfigFixtures(t)
fixtures[configReadPath+"/sip"] = readConfigFixture(t, "config-read-invalid-extra-property-v0.1.json")
@@ -139,7 +171,7 @@ func newMockClient(t *testing.T, server *httptest.Server) *Client {
func validConfigFixtures(t *testing.T) map[string][]byte {
t.Helper()
return map[string][]byte{
configReadPath + "/sip": readConfigFixture(t, "config-read-sip-v0.1.json"),
configReadPath + "/sip": readConfigFixture(t, "config-read-sip-v0.2.json"),
configReadPath + "/tasks": readConfigFixture(t, "task-discovery-snapshot-v0.2.json"),
configReadPath + "/task/" + mockTaskID: readConfigFixture(t, "config-read-task-v0.1.json"),
configReadPath + "/tenant/" + mockTenantID + "/quota": readConfigFixture(t, "config-read-tenant-quota-v0.1.json"),
+1 -1
View File
@@ -69,7 +69,7 @@ func ValidateEvent(raw []byte) error {
}
func ValidateLocalConfigRead(raw []byte) error {
return validateLocalSchema("config-read-v0.1.schema.json", raw)
return validateLocalSchema("config-read-v0.2.schema.json", raw)
}
func ValidateLocalTaskDiscoveryV04(raw []byte) error {
+2
View File
@@ -62,6 +62,8 @@ func localSchemaVersion(name string) string {
switch name {
case "config-read-v0.1.schema.json", "command-next-v0.1-proposal.schema.json", "call-result-v0.1-proposal.schema.json", "local-mock-recording-failure-v0.1.schema.json":
return "v0.1"
case "config-read-v0.2.schema.json":
return "v0.2"
case "task-discovery-v0.3-proposal.schema.json":
return "v0.3"
case "task-discovery-v0.4-proposal.schema.json", "task-control-v0.4-proposal.schema.json", "call-execute-v0.4-proposal.schema.json":
+8 -14
View File
@@ -2,8 +2,6 @@ package dispatcher
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"strconv"
@@ -37,22 +35,18 @@ func (v *AgentSIPConfigVerifier) VerifyAppliedSIPConfig(ctx context.Context, sna
if v == nil || v.Probe == nil || v.AgentID == "" || v.CellID == "" {
return errors.New("Agent status probe, agent ID, and configured cell ID are required")
}
if snapshot.SIPCellID != v.CellID {
return fmt.Errorf("SIP config targets cell %q, configured cell is %q", snapshot.SIPCellID, v.CellID)
}
status, err := v.Probe.Probe(ctx, v.AgentID, v.CellID)
if err != nil {
return fmt.Errorf("probe Agent applied SIP config: %w", err)
}
return verifyAppliedSIPConfigStatus(status, v.AgentID, snapshot)
return verifyAppliedSIPConfigStatus(status, v.AgentID, v.CellID, snapshot)
}
func verifyAppliedSIPConfigStatus(status *agentv1.AgentStatus, agentID string, snapshot configread.Snapshot) error {
digest, digestErr := hex.DecodeString(snapshot.SIPArtifactConfigSHA256)
if snapshot.SIPCellID == "" || snapshot.SIPArtifactRevision <= 0 || digestErr != nil || len(digest) != sha256.Size {
return errors.New("SIP config artifact identity is incomplete")
func verifyAppliedSIPConfigStatus(status *agentv1.AgentStatus, agentID, cellID string, snapshot configread.Snapshot) error {
if snapshot.SIPRevision <= 0 {
return errors.New("SIP config revision is missing")
}
if status == nil || status.AgentId != agentID || status.CellId != snapshot.SIPCellID || status.BootId == "" {
if status == nil || status.AgentId != agentID || status.CellId != cellID || status.BootId == "" {
return errors.New("Agent status identity or boot ID is invalid")
}
var appliedSIP *agentv1.AppliedConfig
@@ -68,9 +62,9 @@ func verifyAppliedSIPConfigStatus(status *agentv1.AgentStatus, agentID string, s
if appliedSIP == nil || appliedSIP.State != AppliedConfigStateApplied || appliedSIP.ObservedAtUnixMs <= 0 {
return errors.New("Agent has not reported one observed, applied SIP config")
}
revision := strconv.FormatInt(snapshot.SIPArtifactRevision, 10)
if appliedSIP.Revision != revision || appliedSIP.ConfigSha256 != snapshot.SIPArtifactConfigSHA256 {
return fmt.Errorf("Agent applied SIP config does not match revision %s and expected SHA-256", revision)
revision := strconv.FormatInt(snapshot.SIPRevision, 10)
if appliedSIP.Revision != revision {
return fmt.Errorf("Agent applied SIP config does not match revision %s", revision)
}
return nil
}
-4
View File
@@ -102,10 +102,6 @@ func (d *Dispatcher) LoadProjectConfig(ctx context.Context, client *configread.C
for key, current := range d.projectConfigs {
current.SIP = append(current.SIP[:0], snapshot.SIP...)
current.SIPRevision = snapshot.SIPRevision
current.SIPSnapshotSHA256 = snapshot.SIPSnapshotSHA256
current.SIPCellID = snapshot.SIPCellID
current.SIPArtifactRevision = snapshot.SIPArtifactRevision
current.SIPArtifactConfigSHA256 = snapshot.SIPArtifactConfigSHA256
current.ExpiresAt = minTime(current.ExpiresAt, sharedExpiry)
if key.tenantID == snapshot.TenantID {
current.TenantQuota = append(current.TenantQuota[:0], snapshot.TenantQuota...)
+10 -32
View File
@@ -40,16 +40,8 @@ type dialTaskConfig struct {
}
type dialSIPConfig struct {
Artifact struct {
Mode string `json:"mode"`
AllowedTargets []string `json:"allowed_targets"`
Trunks []struct {
TrunkID string `json:"trunk_id"`
Enabled bool `json:"enabled"`
CallerProfileIDs []string `json:"caller_profile_ids"`
} `json:"trunks"`
} `json:"artifact"`
TrunkDetails []struct {
Trunks []struct {
Enabled bool `json:"enabled"`
TrunkID string `json:"trunk_id"`
MaxConcurrentCalls *int64 `json:"max_concurrent_calls"`
CallerProfiles []struct {
@@ -57,7 +49,7 @@ type dialSIPConfig struct {
CallerID string `json:"caller_id"`
} `json:"caller_profiles"`
Schedule callwindow.WeeklySchedule `json:"schedule"`
} `json:"trunk_details"`
} `json:"trunks"`
}
// A line is chosen once at admission, in the task's listed order. The second
@@ -74,8 +66,8 @@ func checkSelectedDialPolicy(snapshot configread.Snapshot, callee, trunkID strin
}
func evaluateDialPolicy(snapshot configread.Snapshot, callee, selectedTrunk string, at time.Time, newAdmission bool) (dialDecision, error) {
// This project-wide allowlist is never inferred from an artifact or rewritten
// using a provider prefix. Provider targets further restrict this set.
// The project-wide allowlist is local, never inferred from SaaS or
// rewritten using a provider prefix.
if callee != "15003164745" && callee != "15830461047" {
return dialDecision{}, errors.New("callee is not on the approved outbound whitelist")
}
@@ -96,9 +88,6 @@ func evaluateDialPolicy(snapshot configread.Snapshot, callee, selectedTrunk stri
if task.Status != "running" || len(task.AllowedTrunkIDs) == 0 || task.CallerProfileID == "" {
return dialDecision{}, errors.New("task is not authorized for dialing")
}
if !slices.Contains(sip.Artifact.AllowedTargets, callee) {
return dialDecision{}, errors.New("callee is not in the approved SIP artifact")
}
if task.RingTimeoutMS <= 0 || task.MaxCallDurationMS <= 0 || task.Agent.Config.Conversation.MaxDurationMS <= 0 {
return dialDecision{}, errors.New("task or AI call duration is missing")
}
@@ -118,25 +107,14 @@ func evaluateDialPolicy(snapshot configread.Snapshot, callee, selectedTrunk stri
if selectedTrunk != "" && candidate != selectedTrunk {
continue
}
var artifactTrunk *struct {
TrunkID string `json:"trunk_id"`
Enabled bool `json:"enabled"`
CallerProfileIDs []string `json:"caller_profile_ids"`
}
for i := range sip.Artifact.Trunks {
if sip.Artifact.Trunks[i].TrunkID == candidate {
artifactTrunk = &sip.Artifact.Trunks[i]
break
}
}
if artifactTrunk == nil || !artifactTrunk.Enabled || !slices.Contains(artifactTrunk.CallerProfileIDs, task.CallerProfileID) {
lastReason = fmt.Errorf("trunk %s is disabled or has no approved caller profile", candidate)
continue
}
for _, detail := range sip.TrunkDetails {
for _, detail := range sip.Trunks {
if detail.TrunkID != candidate {
continue
}
if !detail.Enabled {
lastReason = fmt.Errorf("trunk %s is disabled", candidate)
break
}
if detail.MaxConcurrentCalls == nil || *detail.MaxConcurrentCalls <= 0 {
lastReason = fmt.Errorf("trunk %s has no authorized capacity", candidate)
break
+9 -17
View File
@@ -20,13 +20,13 @@ func policyAt(t *testing.T, value string) time.Time {
func policySnapshot(t *testing.T) configread.Snapshot {
t.Helper()
sip := localConfigFixture(t, "config-read-sip-v0.1.json")
sip := localConfigFixture(t, "config-read-sip-v0.2.json")
var document map[string]any
if err := json.Unmarshal(sip, &document); err != nil {
t.Fatal(err)
}
// Explicit local Mock capacity; the documented null remains an unknown real limit.
document["trunk_details"].([]any)[0].(map[string]any)["max_concurrent_calls"] = 3
document["trunks"].([]any)[0].(map[string]any)["max_concurrent_calls"] = 3
sip, err := json.Marshal(document)
if err != nil {
t.Fatal(err)
@@ -77,11 +77,11 @@ func TestSelectDialPolicyRequiresKnownCapacityCallerAndWhitelist(t *testing.T) {
name string
alter func()
}{
{"unknown trunk capacity", func() { sip["trunk_details"].([]any)[0].(map[string]any)["max_concurrent_calls"] = nil }},
{"unknown trunk capacity", func() { sip["trunks"].([]any)[0].(map[string]any)["max_concurrent_calls"] = nil }},
{"no matching caller", func() {
sip["artifact"].(map[string]any)["trunks"].([]any)[0].(map[string]any)["caller_profile_ids"] = []string{"other"}
sip["trunks"].([]any)[0].(map[string]any)["caller_profiles"].([]any)[0].(map[string]any)["caller_profile_id"] = "other"
}},
{"trunk disabled", func() { sip["artifact"].(map[string]any)["trunks"].([]any)[0].(map[string]any)["enabled"] = false }},
{"trunk disabled", func() { sip["trunks"].([]any)[0].(map[string]any)["enabled"] = false }},
} {
t.Run(tc.name, func(t *testing.T) {
var item map[string]any
@@ -121,21 +121,13 @@ func TestSelectDialPolicyUsesFirstEligibleTrunkWithoutPostSelectionSwitch(t *tes
if err := json.Unmarshal(snapshot.SIP, &sip); err != nil {
t.Fatal(err)
}
artifact := sip["artifact"].(map[string]any)
first := artifact["trunks"].([]any)[0].(map[string]any)
first := sip["trunks"].([]any)[0].(map[string]any)
second := make(map[string]any)
for k, v := range first {
second[k] = v
}
second["trunk_id"] = "trunk-second"
artifact["trunks"] = append(artifact["trunks"].([]any), second)
details := sip["trunk_details"].([]any)[0].(map[string]any)
secondDetails := make(map[string]any)
for k, v := range details {
secondDetails[k] = v
}
secondDetails["trunk_id"] = "trunk-second"
sip["trunk_details"] = append(sip["trunk_details"].([]any), secondDetails)
sip["trunks"] = append(sip["trunks"].([]any), second)
snapshot.TaskAllowedTrunkIDs = []string{"trunk-mock", "trunk-second"}
var task map[string]any
if err := json.Unmarshal(snapshot.Task, &task); err != nil {
@@ -143,9 +135,9 @@ func TestSelectDialPolicyUsesFirstEligibleTrunkWithoutPostSelectionSwitch(t *tes
}
task["allowed_trunk_ids"] = snapshot.TaskAllowedTrunkIDs
snapshot.Task, _ = json.Marshal(task)
firstWindow := details["schedule"].(map[string]any)["weekly_windows"].(map[string]any)
firstWindow := first["schedule"].(map[string]any)["weekly_windows"].(map[string]any)
firstWindow["monday"] = []any{map[string]any{"start": "09:00", "end": "10:00"}}
secondDetails["schedule"] = map[string]any{"time_zone": "Asia/Shanghai", "weekly_windows": map[string]any{
second["schedule"] = map[string]any{"time_zone": "Asia/Shanghai", "weekly_windows": map[string]any{
"monday": []any{map[string]any{"start": "10:00", "end": "11:00"}},
"tuesday": []any{}, "wednesday": []any{}, "thursday": []any{}, "friday": []any{}, "saturday": []any{}, "sunday": []any{},
}}
@@ -35,7 +35,7 @@ func TestLocalDispatcherMockSaaSEndToEndWithOutboxRecovery(t *testing.T) {
// The published example intentionally leaves supplier capacity unknown;
// only this isolated Mock grants a positive, explicit trunk limit.
fixtures := map[string][]byte{
"/internal/v1/dispatcher/sip": localConfigFixture(t, "config-read-sip-v0.1.json"),
"/internal/v1/dispatcher/sip": localConfigFixture(t, "config-read-sip-v0.2.json"),
"/internal/v1/dispatcher/task/" + localTestTaskID: readLocalFixture(t, "config-read-task-v0.1.json"),
"/internal/v1/dispatcher/tenant/" + localTestTenantID + "/quota": readLocalFixture(t, "config-read-tenant-quota-v0.1.json"),
}
+8 -8
View File
@@ -541,7 +541,7 @@ func localExecuteTaskCommandBody(t *testing.T, commandID, taskID string, referen
func newLocalV01TestDispatcher(t *testing.T, now time.Time) (*Dispatcher, *store.Store, *httptest.Server) {
t.Helper()
mux := http.NewServeMux()
mux.HandleFunc("/internal/v1/dispatcher/sip", localConfigResponse(string(localConfigFixture(t, "config-read-sip-v0.1.json"))))
mux.HandleFunc("/internal/v1/dispatcher/sip", localConfigResponse(string(localConfigFixture(t, "config-read-sip-v0.2.json"))))
mux.HandleFunc("/internal/v1/dispatcher/tasks", func(w http.ResponseWriter, r *http.Request) {
after := r.URL.Query().Get("after")
if after == "" || after == "0" {
@@ -639,7 +639,7 @@ func newLocalV01MultiTaskConfigServer(t *testing.T, taskIDs []string, quotaRevis
t.Fatal(err)
}
mux := http.NewServeMux()
mux.HandleFunc("/internal/v1/dispatcher/sip", localConfigResponse(string(localConfigFixture(t, "config-read-sip-v0.1.json"))))
mux.HandleFunc("/internal/v1/dispatcher/sip", localConfigResponse(string(localConfigFixture(t, "config-read-sip-v0.2.json"))))
mux.HandleFunc("/internal/v1/dispatcher/tasks", localConfigResponse(string(discoveryBody)))
mux.HandleFunc("/internal/v1/dispatcher/task/", func(w http.ResponseWriter, r *http.Request) {
taskID := r.URL.Path[len("/internal/v1/dispatcher/task/"):]
@@ -657,13 +657,13 @@ func newLocalV01MultiTaskConfigServer(t *testing.T, taskIDs []string, quotaRevis
func localTestSIPConfigVerifier() SIPConfigVerifier {
return SIPConfigVerifierFunc(func(_ context.Context, snapshot configread.Snapshot) error {
status := &agentv1.AgentStatus{
AgentId: "agent-mock", CellId: snapshot.SIPCellID, BootId: "boot-mock",
AgentId: "agent-mock", CellId: "cell-mock", BootId: "boot-mock",
AppliedConfigs: []*agentv1.AppliedConfig{{
Kind: AppliedConfigKindSIP, Revision: fmt.Sprint(snapshot.SIPArtifactRevision),
ConfigSha256: snapshot.SIPArtifactConfigSHA256, State: AppliedConfigStateApplied, ObservedAtUnixMs: 1,
Kind: AppliedConfigKindSIP, Revision: fmt.Sprint(snapshot.SIPRevision),
State: AppliedConfigStateApplied, ObservedAtUnixMs: 1,
}},
}
return verifyAppliedSIPConfigStatus(status, "agent-mock", snapshot)
return verifyAppliedSIPConfigStatus(status, "agent-mock", "cell-mock", snapshot)
})
}
@@ -673,14 +673,14 @@ func localConfigFixture(t *testing.T, name string) []byte {
if err != nil {
t.Fatal(err)
}
if name == "config-read-sip-v0.1.json" {
if name == "config-read-sip-v0.2.json" {
var response map[string]any
if err := json.Unmarshal(body, &response); err != nil {
t.Fatal(err)
}
// Only the isolated Mock is granted explicit capacity; the documented null
// remains unknown and must not authorize a real supplier trunk.
response["trunk_details"].([]any)[0].(map[string]any)["max_concurrent_calls"] = 3
response["trunks"].([]any)[0].(map[string]any)["max_concurrent_calls"] = 3
body, err = json.Marshal(response)
if err != nil {
t.Fatal(err)
+7 -13
View File
@@ -22,17 +22,14 @@ func (p *staticAgentStatusProbe) Probe(_ context.Context, agentID, cellID string
return p.status, p.err
}
func TestAgentSIPConfigVerifierRequiresExactAppliedArtifact(t *testing.T) {
snapshot := configread.Snapshot{
SIPCellID: "cell-a", SIPArtifactRevision: 7,
SIPArtifactConfigSHA256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
}
func TestAgentSIPConfigVerifierRequiresExactAppliedRevision(t *testing.T) {
snapshot := configread.Snapshot{SIPRevision: 7}
newStatus := func() *agentv1.AgentStatus {
return &agentv1.AgentStatus{
AgentId: "agent-a", CellId: snapshot.SIPCellID, BootId: "boot-a",
AgentId: "agent-a", CellId: "cell-a", BootId: "boot-a",
AppliedConfigs: []*agentv1.AppliedConfig{{
Kind: AppliedConfigKindSIP, Revision: fmt.Sprint(snapshot.SIPArtifactRevision),
ConfigSha256: snapshot.SIPArtifactConfigSHA256, State: AppliedConfigStateApplied,
Kind: AppliedConfigKindSIP, Revision: fmt.Sprint(snapshot.SIPRevision),
State: AppliedConfigStateApplied,
ObservedAtUnixMs: 1000,
}},
}
@@ -47,7 +44,7 @@ func TestAgentSIPConfigVerifierRequiresExactAppliedArtifact(t *testing.T) {
}
wrongCellProbe := &staticAgentStatusProbe{status: newStatus()}
wrongCellVerifier := &AgentSIPConfigVerifier{Probe: wrongCellProbe, AgentID: "agent-a", CellID: "cell-b"}
if err := wrongCellVerifier.VerifyAppliedSIPConfig(context.Background(), snapshot); err == nil || wrongCellProbe.gotCellID != "" {
if err := wrongCellVerifier.VerifyAppliedSIPConfig(context.Background(), snapshot); err == nil || wrongCellProbe.gotCellID != "cell-b" {
t.Fatal("SIP config for a different configured cell was probed or accepted")
}
var nilVerifier *AgentSIPConfigVerifier
@@ -69,7 +66,6 @@ func TestAgentSIPConfigVerifierRequiresExactAppliedArtifact(t *testing.T) {
{name: "wrong kind", status: newStatus()},
{name: "not applied", status: newStatus()},
{name: "wrong revision", status: newStatus()},
{name: "wrong digest", status: newStatus()},
{name: "missing observation time", status: newStatus()},
{name: "probe failure", status: newStatus(), err: errors.New("status unavailable")},
}
@@ -83,15 +79,13 @@ func TestAgentSIPConfigVerifierRequiresExactAppliedArtifact(t *testing.T) {
case "missing boot ID":
c.status.BootId = ""
case "multiple SIP applied configs":
c.status.AppliedConfigs = append(c.status.AppliedConfigs, &agentv1.AppliedConfig{Kind: AppliedConfigKindSIP, Revision: "7", ConfigSha256: snapshot.SIPArtifactConfigSHA256, State: AppliedConfigStateApplied, ObservedAtUnixMs: 2000})
c.status.AppliedConfigs = append(c.status.AppliedConfigs, &agentv1.AppliedConfig{Kind: AppliedConfigKindSIP, Revision: "7", State: AppliedConfigStateApplied, ObservedAtUnixMs: 2000})
case "wrong kind":
c.status.AppliedConfigs[0].Kind = "media"
case "not applied":
c.status.AppliedConfigs[0].State = "pending"
case "wrong revision":
c.status.AppliedConfigs[0].Revision = "6"
case "wrong digest":
c.status.AppliedConfigs[0].ConfigSha256 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
case "missing observation time":
c.status.AppliedConfigs[0].ObservedAtUnixMs = 0
}