@@ -1,10 +1,11 @@
1 1 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
package api
2 2 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
3 3 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
import (
4 4 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"bytes"
5 5 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"encoding/json"
6 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "fmt"
6 7 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"io"
7 8 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"log/slog"
8 9 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"net/http"
9 10 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"net/http/httptest"
10 11 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"testing"
@@ -12,17 +13,26 @@
12 13 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
13 14 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"github.com/conflicthq/scuttlebot/internal/config"
14 15 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"github.com/conflicthq/scuttlebot/internal/registry"
15 16 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"github.com/conflicthq/scuttlebot/internal/topology"
16 17 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
)
18 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
19 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // accessCall records a single GrantAccess or RevokeAccess invocation.
20 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ type accessCall struct {
21 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Nick string
22 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Channel string
23 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Level string // "OP", "VOICE", or "" for revoke
24 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
17 25 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
18 26 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// stubTopologyManager implements topologyManager for tests.
19 27 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// It records the last ProvisionChannel call and returns a canned Policy.
20 28 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
type stubTopologyManager struct {
21 29 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
last topology.ChannelConfig
22 30 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
policy *topology.Policy
23 31 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
provErr error
32 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ grants []accessCall
33 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ revokes []accessCall
24 34 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
25 35 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
26 36 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (s *stubTopologyManager) ProvisionChannel(ch topology.ChannelConfig) error {
27 37 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
s.last = ch
28 38 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return s.provErr
@@ -29,14 +39,55 @@
29 39 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
30 40 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
31 41 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (s *stubTopologyManager) DropChannel(_ string) {}
32 42 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
33 43 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (s *stubTopologyManager) Policy() *topology.Policy { return s.policy }
44 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
45 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (s *stubTopologyManager) GrantAccess(nick, channel, level string) {
46 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ s.grants = append(s.grants, accessCall{Nick: nick, Channel: channel, Level: level})
47 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
48 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
49 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (s *stubTopologyManager) RevokeAccess(nick, channel string) {
50 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ s.revokes = append(s.revokes, accessCall{Nick: nick, Channel: channel})
51 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
52 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // stubProvisioner is a minimal AccountProvisioner for agent registration tests.
54 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ type stubProvisioner struct {
55 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ accounts map[string]string
56 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
57 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
58 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func newStubProvisioner() *stubProvisioner {
59 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return &stubProvisioner{accounts: make(map[string]string)}
60 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
61 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
62 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (p *stubProvisioner) RegisterAccount(name, pass string) error {
63 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if _, ok := p.accounts[name]; ok {
64 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return fmt.Errorf("ACCOUNT_EXISTS")
65 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
66 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ p.accounts[name] = pass
67 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return nil
68 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
69 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
70 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (p *stubProvisioner) ChangePassword(name, pass string) error {
71 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ p.accounts[name] = pass
72 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return nil
73 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
34 74 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
35 75 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func newTopoTestServer(t *testing.T, topo *stubTopologyManager) (*httptest.Server, string) {
36 76 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
t.Helper()
37 77 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
reg := registry.New(nil, []byte("key"))
78 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ log := slog.New(slog.NewTextHandler(io.Discard, nil))
79 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv := httptest.NewServer(New(reg, []string{"tok"}, nil, nil, nil, nil, topo, nil, "", log).Handler())
80 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Cleanup(srv.Close)
81 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return srv, "tok"
82 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
83 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
84 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // newTopoTestServerWithRegistry creates a test server with both topology and a
85 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // real registry backed by stubProvisioner, so agent registration works.
86 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func newTopoTestServerWithRegistry(t *testing.T, topo *stubTopologyManager) (*httptest.Server, string) {
87 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Helper()
88 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ reg := registry.New(newStubProvisioner(), []byte("key"))
38 89 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
log := slog.New(slog.NewTextHandler(io.Discard, nil))
39 90 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
srv := httptest.NewServer(New(reg, []string{"tok"}, nil, nil, nil, nil, topo, nil, "", log).Handler())
40 91 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
t.Cleanup(srv.Close)
41 92 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return srv, "tok"
42 93 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
@@ -145,5 +196,168 @@
145 196 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
146 197 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if len(got.Types) != 1 || got.Types[0].Name != "task" {
147 198 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
t.Errorf("types = %v", got.Types)
148 199 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
149 200 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
201 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // --- Agent mode assignment tests ---
203 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
204 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // topoDoJSON is a helper for issuing authenticated JSON requests against a test server.
205 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func topoDoJSON(t *testing.T, srv *httptest.Server, tok, method, path string, body any) *http.Response {
206 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Helper()
207 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ var buf bytes.Buffer
208 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if body != nil {
209 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err := json.NewEncoder(&buf).Encode(body); err != nil {
210 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("encode: %v", err)
211 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
212 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
213 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ req, _ := http.NewRequest(method, srv.URL+path, &buf)
214 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ req.Header.Set("Authorization", "Bearer "+tok)
215 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ req.Header.Set("Content-Type", "application/json")
216 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp, err := http.DefaultClient.Do(req)
217 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err != nil {
218 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("do: %v", err)
219 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
220 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return resp
221 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
222 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
223 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestRegisterGrantsOPForOrchestrator(t *testing.T) {
224 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ stub := &stubTopologyManager{}
225 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv, tok := newTopoTestServerWithRegistry(t, stub)
226 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
227 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp := topoDoJSON(t, srv, tok, "POST", "/v1/agents/register", map[string]any{
228 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "nick": "orch-1",
229 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "type": "orchestrator",
230 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "channels": []string{"#fleet", "#project.foo"},
231 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
232 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer resp.Body.Close()
233 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if resp.StatusCode != http.StatusCreated {
234 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("register: want 201, got %d", resp.StatusCode)
235 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
236 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
237 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(stub.grants) != 2 {
238 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("grants: want 2, got %d", len(stub.grants))
239 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
240 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for i, want := range []accessCall{
241 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {Nick: "orch-1", Channel: "#fleet", Level: "OP"},
242 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {Nick: "orch-1", Channel: "#project.foo", Level: "OP"},
243 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } {
244 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if stub.grants[i] != want {
245 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("grant[%d] = %+v, want %+v", i, stub.grants[i], want)
246 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
247 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
248 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
249 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
250 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestRegisterGrantsVOICEForWorker(t *testing.T) {
251 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ stub := &stubTopologyManager{}
252 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv, tok := newTopoTestServerWithRegistry(t, stub)
253 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
254 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp := topoDoJSON(t, srv, tok, "POST", "/v1/agents/register", map[string]any{
255 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "nick": "worker-1",
256 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "type": "worker",
257 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "channels": []string{"#fleet"},
258 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
259 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer resp.Body.Close()
260 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if resp.StatusCode != http.StatusCreated {
261 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("register: want 201, got %d", resp.StatusCode)
262 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
263 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
264 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(stub.grants) != 1 {
265 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("grants: want 1, got %d", len(stub.grants))
266 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
267 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if stub.grants[0].Level != "VOICE" {
268 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("level = %q, want VOICE", stub.grants[0].Level)
269 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
270 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
271 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
272 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestRegisterNoModeForObserver(t *testing.T) {
273 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ stub := &stubTopologyManager{}
274 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv, tok := newTopoTestServerWithRegistry(t, stub)
275 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
276 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp := topoDoJSON(t, srv, tok, "POST", "/v1/agents/register", map[string]any{
277 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "nick": "obs-1",
278 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "type": "observer",
279 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "channels": []string{"#fleet"},
280 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
281 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer resp.Body.Close()
282 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if resp.StatusCode != http.StatusCreated {
283 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("register: want 201, got %d", resp.StatusCode)
284 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
285 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
286 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(stub.grants) != 0 {
287 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("grants: want 0, got %d — observer should get no mode", len(stub.grants))
288 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
289 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
290 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
291 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestRegisterGrantsOPForOperator(t *testing.T) {
292 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ stub := &stubTopologyManager{}
293 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv, tok := newTopoTestServerWithRegistry(t, stub)
294 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
295 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp := topoDoJSON(t, srv, tok, "POST", "/v1/agents/register", map[string]any{
296 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "nick": "human-op",
297 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "type": "operator",
298 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "channels": []string{"#fleet"},
299 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
300 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer resp.Body.Close()
301 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if resp.StatusCode != http.StatusCreated {
302 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("register: want 201, got %d", resp.StatusCode)
303 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
304 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
305 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(stub.grants) != 1 {
306 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("grants: want 1, got %d", len(stub.grants))
307 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
308 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if stub.grants[0].Level != "OP" {
309 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("level = %q, want OP", stub.grants[0].Level)
310 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
311 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
312 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
313 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestRevokeRemovesAccess(t *testing.T) {
314 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ stub := &stubTopologyManager{}
315 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv, tok := newTopoTestServerWithRegistry(t, stub)
316 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
317 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp := topoDoJSON(t, srv, tok, "POST", "/v1/agents/register", map[string]any{
318 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "nick": "orch-rev",
319 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "type": "orchestrator",
320 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "channels": []string{"#fleet", "#project.x"},
321 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
322 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp.Body.Close()
323 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
324 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp = topoDoJSON(t, srv, tok, "POST", "/v1/agents/orch-rev/revoke", nil)
325 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer resp.Body.Close()
326 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if resp.StatusCode != http.StatusNoContent {
327 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("revoke: want 204, got %d", resp.StatusCode)
328 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
329 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
330 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(stub.revokes) != 2 {
331 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("revokes: want 2, got %d", len(stub.revokes))
332 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
333 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for i, want := range []string{"#fleet", "#project.x"} {
334 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if stub.revokes[i].Channel != want {
335 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("revoke[%d].Channel = %q, want %q", i, stub.revokes[i].Channel, want)
336 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
337 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
338 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
339 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
340 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestDeleteRemovesAccess(t *testing.T) {
341 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ stub := &stubTopologyManager{}
342 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ srv, tok := newTopoTestServerWithRegistry(t, stub)
343 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
344 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp := topoDoJSON(t, srv, tok, "POST", "/v1/agents/register", map[string]any{
345 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "nick": "del-agent",
346 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "type": "worker",
347 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "channels": []string{"#fleet"},
348 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
349 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp.Body.Close()
350 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
351 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ resp = topoDoJSON(t, srv, tok, "DELETE", "/v1/agents/del-agent", nil)
352 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer resp.Body.Close()
353 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if resp.StatusCode != http.StatusNoContent {
354 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("delete: want 204, got %d", resp.StatusCode)
355 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
356 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
357 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(stub.revokes) != 1 {
358 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("revokes: want 1, got %d", len(stub.revokes))
359 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
360 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if stub.revokes[0].Nick != "del-agent" || stub.revokes[0].Channel != "#fleet" {
361 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("revoke = %+v, want del-agent on #fleet", stub.revokes[0])
362 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
363 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
150 364 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!