Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

1package xrpc 2 3import ( 4 "encoding/json" 5 "net/http" 6 "net/http/httptest" 7 "testing" 8 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/eventstream" 11 "tangled.org/core/knotserver/db" 12) 13 14func eventsOfType(t *testing.T, x *Xrpc, nsid string) []eventstream.Event { 15 t.Helper() 16 all, err := x.Db.GetEvents(0, 1000) 17 if err != nil { 18 t.Fatalf("GetEvents: %v", err) 19 } 20 var out []eventstream.Event 21 for _, e := range all { 22 if e.Nsid == nsid { 23 out = append(out, e) 24 } 25 } 26 return out 27} 28 29func decodeMemberUpdate(t *testing.T, e eventstream.Event) db.KnotMemberUpdate { 30 t.Helper() 31 var m db.KnotMemberUpdate 32 if err := json.Unmarshal(e.EventJson, &m); err != nil { 33 t.Fatalf("decode memberUpdate: %v", err) 34 } 35 return m 36} 37 38func decodeCollaboratorUpdate(t *testing.T, e eventstream.Event) db.RepoCollaboratorUpdate { 39 t.Helper() 40 var c db.RepoCollaboratorUpdate 41 if err := json.Unmarshal(e.EventJson, &c); err != nil { 42 t.Fatalf("decode collaboratorUpdate: %v", err) 43 } 44 return c 45} 46 47func TestAddMember_EmitsAddEvent(t *testing.T) { 48 x, _ := newACLXrpc(t) 49 rec := httptest.NewRecorder() 50 x.AddMember(rec, aclRequest(t, aclOwner, tangled.KnotAddMember_Input{Subject: aclSubject})) 51 if rec.Code != http.StatusOK { 52 t.Fatalf("status = %d, body=%s", rec.Code, rec.Body.String()) 53 } 54 55 evs := eventsOfType(t, x, db.KnotMemberUpdateNSID) 56 if len(evs) != 1 { 57 t.Fatalf("memberUpdate events = %d, want 1", len(evs)) 58 } 59 m := decodeMemberUpdate(t, evs[0]) 60 if m.Op != db.AclOpAdd || m.Subject != aclSubject { 61 t.Errorf("event = %+v, want op=add subject=%s", m, aclSubject) 62 } 63} 64 65func TestRemoveMember_EmitsRemoveEvent(t *testing.T) { 66 x, _ := newACLXrpc(t) 67 seedMembers(t, x, aclSubject) 68 69 rec := httptest.NewRecorder() 70 x.RemoveMember(rec, aclRequest(t, aclOwner, tangled.KnotRemoveMember_Input{Subject: aclSubject})) 71 if rec.Code != http.StatusOK { 72 t.Fatalf("status = %d, body=%s", rec.Code, rec.Body.String()) 73 } 74 75 evs := eventsOfType(t, x, db.KnotMemberUpdateNSID) 76 if len(evs) != 2 { 77 t.Fatalf("memberUpdate events = %d, want 2 (add then remove)", len(evs)) 78 } 79 last := decodeMemberUpdate(t, evs[len(evs)-1]) 80 if last.Op != db.AclOpRemove || last.Subject != aclSubject { 81 t.Errorf("last event = %+v, want op=remove subject=%s", last, aclSubject) 82 } 83} 84 85func TestAddMember_NoOpDoesNotEmit(t *testing.T) { 86 x, _ := newACLXrpc(t) 87 for range 2 { 88 rec := httptest.NewRecorder() 89 x.AddMember(rec, aclRequest(t, aclOwner, tangled.KnotAddMember_Input{Subject: aclSubject})) 90 if rec.Code != http.StatusOK { 91 t.Fatalf("status = %d, body=%s", rec.Code, rec.Body.String()) 92 } 93 } 94 95 if evs := eventsOfType(t, x, db.KnotMemberUpdateNSID); len(evs) != 1 { 96 t.Errorf("memberUpdate events = %d, want 1; a duplicate grant is a no-op and must not emit", len(evs)) 97 } 98} 99 100func TestCollaborator_EmitsAddThenRemove(t *testing.T) { 101 x, _ := newACLXrpc(t) 102 seedRepo(t, x) 103 104 addRec := httptest.NewRecorder() 105 x.AddCollaborator(addRec, aclRequest(t, aclOwner, tangled.RepoAddCollaborator_Input{Repo: aclRepoDid, Subject: aclSubject})) 106 if addRec.Code != http.StatusOK { 107 t.Fatalf("add status = %d, body=%s", addRec.Code, addRec.Body.String()) 108 } 109 110 rmRec := httptest.NewRecorder() 111 x.RemoveCollaborator(rmRec, aclRequest(t, aclOwner, tangled.RepoRemoveCollaborator_Input{Repo: aclRepoDid, Subject: aclSubject})) 112 if rmRec.Code != http.StatusOK { 113 t.Fatalf("remove status = %d, body=%s", rmRec.Code, rmRec.Body.String()) 114 } 115 116 evs := eventsOfType(t, x, db.RepoCollaboratorUpdateNSID) 117 if len(evs) != 2 { 118 t.Fatalf("collaboratorUpdate events = %d, want 2 (add then remove)", len(evs)) 119 } 120 121 add := decodeCollaboratorUpdate(t, evs[0]) 122 if add.Op != db.AclOpAdd || add.Subject != aclSubject || add.Repo != aclRepoDid { 123 t.Errorf("add event = %+v, want op=add subject=%s repo=%s", add, aclSubject, aclRepoDid) 124 } 125 rm := decodeCollaboratorUpdate(t, evs[1]) 126 if rm.Op != db.AclOpRemove || rm.Subject != aclSubject || rm.Repo != aclRepoDid { 127 t.Errorf("remove event = %+v, want op=remove subject=%s repo=%s", rm, aclSubject, aclRepoDid) 128 } 129}