multiuser.test.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. const { eq, ok, notOk } = require('../../helpers/assert');
  2. const { makeNetwork, makePeer } = require('../../helpers/setup');
  3. describe('multi-user (3+ peers): tribe membership & content', (t) => {
  4. t('A creates tribe, B and C both join → Members:3, all see same content', async () => {
  5. const net = makeNetwork();
  6. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  7. A.setActor();
  8. const r = await A.use('tribes').createTribe('G3', '', null, '', [], false, true, 'strict', null, 'OPEN', '');
  9. const codeB = await A.use('tribes').generateInvite(r.key);
  10. B.setActor();
  11. await B.use('tribes').joinByInvite(codeB);
  12. A.setActor();
  13. const codeC = await A.use('tribes').generateInvite(r.key);
  14. C.setActor();
  15. await C.use('tribes').joinByInvite(codeC);
  16. A.setActor();
  17. await A.use('tribesContent').create(r.key, 'feed', { description: 'broadcast' });
  18. for (const peer of [A, B, C]) {
  19. peer.setActor();
  20. const tribe = await peer.use('tribes').getTribeById(r.key);
  21. ok(tribe, `${peer.keypair.id.slice(0, 8)} sees tribe`);
  22. eq(tribe.members.length, 3, `${peer.keypair.id.slice(0, 8)} sees Members:3`);
  23. const items = await peer.use('tribesContent').listByTribe(r.key, 'feed');
  24. eq(items.length, 1);
  25. eq(items[0].description, 'broadcast');
  26. }
  27. });
  28. t('outsider D cannot see private tribe with 3 members', async () => {
  29. const net = makeNetwork();
  30. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net); const D = makePeer(net);
  31. A.setActor();
  32. const r = await A.use('tribes').createTribe('Closed', '', null, '', [], false, true, 'strict', null, 'OPEN', '');
  33. for (const peer of [B, C]) {
  34. A.setActor();
  35. const code = await A.use('tribes').generateInvite(r.key);
  36. peer.setActor();
  37. await peer.use('tribes').joinByInvite(code);
  38. }
  39. A.setActor();
  40. await A.use('tribesContent').create(r.key, 'feed', { description: 'inner-circle' });
  41. D.setActor();
  42. const list = await D.use('tribes').listAll();
  43. notOk(list.find(t => t.id === r.key), 'D does not see private tribe');
  44. const items = await D.use('tribesContent').listByTribe(r.key, 'feed');
  45. eq(items.length, 0, 'D sees no inner content');
  46. });
  47. t('three members publishing → each member sees all three feeds', async () => {
  48. const net = makeNetwork();
  49. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  50. A.setActor();
  51. const r = await A.use('tribes').createTribe('Trio', '', null, '', [], false, true, 'strict', null, 'OPEN', '');
  52. for (const peer of [B, C]) {
  53. A.setActor();
  54. const code = await A.use('tribes').generateInvite(r.key);
  55. peer.setActor();
  56. await peer.use('tribes').joinByInvite(code);
  57. }
  58. A.setActor(); await A.use('tribesContent').create(r.key, 'feed', { description: 'from-A' });
  59. B.setActor(); await B.use('tribesContent').create(r.key, 'feed', { description: 'from-B' });
  60. C.setActor(); await C.use('tribesContent').create(r.key, 'feed', { description: 'from-C' });
  61. for (const peer of [A, B, C]) {
  62. peer.setActor();
  63. const items = await peer.use('tribesContent').listByTribe(r.key, 'feed');
  64. const texts = items.map(i => i.description).sort();
  65. eq(texts.length, 3);
  66. eq(texts[0], 'from-A');
  67. eq(texts[1], 'from-B');
  68. eq(texts[2], 'from-C');
  69. }
  70. });
  71. });
  72. describe('multi-user: transfers between three peers', (t) => {
  73. t('A→B transfer is invisible (uninvolved) to C', async () => {
  74. const net = makeNetwork();
  75. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  76. A.setActor();
  77. await A.use('transfers').createTransfer(B.keypair.id, 'private-deal', '50', '2026-12-31', []);
  78. C.setActor();
  79. const cList = await C.use('transfers').listAll('all');
  80. const ourTransfer = cList.find(t => t.concept === 'private-deal');
  81. ok(ourTransfer, 'C sees public log entry');
  82. eq(ourTransfer.from, A.keypair.id);
  83. eq(ourTransfer.to, B.keypair.id);
  84. notOk(ourTransfer.from === C.keypair.id || ourTransfer.to === C.keypair.id, 'C is not party');
  85. });
  86. t('three-way TIME-based transfers — categories preserved per peer view', async () => {
  87. const net = makeNetwork();
  88. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  89. A.setActor();
  90. await A.use('transfers').createTransfer(B.keypair.id, 'help-A-to-B', '2', '2026-12-31', [], 'TIME');
  91. B.setActor();
  92. await B.use('transfers').createTransfer(C.keypair.id, 'help-B-to-C', '3', '2026-12-31', [], 'TIME');
  93. C.setActor();
  94. await C.use('transfers').createTransfer(A.keypair.id, 'help-C-to-A', '1', '2026-12-31', [], 'TIME');
  95. for (const peer of [A, B, C]) {
  96. peer.setActor();
  97. const list = await peer.use('transfers').listAll('all');
  98. const time = list.filter(t => t.category === 'TIME');
  99. eq(time.length, 3, `${peer.keypair.id.slice(0, 8)} sees all 3 TIME transfers`);
  100. }
  101. });
  102. });
  103. describe('multi-user: forum threads', (t) => {
  104. t('A creates a forum thread, all three peers see it on the network', async () => {
  105. const net = makeNetwork();
  106. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  107. A.setActor();
  108. const forum = await A.use('forum').createForum('general', 'shared topic', 'hello world');
  109. ok(forum && forum.key, 'forum created');
  110. for (const peer of [A, B, C]) {
  111. peer.setActor();
  112. const list = await peer.use('forum').listAll('all');
  113. const found = list.find(f => f.id === forum.key || f.key === forum.key);
  114. ok(found, `${peer.keypair.id.slice(0, 8)} sees the forum thread`);
  115. }
  116. });
  117. });
  118. describe('multi-user: votes across three peers', (t) => {
  119. t('A creates vote, B and C cast different options, A sees both votes', async () => {
  120. const net = makeNetwork();
  121. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  122. A.setActor();
  123. const vote = await A.use('votes').createVote('tea or coffee?', '2026-12-31', ['tea', 'coffee']);
  124. ok(vote && (vote.key || vote.id), 'vote created');
  125. const voteId = vote.key || vote.id;
  126. B.setActor();
  127. await B.use('votes').voteOnVote(voteId, 'tea');
  128. C.setActor();
  129. await C.use('votes').voteOnVote(voteId, 'coffee');
  130. A.setActor();
  131. const v = await A.use('votes').getVoteById(voteId);
  132. ok(v, 'A sees the vote');
  133. ok(v.totalVotes >= 2, `expected ≥2 votes after B+C, got ${v.totalVotes}`);
  134. });
  135. });
  136. describe('multi-user: sub-tribe membership isolation (3 peers)', (t) => {
  137. t('B in parent cannot read sub-tribe content; C in sub cannot read parent-only content', async () => {
  138. const net = makeNetwork();
  139. const A = makePeer(net); const B = makePeer(net); const C = makePeer(net);
  140. A.setActor();
  141. const parent = await A.use('tribes').createTribe('P', '', null, '', [], false, true, 'strict', null, 'OPEN', '');
  142. const sub = await A.use('tribes').createTribe('S', '', null, '', [], false, true, 'strict', parent.key, 'OPEN', '');
  143. A.setActor();
  144. const parentInvite = await A.use('tribes').generateInvite(parent.key);
  145. B.setActor();
  146. await B.use('tribes').joinByInvite(parentInvite);
  147. A.setActor();
  148. const subInvite = await A.use('tribes').generateInvite(sub.key);
  149. C.setActor();
  150. await C.use('tribes').joinByInvite(subInvite);
  151. A.setActor();
  152. await A.use('tribesContent').create(parent.key, 'feed', { description: 'parent-only-msg' });
  153. await A.use('tribesContent').create(sub.key, 'feed', { description: 'sub-only-msg' });
  154. B.setActor();
  155. const bParent = await B.use('tribesContent').listByTribe(parent.key, 'feed');
  156. eq(bParent.length, 1, 'B sees parent content');
  157. eq(bParent[0].description, 'parent-only-msg');
  158. const bSub = await B.use('tribesContent').listByTribe(sub.key, 'feed');
  159. eq(bSub.length, 0, 'B cannot read sub content');
  160. C.setActor();
  161. const cSub = await C.use('tribesContent').listByTribe(sub.key, 'feed');
  162. eq(cSub.length, 1, 'C sees sub content');
  163. eq(cSub[0].description, 'sub-only-msg');
  164. const cParent = await C.use('tribesContent').listByTribe(parent.key, 'feed');
  165. eq(cParent.length, 0, 'C cannot read parent content');
  166. });
  167. });