Files
synapbus/internal/actions/registry_test.go
T
Algis DumbrisandClaude Opus 4.6 8df22457ab feat: trust scores, claim semantics, state-change webhooks (011-trust-claims-triggers)
Trust scores: per (agent, action_type) pair, stored in agent_trust
table. Auto-adjusts when human reacts to AI agent messages (approve
+0.05, reject -0.1). Scores clamped [0.0, 1.0]. MCP get_trust action
+ REST API /api/trust/{agent}. Web UI shows trust progress bars on
agent detail pages.

Claim semantics: only one in_progress reaction per message enforced.
First agent to claim wins, duplicates rejected with clear error.

State-change webhooks: StateChangeNotifier interface fires
workflow.state_changed events through existing webhook infrastructure
when reactions change a message's derived workflow state.

Channel thresholds: publish_threshold and approve_threshold fields
on channels for configuring autonomy gates.

Migration 014_trust_claims.sql. 17 new test cases across trust
model + store.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 21:41:54 +02:00

134 lines
3.0 KiB
Go

package actions
import (
"testing"
)
func TestRegistryHas28Actions(t *testing.T) {
r := NewRegistry()
got := len(r.List())
if got != 28 {
t.Errorf("expected 28 actions, got %d", got)
}
}
func TestRegistryCategories(t *testing.T) {
r := NewRegistry()
tests := []struct {
category string
want int
}{
{"messaging", 7},
{"channels", 9},
{"swarm", 5},
{"attachments", 2},
{"reactions", 4},
{"trust", 1},
}
for _, tt := range tests {
t.Run(tt.category, func(t *testing.T) {
got := len(r.ListByCategory(tt.category))
if got != tt.want {
t.Errorf("category %q: expected %d actions, got %d", tt.category, tt.want, got)
}
})
}
}
func TestRegistryGetByName(t *testing.T) {
r := NewRegistry()
allNames := []string{
// messaging
"my_status", "send_message", "read_inbox", "claim_messages", "mark_done", "search_messages", "discover_agents",
// channels
"create_channel", "join_channel", "leave_channel", "list_channels",
"invite_to_channel", "kick_from_channel", "get_channel_messages",
"send_channel_message", "update_channel",
// swarm
"post_task", "bid_task", "accept_bid", "complete_task", "list_tasks",
// attachments
"upload_attachment", "download_attachment",
// reactions
"react", "unreact", "get_reactions", "list_by_state",
// trust
"get_trust",
}
for _, name := range allNames {
t.Run(name, func(t *testing.T) {
a, ok := r.Get(name)
if !ok {
t.Fatalf("action %q not found in registry", name)
}
if a.Name != name {
t.Errorf("expected name %q, got %q", name, a.Name)
}
})
}
}
func TestRegistryGetNotFound(t *testing.T) {
r := NewRegistry()
_, ok := r.Get("nonexistent_action")
if ok {
t.Error("expected Get to return false for nonexistent action")
}
}
func TestRegistryActionsHaveExamples(t *testing.T) {
r := NewRegistry()
for _, a := range r.List() {
t.Run(a.Name, func(t *testing.T) {
if len(a.Examples) == 0 {
t.Errorf("action %q has no examples", a.Name)
}
})
}
}
func TestRegistryActionsHaveDescriptions(t *testing.T) {
r := NewRegistry()
for _, a := range r.List() {
t.Run(a.Name, func(t *testing.T) {
if a.Description == "" {
t.Errorf("action %q has empty description", a.Name)
}
})
}
}
func TestRegistryActionsHaveReturns(t *testing.T) {
r := NewRegistry()
for _, a := range r.List() {
t.Run(a.Name, func(t *testing.T) {
if a.Returns == "" {
t.Errorf("action %q has empty Returns field", a.Name)
}
})
}
}
func TestRegistryListByUnknownCategory(t *testing.T) {
r := NewRegistry()
got := r.ListByCategory("nonexistent")
if len(got) != 0 {
t.Errorf("expected 0 actions for unknown category, got %d", len(got))
}
}
func TestRegistryListReturnsCopy(t *testing.T) {
r := NewRegistry()
list1 := r.List()
list2 := r.List()
// Mutating the first list should not affect the second.
if len(list1) > 0 {
list1[0].Name = "mutated"
if list2[0].Name == "mutated" {
t.Error("List() should return a copy, not a reference to internal slice")
}
}
}