clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

main
tinyrelay / internal / daemon / agent_self_test.go
agent_self_test.go 5.7K
1package daemon23import (4 "context"5 "encoding/json"6 "strconv"7 "strings"8 "testing"9 "time"1011 "github.com/FelineStateMachine/tinyrelay/internal/event"12)1314func browseGrantAs(t *testing.T, tenant *Tenant, actor, params string) grantSelf {15 t.Helper()16 var raw []json.RawMessage17 if params != "" {18 raw = []json.RawMessage{json.RawMessage(params)}19 }20 result, err := tenant.Execute(context.Background(), actor, "browsegrant", raw)21 if err != nil {22 t.Fatalf("browsegrant by %q: %v", actor, err)23 }24 self, ok := result.(grantSelf)25 if !ok {26 t.Fatalf("browsegrant result %T", result)27 }28 return self29}3031func TestBrowseGrantReportsOwnGrantWithoutOperatorRole(t *testing.T) {32 _, tenant := testTenant(t)33 ctx := context.Background()34 owner := tenant.Policy().Owner35 agent, _ := event.PublicKey(testAgentSecret)36 member, _ := event.PublicKey(testMemberSecret)37 now := time.Now().Unix()38 raw, _ := json.Marshal(map[string]string{"role": "member"})39 if _, err := tenant.Execute(ctx, owner, "setmember", []json.RawMessage{json.RawMessage(strconv.Quote(member)), raw}); err != nil {40 t.Fatal(err)41 }42 if err := publishAs(t, tenant, agentGrantEvent(t, agent, now, now+3600, []string{"k", "9"}, []string{"k", "1"}, []string{"room", "build"}, []string{"jobs", "serve"}, []string{"rate", "30"})); err != nil {43 t.Fatal(err)44 }45 if err := publishAs(t, tenant, signedEvent(t, testAgentSecret, 1, now-5, nil, "hello")); err != nil {46 t.Fatal(err)47 }48 needs := `{"rooms":["build","ops"],"kinds":[9,12,43002,43001,0]}`4950 // The agent sees its own grant, state and what its needs lack.51 self := browseGrantAs(t, tenant, agent, needs)52 if !self.Member || self.Role != "agent" || self.State != "active" || !self.Enforced || self.Grant == nil || self.Grant.Name != "helper" || self.Grant.LastEvent != now-5 {53 t.Fatalf("agent self = %+v", self)54 }55 if self.Rate != 30 || self.Expires != now+3600 || self.Allows == nil || strings.Join(self.Allows.Rooms, ",") != "build" || self.Allows.Jobs != "serve" || len(self.Allows.Kinds) != 2 || self.Allows.Kinds[0] != 9 {56 t.Fatalf("agent allows = %+v rate %d expires %d", self.Allows, self.Rate, self.Expires)57 }58 if self.Missing == nil || strings.Join(self.Missing.Rooms, ",") != "ops" || len(self.Missing.Kinds) != 2 || self.Missing.Kinds[0] != 12 || self.Missing.Kinds[1] != 43001 {59 t.Fatalf("agent missing = %+v", self.Missing)60 }61 if encoded, _ := json.Marshal(self); !strings.Contains(string(encoded), `"grant":{`) || !strings.Contains(string(encoded), `"allows":{`) {62 t.Fatalf("wire shape %s", encoded)63 }6465 // Without needs there is no missing section.66 if self := browseGrantAs(t, tenant, agent, ""); self.Missing != nil || self.State != "active" {67 t.Fatalf("agent without needs = %+v", self)68 }6970 // A human member has no grant and the gate does not apply one, so nothing71 // is missing for it.72 self = browseGrantAs(t, tenant, member, needs)73 if !self.Member || self.Role != "member" || self.State != "none" || self.Grant != nil || self.Allows != nil || self.Enforced {74 t.Fatalf("member self = %+v", self)75 }76 if self.Missing == nil || len(self.Missing.Rooms) != 0 || len(self.Missing.Kinds) != 0 {77 t.Fatalf("member missing = %+v", self.Missing)78 }79 if encoded, _ := json.Marshal(self); !strings.Contains(string(encoded), `"grant":null`) || strings.Contains(string(encoded), `"allows"`) {80 t.Fatalf("member wire shape %s", encoded)81 }8283 // A stranger is not a member and lacks everything it asked for.84 stranger := strings.Repeat("e", 64)85 self = browseGrantAs(t, tenant, stranger, needs)86 if self.Member || self.Role != "" || self.State != "none" || self.Grant != nil || !self.Enforced {87 t.Fatalf("stranger self = %+v", self)88 }89 if self.Missing == nil || strings.Join(self.Missing.Rooms, ",") != "build,ops" || len(self.Missing.Kinds) != 5 {90 t.Fatalf("stranger missing = %+v", self.Missing)91 }9293 // The owner may still not use it to read another key's grant: the94 // method only ever describes the caller.95 if self := browseGrantAs(t, tenant, owner, `{"agent":"`+agent+`"}`); self.Role != "owner" || self.Grant != nil {96 t.Fatalf("owner self = %+v", self)97 }9899 // Paused and revoked grants are reported as such.100 if _, err := tenant.Execute(ctx, owner, "pauseagent", []json.RawMessage{json.RawMessage(strconv.Quote(agent))}); err != nil {101 t.Fatal(err)102 }103 if self := browseGrantAs(t, tenant, agent, needs); self.State != "paused" || !self.Member || len(self.Missing.Rooms) != 1 {104 t.Fatalf("paused self = %+v", self)105 }106 if _, err := tenant.Execute(ctx, owner, "revokeagent", []json.RawMessage{json.RawMessage(strconv.Quote(agent))}); err != nil {107 t.Fatal(err)108 }109 if self := browseGrantAs(t, tenant, agent, ""); self.State != "revoked" || self.Member || self.Role != "" {110 t.Fatalf("revoked self = %+v", self)111 }112113 // Bad needs are refused.114 for _, bad := range []string{`{"kinds":[-1]}`, `{"rooms":[""]}`, `[1]`} {115 if _, err := tenant.Execute(ctx, agent, "browsegrant", []json.RawMessage{json.RawMessage(bad)}); err == nil || !strings.HasPrefix(err.Error(), "invalid:") {116 t.Fatalf("browsegrant %s: %v", bad, err)117 }118 }119}120121func TestBrowseGrantReportsExpiredGrants(t *testing.T) {122 _, tenant := testTenant(t)123 agent, _ := event.PublicKey(testAgentSecret)124 now := time.Now().Unix()125 if err := publishAs(t, tenant, agentGrantEvent(t, agent, now-10, now+60, []string{"k", "9"})); err != nil {126 t.Fatal(err)127 }128 if self := browseGrantAs(t, tenant, agent, ""); self.State != "active" {129 t.Fatalf("fresh grant = %+v", self)130 }131 if _, err := tenant.store.DB().Exec(`UPDATE agent_grants SET expires_at=? WHERE agent=?`, now-1, agent); err != nil {132 t.Fatal(err)133 }134 if self := browseGrantAs(t, tenant, agent, `{"kinds":[9]}`); self.State != "expired" || self.Grant == nil || len(self.Missing.Kinds) != 0 {135 t.Fatalf("expired grant = %+v", self)136 }137}