@session-foundation/qa-seeder 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39,6 +39,27 @@ var import_lodash5 = require("lodash");
39
39
 
40
40
  // src/requests/seedRequest.ts
41
41
  var import_lodash = require("lodash");
42
+
43
+ // src/utils/httpAgents.ts
44
+ var import_http = require("http");
45
+ var import_https = require("https");
46
+ var httpAgentIPv4 = new import_http.Agent({
47
+ family: 4,
48
+ // Force IPv4
49
+ keepAlive: true
50
+ });
51
+ var httpsAgentIPv4 = new import_https.Agent({
52
+ family: 4,
53
+ // Force IPv4
54
+ keepAlive: true,
55
+ rejectUnauthorized: false
56
+ // Allow self-signed certificates for testing
57
+ });
58
+ function getAgentForUrl(url) {
59
+ return url.startsWith("https") ? httpsAgentIPv4 : httpAgentIPv4;
60
+ }
61
+
62
+ // src/requests/seedRequest.ts
42
63
  var fetchedSnodesFromSeed = {};
43
64
  async function getAllSnodesFromSeed(seedNodeUrl) {
44
65
  if (!seedNodeUrl.startsWith("http")) {
@@ -54,9 +75,12 @@ async function getAllSnodesFromSeed(seedNodeUrl) {
54
75
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
55
76
  console.info(`Fetching snodes from seed node: "${seedNodeUrl}/json_rpc"`);
56
77
  try {
57
- const result = await fetch(`${seedNodeUrl}/json_rpc`, {
78
+ const url = `${seedNodeUrl}/json_rpc`;
79
+ const result = await fetch(url, {
58
80
  body: JSON.stringify(getAll.build()),
59
- method: "POST"
81
+ method: "POST",
82
+ // @ts-expect-error - Node.js fetch supports agent option
83
+ agent: getAgentForUrl(url)
60
84
  });
61
85
  console.info("snode from list result status:", result.status);
62
86
  const json = await result.json();
@@ -430,7 +454,9 @@ var SessionUser = class {
430
454
  `https://${snode.ip}:${snode.port}/storage_rpc/v1`,
431
455
  {
432
456
  body: JSON.stringify(builtRequest),
433
- method: "POST"
457
+ method: "POST",
458
+ // @ts-expect-error - Node.js fetch supports agent option
459
+ agent: httpsAgentIPv4
434
460
  }
435
461
  );
436
462
  return ret.status;
@@ -596,21 +622,57 @@ var SessionGroup = class {
596
622
  }
597
623
  async pushChangesToSwarm(snode) {
598
624
  const pushHex = this.metagroupW.pushHex();
599
- const toPush = (0, import_lodash3.compact)([
600
- pushHex.keysPush,
601
- pushHex.membersPush,
602
- pushHex.infosPush
625
+ const keysRequests = pushHex.keysPush ? new StoreGroupConfigSubRequest({
626
+ namespace: pushHex.keysPush.storageNamespace.value,
627
+ encryptedData: this.sodium.from_hex(pushHex.keysPush.dataHex),
628
+ groupPk: this.groupPk,
629
+ ttlMs: 1e3 * 3600 * 24,
630
+ // 1 day should be enough for testing and debugging a test?
631
+ adminGroupSigner: this.adminGroupSigner
632
+ }) : null;
633
+ const membersRequests = [];
634
+ if (pushHex.membersPush?.dataHex) {
635
+ for (let i = 0; i < pushHex.membersPush.dataHex.size(); i++) {
636
+ const dataHex = pushHex.membersPush.dataHex.get(i);
637
+ if (!dataHex) {
638
+ continue;
639
+ }
640
+ const memberRequest = new StoreGroupConfigSubRequest({
641
+ namespace: pushHex.membersPush.storageNamespace.value,
642
+ encryptedData: this.sodium.from_hex(dataHex),
643
+ groupPk: this.groupPk,
644
+ ttlMs: 1e3 * 3600 * 24,
645
+ // 1 day should be enough for testing and debugging a test?
646
+ adminGroupSigner: this.adminGroupSigner
647
+ });
648
+ membersRequests.push(memberRequest);
649
+ }
650
+ }
651
+ console.warn("pushHex.membersPush.dataHex", pushHex.membersPush?.dataHex.size());
652
+ console.warn("membersRequests", membersRequests);
653
+ const infosRequests = [];
654
+ if (pushHex.infosPush?.dataHex) {
655
+ for (let i = 0; i < pushHex.infosPush.dataHex.size(); i++) {
656
+ const dataHex = pushHex.infosPush.dataHex.get(i);
657
+ if (!dataHex) {
658
+ continue;
659
+ }
660
+ const infoRequest = new StoreGroupConfigSubRequest({
661
+ namespace: pushHex.infosPush.storageNamespace.value,
662
+ encryptedData: this.sodium.from_hex(dataHex),
663
+ groupPk: this.groupPk,
664
+ ttlMs: 1e3 * 3600 * 24,
665
+ // 1 day should be enough for testing and debugging a test?
666
+ adminGroupSigner: this.adminGroupSigner
667
+ });
668
+ infosRequests.push(infoRequest);
669
+ }
670
+ }
671
+ const storeRequests = (0, import_lodash3.compact)([
672
+ keysRequests,
673
+ ...membersRequests,
674
+ ...infosRequests
603
675
  ]);
604
- const storeRequests = toPush.map((m) => {
605
- return new StoreGroupConfigSubRequest({
606
- namespace: m.storageNamespace.value,
607
- encryptedData: this.sodium.from_hex(m.dataHex.toString()),
608
- groupPk: this.groupPk,
609
- ttlMs: 1e3 * 3600 * 24,
610
- // 1 day should be enough for testing and debugging a test?
611
- adminGroupSigner: this.adminGroupSigner
612
- });
613
- });
614
676
  const storeResult = await Promise.all(
615
677
  storeRequests.map(async (request) => {
616
678
  const builtRequest = await request.build();
@@ -622,7 +684,9 @@ var SessionGroup = class {
622
684
  `https://${snode.ip}:${snode.port}/storage_rpc/v1`,
623
685
  {
624
686
  body: JSON.stringify(builtRequest),
625
- method: "POST"
687
+ method: "POST",
688
+ // @ts-expect-error - Node.js fetch supports agent option
689
+ agent: httpsAgentIPv4
626
690
  }
627
691
  );
628
692
  return ret.status;
@@ -654,7 +718,9 @@ async function getSwarmOfUser(sessionId, snode) {
654
718
  `https://${snode.public_ip}:${snode.storage_port}/storage_rpc/v1`,
655
719
  {
656
720
  body: JSON.stringify(await swarmRequest.build()),
657
- method: "POST"
721
+ method: "POST",
722
+ // @ts-expect-error - Node.js fetch supports agent option
723
+ agent: httpsAgentIPv4
658
724
  }
659
725
  );
660
726
  const swarm = await swarmResult.json();
package/dist/index.mjs CHANGED
@@ -3,6 +3,27 @@ import { isArray, isEmpty as isEmpty3 } from "lodash";
3
3
 
4
4
  // src/requests/seedRequest.ts
5
5
  import { sample } from "lodash";
6
+
7
+ // src/utils/httpAgents.ts
8
+ import { Agent as HttpAgent } from "http";
9
+ import { Agent as HttpsAgent } from "https";
10
+ var httpAgentIPv4 = new HttpAgent({
11
+ family: 4,
12
+ // Force IPv4
13
+ keepAlive: true
14
+ });
15
+ var httpsAgentIPv4 = new HttpsAgent({
16
+ family: 4,
17
+ // Force IPv4
18
+ keepAlive: true,
19
+ rejectUnauthorized: false
20
+ // Allow self-signed certificates for testing
21
+ });
22
+ function getAgentForUrl(url) {
23
+ return url.startsWith("https") ? httpsAgentIPv4 : httpAgentIPv4;
24
+ }
25
+
26
+ // src/requests/seedRequest.ts
6
27
  var fetchedSnodesFromSeed = {};
7
28
  async function getAllSnodesFromSeed(seedNodeUrl) {
8
29
  if (!seedNodeUrl.startsWith("http")) {
@@ -18,9 +39,12 @@ async function getAllSnodesFromSeed(seedNodeUrl) {
18
39
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
19
40
  console.info(`Fetching snodes from seed node: "${seedNodeUrl}/json_rpc"`);
20
41
  try {
21
- const result = await fetch(`${seedNodeUrl}/json_rpc`, {
42
+ const url = `${seedNodeUrl}/json_rpc`;
43
+ const result = await fetch(url, {
22
44
  body: JSON.stringify(getAll.build()),
23
- method: "POST"
45
+ method: "POST",
46
+ // @ts-expect-error - Node.js fetch supports agent option
47
+ agent: getAgentForUrl(url)
24
48
  });
25
49
  console.info("snode from list result status:", result.status);
26
50
  const json = await result.json();
@@ -396,7 +420,9 @@ var SessionUser = class {
396
420
  `https://${snode.ip}:${snode.port}/storage_rpc/v1`,
397
421
  {
398
422
  body: JSON.stringify(builtRequest),
399
- method: "POST"
423
+ method: "POST",
424
+ // @ts-expect-error - Node.js fetch supports agent option
425
+ agent: httpsAgentIPv4
400
426
  }
401
427
  );
402
428
  return ret.status;
@@ -564,21 +590,57 @@ var SessionGroup = class {
564
590
  }
565
591
  async pushChangesToSwarm(snode) {
566
592
  const pushHex = this.metagroupW.pushHex();
567
- const toPush = compact([
568
- pushHex.keysPush,
569
- pushHex.membersPush,
570
- pushHex.infosPush
593
+ const keysRequests = pushHex.keysPush ? new StoreGroupConfigSubRequest({
594
+ namespace: pushHex.keysPush.storageNamespace.value,
595
+ encryptedData: this.sodium.from_hex(pushHex.keysPush.dataHex),
596
+ groupPk: this.groupPk,
597
+ ttlMs: 1e3 * 3600 * 24,
598
+ // 1 day should be enough for testing and debugging a test?
599
+ adminGroupSigner: this.adminGroupSigner
600
+ }) : null;
601
+ const membersRequests = [];
602
+ if (pushHex.membersPush?.dataHex) {
603
+ for (let i = 0; i < pushHex.membersPush.dataHex.size(); i++) {
604
+ const dataHex = pushHex.membersPush.dataHex.get(i);
605
+ if (!dataHex) {
606
+ continue;
607
+ }
608
+ const memberRequest = new StoreGroupConfigSubRequest({
609
+ namespace: pushHex.membersPush.storageNamespace.value,
610
+ encryptedData: this.sodium.from_hex(dataHex),
611
+ groupPk: this.groupPk,
612
+ ttlMs: 1e3 * 3600 * 24,
613
+ // 1 day should be enough for testing and debugging a test?
614
+ adminGroupSigner: this.adminGroupSigner
615
+ });
616
+ membersRequests.push(memberRequest);
617
+ }
618
+ }
619
+ console.warn("pushHex.membersPush.dataHex", pushHex.membersPush?.dataHex.size());
620
+ console.warn("membersRequests", membersRequests);
621
+ const infosRequests = [];
622
+ if (pushHex.infosPush?.dataHex) {
623
+ for (let i = 0; i < pushHex.infosPush.dataHex.size(); i++) {
624
+ const dataHex = pushHex.infosPush.dataHex.get(i);
625
+ if (!dataHex) {
626
+ continue;
627
+ }
628
+ const infoRequest = new StoreGroupConfigSubRequest({
629
+ namespace: pushHex.infosPush.storageNamespace.value,
630
+ encryptedData: this.sodium.from_hex(dataHex),
631
+ groupPk: this.groupPk,
632
+ ttlMs: 1e3 * 3600 * 24,
633
+ // 1 day should be enough for testing and debugging a test?
634
+ adminGroupSigner: this.adminGroupSigner
635
+ });
636
+ infosRequests.push(infoRequest);
637
+ }
638
+ }
639
+ const storeRequests = compact([
640
+ keysRequests,
641
+ ...membersRequests,
642
+ ...infosRequests
571
643
  ]);
572
- const storeRequests = toPush.map((m) => {
573
- return new StoreGroupConfigSubRequest({
574
- namespace: m.storageNamespace.value,
575
- encryptedData: this.sodium.from_hex(m.dataHex.toString()),
576
- groupPk: this.groupPk,
577
- ttlMs: 1e3 * 3600 * 24,
578
- // 1 day should be enough for testing and debugging a test?
579
- adminGroupSigner: this.adminGroupSigner
580
- });
581
- });
582
644
  const storeResult = await Promise.all(
583
645
  storeRequests.map(async (request) => {
584
646
  const builtRequest = await request.build();
@@ -590,7 +652,9 @@ var SessionGroup = class {
590
652
  `https://${snode.ip}:${snode.port}/storage_rpc/v1`,
591
653
  {
592
654
  body: JSON.stringify(builtRequest),
593
- method: "POST"
655
+ method: "POST",
656
+ // @ts-expect-error - Node.js fetch supports agent option
657
+ agent: httpsAgentIPv4
594
658
  }
595
659
  );
596
660
  return ret.status;
@@ -624,7 +688,9 @@ async function getSwarmOfUser(sessionId, snode) {
624
688
  `https://${snode.public_ip}:${snode.storage_port}/storage_rpc/v1`,
625
689
  {
626
690
  body: JSON.stringify(await swarmRequest.build()),
627
- method: "POST"
691
+ method: "POST",
692
+ // @ts-expect-error - Node.js fetch supports agent option
693
+ agent: httpsAgentIPv4
628
694
  }
629
695
  );
630
696
  const swarm = await swarmResult.json();
package/package.json CHANGED
@@ -1,21 +1,26 @@
1
1
  {
2
2
  "name": "@session-foundation/qa-seeder",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
7
7
  "dependencies": {
8
8
  "lodash": "^4.17.21",
9
- "@session-foundation/mnemonic": "^0.0.8",
10
- "@session-foundation/libsession-wasm": "^0.0.9",
11
9
  "@session-foundation/sodium": "^0.0.6",
12
- "@session-foundation/basic-types": "^0.0.6"
10
+ "@session-foundation/basic-types": "^0.0.6",
11
+ "@session-foundation/mnemonic": "^0.0.8",
12
+ "@session-foundation/libsession-wasm": "^0.0.10"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@turbo/gen": "^2.4.4",
16
+ "@types/chai": "^5.2.2",
16
17
  "@types/lodash": "^4.17.16",
18
+ "@types/mocha": "^10.0.10",
17
19
  "@types/node": "^22.13.10",
20
+ "chai": "^5.2.0",
18
21
  "eslint": "^9.23.0",
22
+ "mocha": "^11.7.1",
23
+ "ts-node": "^10.9.2",
19
24
  "tsup": "^8.5.1",
20
25
  "typescript": "5.8.2",
21
26
  "@repo/eslint-config": "0.0.0",
@@ -29,6 +34,8 @@
29
34
  "build": "tsup src/index.ts --format cjs,esm --dts",
30
35
  "dev": "pnpm build --watch",
31
36
  "lint": "eslint . --max-warnings 0",
32
- "check-types": "tsc --noEmit"
37
+ "check-types": "tsc --noEmit",
38
+ "pretest": "tsc --project tsconfig.test.json",
39
+ "test": "mocha --require ts-node/register --extensions ts 'tests/**/*.test.ts'"
33
40
  }
34
41
  }
@@ -0,0 +1,133 @@
1
+ import { describe, it } from 'mocha';
2
+ import { expect } from 'chai';
3
+ import { buildStateForTest, USERNAME } from '../src/index';
4
+
5
+ describe('buildStateForTest', () => {
6
+ // Use testnet for faster tests (no need to hit mainnet)
7
+ const network = 'http://sesh-net.local:1280';
8
+
9
+ describe('1user', () => {
10
+ it('should create a single user with valid properties', async () => {
11
+ // This test may take a while as it pushes to the network
12
+
13
+ const state = await buildStateForTest('1user', undefined, network);
14
+
15
+ expect(state).to.have.property('users');
16
+ expect(state.users).to.be.an('array');
17
+ expect(state.users).to.have.lengthOf(1);
18
+
19
+ const user = state.users[0];
20
+ if (!user) {
21
+ throw new Error('User should be defined');
22
+ }
23
+
24
+ // Validate user properties
25
+ expect(user).to.have.property('seed');
26
+ expect(user.seed).to.be.instanceOf(Uint8Array);
27
+ expect(user.seed).to.have.lengthOf(32);
28
+
29
+ expect(user).to.have.property('seedPhrase');
30
+ expect(user.seedPhrase).to.be.a('string');
31
+ expect(user.seedPhrase.split(' ')).to.have.lengthOf(13);
32
+
33
+ expect(user).to.have.property('sessionId');
34
+ expect(user.sessionId).to.be.a('string');
35
+ expect(user.sessionId).to.match(/^05[0-9a-f]{64}$/);
36
+
37
+ expect(user).to.have.property('userName');
38
+ expect(user.userName).to.equal(USERNAME.ALICE);
39
+ });
40
+
41
+ it('should create a user that gets pushed to the swarm', async function () {
42
+ const state = await buildStateForTest('1user', undefined, network);
43
+
44
+ // The fact that buildStateForTest completes without throwing
45
+ // means the user was successfully pushed to the swarm
46
+ expect(state.users).to.have.lengthOf(1);
47
+ expect(state.users[0]?.sessionId).to.be.a('string');
48
+ });
49
+ });
50
+
51
+ describe('2friendsInGroup', () => {
52
+ const groupName = 'Test Group';
53
+
54
+ it('should create two friends in a group with valid properties', async () => {
55
+ const state = await buildStateForTest('2friendsInGroup', groupName, network);
56
+
57
+ // Validate users
58
+ expect(state).to.have.property('users');
59
+ expect(state.users).to.be.an('array');
60
+ expect(state.users).to.have.lengthOf(2);
61
+
62
+ const user1 = state.users[0];
63
+ const user2 = state.users[1];
64
+
65
+ if (!user1 || !user2) {
66
+ throw new Error('Both users should be defined');
67
+ }
68
+
69
+ // Validate first user
70
+ expect(user1).to.have.property('seed');
71
+ expect(user1.seed).to.be.instanceOf(Uint8Array);
72
+ expect(user1).to.have.property('seedPhrase');
73
+ expect(user1.seedPhrase).to.be.a('string');
74
+ expect(user1).to.have.property('sessionId');
75
+ expect(user1.sessionId).to.match(/^05[0-9a-f]{64}$/);
76
+ expect(user1).to.have.property('userName');
77
+ expect(user1.userName).to.equal(USERNAME.ALICE);
78
+
79
+ // Validate second user
80
+ expect(user2).to.have.property('seed');
81
+ expect(user2.seed).to.be.instanceOf(Uint8Array);
82
+ expect(user2).to.have.property('seedPhrase');
83
+ expect(user2.seedPhrase).to.be.a('string');
84
+ expect(user2).to.have.property('sessionId');
85
+ expect(user2.sessionId).to.match(/^05[0-9a-f]{64}$/);
86
+ expect(user2).to.have.property('userName');
87
+ expect(user2.userName).to.equal(USERNAME.BOB);
88
+
89
+ // Validate users are different
90
+ expect(user1.sessionId).to.not.equal(user2.sessionId);
91
+
92
+ // Validate group
93
+ expect(state).to.have.property('group');
94
+ expect(state.group).to.be.an('object');
95
+ expect(state.group.groupPk).to.be.a('string');
96
+ expect(state.group.groupPk).to.match(/^03[0-9a-f]{64}$/);
97
+ expect(state.group.groupName).to.equal(groupName);
98
+ expect(state.group.adminSecretKey).to.be.instanceOf(Uint8Array);
99
+ expect(state.group.adminSecretKey).to.have.lengthOf(64);
100
+ });
101
+
102
+ it('should create friends that get pushed to the swarm', async () => {
103
+ const state = await buildStateForTest('2friendsInGroup', groupName, network);
104
+
105
+ // The fact that buildStateForTest completes without throwing
106
+ // means the users and group were successfully pushed to the swarm
107
+ expect(state.users).to.have.lengthOf(2);
108
+ expect(state.users[0]?.sessionId).to.be.a('string');
109
+ expect(state.users[1]?.sessionId).to.be.a('string');
110
+ expect(state.group.groupPk).to.be.a('string');
111
+ });
112
+
113
+ it('should throw error when groupName is not provided', async () => {
114
+ try {
115
+ // @ts-expect-error - Testing runtime error when groupName is undefined
116
+ await buildStateForTest('2friendsInGroup', undefined, network);
117
+ throw new Error('Should have thrown an error');
118
+ } catch (error) {
119
+ expect(error).to.be.instanceOf(Error);
120
+ expect((error as Error).message).to.include('groupName');
121
+ }
122
+ });
123
+ });
124
+
125
+ describe('none', () => {
126
+ it('should return empty users object', async () => {
127
+ const state = await buildStateForTest('none', undefined, network);
128
+
129
+ expect(state).to.have.property('users');
130
+ expect(state.users).to.deep.equal({});
131
+ });
132
+ });
133
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": ".",
6
+ "target": "ES2017",
7
+ "lib": ["ES2017", "ES2015"]
8
+ },
9
+ "include": ["src", "tests"],
10
+ "exclude": ["node_modules", "dist"]
11
+ }