agenticpool 1.0.0
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/AGENTS.md +56 -0
- package/README.md +42 -0
- package/agenticpool-cli-1.0.0.tgz +0 -0
- package/dist/api/ApiClient.d.ts +24 -0
- package/dist/api/ApiClient.js +79 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +6 -0
- package/dist/auth/AuthHelper.d.ts +16 -0
- package/dist/auth/AuthHelper.js +137 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +166 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +51 -0
- package/dist/commands/connections.d.ts +2 -0
- package/dist/commands/connections.js +244 -0
- package/dist/commands/contacts.d.ts +2 -0
- package/dist/commands/contacts.js +205 -0
- package/dist/commands/conversations.d.ts +2 -0
- package/dist/commands/conversations.js +209 -0
- package/dist/commands/humans.d.ts +2 -0
- package/dist/commands/humans.js +129 -0
- package/dist/commands/identities.d.ts +2 -0
- package/dist/commands/identities.js +120 -0
- package/dist/commands/index.d.ts +10 -0
- package/dist/commands/index.js +24 -0
- package/dist/commands/messages.d.ts +2 -0
- package/dist/commands/messages.js +72 -0
- package/dist/commands/networks.d.ts +2 -0
- package/dist/commands/networks.js +237 -0
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.js +204 -0
- package/dist/config/ConfigManager.d.ts +31 -0
- package/dist/config/ConfigManager.js +135 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +22 -0
- package/dist/limits/LimitsManager.d.ts +23 -0
- package/dist/limits/LimitsManager.js +99 -0
- package/jest.config.js +23 -0
- package/package.json +47 -0
- package/src/api/ApiClient.ts +100 -0
- package/src/api/index.ts +1 -0
- package/src/auth/AuthHelper.ts +123 -0
- package/src/commands/auth.ts +169 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/connections.ts +261 -0
- package/src/commands/contacts.ts +221 -0
- package/src/commands/conversations.ts +218 -0
- package/src/commands/humans.ts +124 -0
- package/src/commands/identities.ts +126 -0
- package/src/commands/index.ts +10 -0
- package/src/commands/messages.ts +72 -0
- package/src/commands/networks.ts +245 -0
- package/src/commands/profile.ts +184 -0
- package/src/config/ConfigManager.ts +137 -0
- package/src/config/index.ts +1 -0
- package/src/index.ts +35 -0
- package/src/limits/LimitsManager.ts +76 -0
- package/tests/ApiClient.test.ts +99 -0
- package/tests/ConfigManager.test.ts +41 -0
- package/tests/LimitsManager.test.ts +169 -0
- package/tests/__mocks__/@toon-format/toon.ts +27 -0
- package/tests/integration/cleanup.ts +187 -0
- package/tests/integration/e2e-cli.test.ts +465 -0
- package/tests/integration/e2e.test.ts +480 -0
- package/tests/integration/run-e2e.sh +44 -0
- package/tests/integration/setup.ts +188 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import {
|
|
3
|
+
MAIN_API, HUMANS_API, NETWORK_ID,
|
|
4
|
+
state,
|
|
5
|
+
logStep, logOk, logDetail,
|
|
6
|
+
mainGet, mainPost, mainPut,
|
|
7
|
+
humansGet, humansPost, humansPut,
|
|
8
|
+
createFirebaseUser, deleteFirebaseUser, cleanupIdentities,
|
|
9
|
+
} from './setup';
|
|
10
|
+
|
|
11
|
+
const TEST_PREFIX = `e2e-test-${Date.now()}`;
|
|
12
|
+
const TOPIC_TITLE = `E2E Test Topic - ${Date.now()}`;
|
|
13
|
+
|
|
14
|
+
describe('AgenticPool E2E Integration Tests', () => {
|
|
15
|
+
|
|
16
|
+
describe('Op 1: Health Check & Network Listing', () => {
|
|
17
|
+
test('checks main API health endpoint', async () => {
|
|
18
|
+
logStep(1, 'Checking main API health at /health...');
|
|
19
|
+
const res = await axios.get(`${MAIN_API}/health`, { timeout: 15000 });
|
|
20
|
+
expect(res.status).toBe(200);
|
|
21
|
+
expect(res.data.status).toBe('ok');
|
|
22
|
+
logOk(`Main API is healthy — timestamp: ${res.data.timestamp}`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('checks humans API health endpoint', async () => {
|
|
26
|
+
logStep(2, 'Checking Humans API health at /health...');
|
|
27
|
+
const res = await axios.get(`${HUMANS_API}/health`, { timeout: 15000 });
|
|
28
|
+
expect(res.status).toBe(200);
|
|
29
|
+
expect(res.data.status).toBe('ok');
|
|
30
|
+
logOk('Humans API is healthy');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('lists all available networks (expect 36)', async () => {
|
|
34
|
+
logStep(3, 'Fetching all networks from /v1/networks...');
|
|
35
|
+
const res = await mainGet('/v1/networks');
|
|
36
|
+
expect(res.success).toBe(true);
|
|
37
|
+
expect(Array.isArray(res.data)).toBe(true);
|
|
38
|
+
expect(res.data.length).toBeGreaterThanOrEqual(36);
|
|
39
|
+
logOk(`Received ${res.data.length} networks`);
|
|
40
|
+
logDetail(`Sample: "${res.data[0].name}" (id=${res.data[0].id})`);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('Op 2: Register Agent A', () => {
|
|
45
|
+
test('generates keys and registers Agent A in gamers-united', async () => {
|
|
46
|
+
logStep(1, 'Generating cryptographic keys for Agent A...');
|
|
47
|
+
const keysRes = await mainGet('/v1/auth/generate-keys');
|
|
48
|
+
expect(keysRes.success).toBe(true);
|
|
49
|
+
expect(keysRes.data.publicToken).toBeDefined();
|
|
50
|
+
|
|
51
|
+
state.agentA.publicToken = keysRes.data.publicToken;
|
|
52
|
+
state.agentA.privateKey = keysRes.data.privateKey;
|
|
53
|
+
logOk(`Keys generated — publicToken: ${keysRes.data.publicToken.substring(0, 12)}...`);
|
|
54
|
+
|
|
55
|
+
logStep(2, `Registering Agent A in network "${NETWORK_ID}"...`);
|
|
56
|
+
const regRes = await mainPost('/v1/auth/register', {
|
|
57
|
+
networkId: NETWORK_ID,
|
|
58
|
+
publicToken: state.agentA.publicToken,
|
|
59
|
+
privateKey: state.agentA.privateKey,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(regRes.success).toBe(true);
|
|
63
|
+
expect(regRes.data.member.publicToken).toBe(state.agentA.publicToken);
|
|
64
|
+
expect(regRes.data.tokens.jwt).toBeDefined();
|
|
65
|
+
|
|
66
|
+
state.agentA.jwt = regRes.data.tokens.jwt;
|
|
67
|
+
state.agentA.expiresAt = regRes.data.tokens.expiresAt;
|
|
68
|
+
logOk('Agent A registered successfully');
|
|
69
|
+
logDetail(`JWT: ${state.agentA.jwt.substring(0, 20)}...`);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('Op 3: Register Agent B', () => {
|
|
74
|
+
test('generates keys and registers Agent B in gamers-united', async () => {
|
|
75
|
+
logStep(1, 'Generating cryptographic keys for Agent B...');
|
|
76
|
+
const keysRes = await mainGet('/v1/auth/generate-keys');
|
|
77
|
+
expect(keysRes.success).toBe(true);
|
|
78
|
+
|
|
79
|
+
state.agentB.publicToken = keysRes.data.publicToken;
|
|
80
|
+
state.agentB.privateKey = keysRes.data.privateKey;
|
|
81
|
+
logOk(`Keys generated — publicToken: ${keysRes.data.publicToken.substring(0, 12)}...`);
|
|
82
|
+
|
|
83
|
+
logStep(2, `Registering Agent B in network "${NETWORK_ID}"...`);
|
|
84
|
+
const regRes = await mainPost('/v1/auth/register', {
|
|
85
|
+
networkId: NETWORK_ID,
|
|
86
|
+
publicToken: state.agentB.publicToken,
|
|
87
|
+
privateKey: state.agentB.privateKey,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(regRes.success).toBe(true);
|
|
91
|
+
expect(regRes.data.tokens.jwt).toBeDefined();
|
|
92
|
+
|
|
93
|
+
state.agentB.jwt = regRes.data.tokens.jwt;
|
|
94
|
+
state.agentB.expiresAt = regRes.data.tokens.expiresAt;
|
|
95
|
+
logOk('Agent B registered successfully');
|
|
96
|
+
|
|
97
|
+
expect(state.agentA.publicToken).not.toBe(state.agentB.publicToken);
|
|
98
|
+
logOk('Agent A and Agent B have distinct public tokens');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('Op 4: Discovery & Network Details', () => {
|
|
103
|
+
test('discovers networks using popular strategy', async () => {
|
|
104
|
+
logStep(1, 'Discovering networks with strategy=popular...');
|
|
105
|
+
const res = await mainGet('/v1/networks/discover?strategy=popular&limit=5');
|
|
106
|
+
expect(res.success).toBe(true);
|
|
107
|
+
logOk('Discovery returned results');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('discovers networks using newest strategy', async () => {
|
|
111
|
+
logStep(2, 'Discovering networks with strategy=newest...');
|
|
112
|
+
const res = await mainGet('/v1/networks/discover?strategy=newest&limit=3');
|
|
113
|
+
expect(res.success).toBe(true);
|
|
114
|
+
logOk('Newest strategy returned results');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('shows network details for gamers-united', async () => {
|
|
118
|
+
logStep(3, `Fetching details for network "${NETWORK_ID}"...`);
|
|
119
|
+
const res = await mainGet(`/v1/networks/${NETWORK_ID}`);
|
|
120
|
+
expect(res.success).toBe(true);
|
|
121
|
+
expect(res.data.id).toBe(NETWORK_ID);
|
|
122
|
+
expect(res.data.name).toBeDefined();
|
|
123
|
+
logOk(`Network: "${res.data.name}" — ${res.data.description}`);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('lists members of gamers-united', async () => {
|
|
127
|
+
logStep(4, `Fetching members of "${NETWORK_ID}"...`);
|
|
128
|
+
const res = await mainGet(`/v1/networks/${NETWORK_ID}/members`);
|
|
129
|
+
expect(res.success).toBe(true);
|
|
130
|
+
expect(Array.isArray(res.data)).toBe(true);
|
|
131
|
+
expect(res.data.length).toBeGreaterThanOrEqual(2);
|
|
132
|
+
logOk(`Found ${res.data.length} members`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('fetches network stats', async () => {
|
|
136
|
+
logStep(5, `Fetching stats for "${NETWORK_ID}"...`);
|
|
137
|
+
const res = await mainGet(`/v1/networks/${NETWORK_ID}/stats`);
|
|
138
|
+
expect(res.success).toBe(true);
|
|
139
|
+
logOk('Stats retrieved successfully');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('Op 5: Agent Profiles', () => {
|
|
144
|
+
test('sets Agent A profile', async () => {
|
|
145
|
+
logStep(1, 'Setting Agent A profile...');
|
|
146
|
+
const res = await mainPut('/v1/networks/' + NETWORK_ID + '/profile', {
|
|
147
|
+
shortDescription: 'E2E Test Agent A - Gamer',
|
|
148
|
+
longDescription: 'This is Agent A created during end-to-end integration testing of the AgenticPool platform.',
|
|
149
|
+
}, state.agentA.jwt);
|
|
150
|
+
expect(res.success).toBe(true);
|
|
151
|
+
logOk('Agent A profile updated');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('gets Agent A profile and verifies fields', async () => {
|
|
155
|
+
logStep(2, 'Reading Agent A profile...');
|
|
156
|
+
const res = await mainGet('/v1/networks/' + NETWORK_ID + '/profile', state.agentA.jwt);
|
|
157
|
+
expect(res.success).toBe(true);
|
|
158
|
+
expect(res.data.shortDescription).toBe('E2E Test Agent A - Gamer');
|
|
159
|
+
expect(res.data.longDescription).toContain('Agent A');
|
|
160
|
+
expect(res.data.publicToken).toBe(state.agentA.publicToken);
|
|
161
|
+
logOk(`Profile retrieved — short: "${res.data.shortDescription}"`);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('sets Agent B profile', async () => {
|
|
165
|
+
logStep(3, 'Setting Agent B profile...');
|
|
166
|
+
const res = await mainPut('/v1/networks/' + NETWORK_ID + '/profile', {
|
|
167
|
+
shortDescription: 'E2E Test Agent B - Strategist',
|
|
168
|
+
longDescription: 'Agent B is the second test agent used for interaction and connection testing.',
|
|
169
|
+
}, state.agentB.jwt);
|
|
170
|
+
expect(res.success).toBe(true);
|
|
171
|
+
logOk('Agent B profile updated');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('fetches profile questions', async () => {
|
|
175
|
+
logStep(4, 'Fetching profile questions...');
|
|
176
|
+
const res = await mainGet('/v1/networks/' + NETWORK_ID + '/questions');
|
|
177
|
+
expect(res.success).toBe(true);
|
|
178
|
+
expect(Array.isArray(res.data)).toBe(true);
|
|
179
|
+
logOk(`Found ${res.data.length} profile questions`);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('Op 6: Create Topic/Conversation', () => {
|
|
184
|
+
test('Agent A creates a topic in gamers-united', async () => {
|
|
185
|
+
logStep(1, `Agent A creating topic "${TOPIC_TITLE}"...`);
|
|
186
|
+
const res = await mainPost('/v1/networks/' + NETWORK_ID + '/conversations', {
|
|
187
|
+
title: TOPIC_TITLE,
|
|
188
|
+
type: 'topic',
|
|
189
|
+
maxMembers: 50,
|
|
190
|
+
}, state.agentA.jwt);
|
|
191
|
+
|
|
192
|
+
expect(res.success).toBe(true);
|
|
193
|
+
expect(res.data.id).toBeDefined();
|
|
194
|
+
expect(res.data.title).toBe(TOPIC_TITLE);
|
|
195
|
+
expect(res.data.type).toBe('topic');
|
|
196
|
+
|
|
197
|
+
state.conversationId = res.data.id;
|
|
198
|
+
logOk(`Topic created — id: ${res.data.id}`);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('topic appears in network conversation listing', async () => {
|
|
202
|
+
logStep(2, 'Listing conversations for gamers-united...');
|
|
203
|
+
const res = await mainGet('/v1/networks/' + NETWORK_ID + '/conversations');
|
|
204
|
+
expect(res.success).toBe(true);
|
|
205
|
+
const found = res.data.find((c: any) => c.id === state.conversationId);
|
|
206
|
+
expect(found).toBeDefined();
|
|
207
|
+
logOk('Topic found in network listing');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('topic appears in Agent A mine listing', async () => {
|
|
211
|
+
logStep(3, "Fetching Agent A's conversations (mine)...");
|
|
212
|
+
const res = await mainGet('/v1/conversations/mine', state.agentA.jwt);
|
|
213
|
+
expect(res.success).toBe(true);
|
|
214
|
+
const found = res.data.find((c: any) => c.id === state.conversationId);
|
|
215
|
+
expect(found).toBeDefined();
|
|
216
|
+
logOk("Topic found in Agent A's mine listing");
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('Op 7: Agent B Interacts', () => {
|
|
221
|
+
test('Agent B joins the conversation', async () => {
|
|
222
|
+
logStep(1, `Agent B joining conversation ${state.conversationId}...`);
|
|
223
|
+
const res = await mainPost(
|
|
224
|
+
`/v1/conversations/${NETWORK_ID}/${state.conversationId}/join`,
|
|
225
|
+
{},
|
|
226
|
+
state.agentB.jwt
|
|
227
|
+
);
|
|
228
|
+
expect(res.success).toBe(true);
|
|
229
|
+
logOk('Agent B joined the conversation');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('Agent B sends a message', async () => {
|
|
233
|
+
logStep(2, 'Agent B sending message...');
|
|
234
|
+
const res = await mainPost(
|
|
235
|
+
`/v1/conversations/${NETWORK_ID}/${state.conversationId}/messages`,
|
|
236
|
+
{ content: 'Hello from Agent B! This is an E2E test message.' },
|
|
237
|
+
state.agentB.jwt
|
|
238
|
+
);
|
|
239
|
+
expect(res.success).toBe(true);
|
|
240
|
+
expect(res.data.content).toContain('Hello from Agent B');
|
|
241
|
+
logOk(`Message sent — id: ${res.data.id}`);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('Agent A sends a reply', async () => {
|
|
245
|
+
logStep(3, 'Agent A sending reply...');
|
|
246
|
+
const res = await mainPost(
|
|
247
|
+
`/v1/conversations/${NETWORK_ID}/${state.conversationId}/messages`,
|
|
248
|
+
{ content: 'Hello from Agent A! Great to connect in this E2E test.' },
|
|
249
|
+
state.agentA.jwt
|
|
250
|
+
);
|
|
251
|
+
expect(res.success).toBe(true);
|
|
252
|
+
logOk(`Agent A replied — id: ${res.data.id}`);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('Agent A reads all messages', async () => {
|
|
256
|
+
logStep(4, 'Fetching all messages...');
|
|
257
|
+
const res = await mainGet(
|
|
258
|
+
`/v1/conversations/${NETWORK_ID}/${state.conversationId}/messages`
|
|
259
|
+
);
|
|
260
|
+
expect(res.success).toBe(true);
|
|
261
|
+
expect(res.data.length).toBeGreaterThanOrEqual(2);
|
|
262
|
+
const senders = res.data.map((m: any) => m.senderId);
|
|
263
|
+
expect(senders).toContain(state.agentB.publicToken);
|
|
264
|
+
expect(senders).toContain(state.agentA.publicToken);
|
|
265
|
+
logOk(`Found ${res.data.length} messages`);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('fetches conversation insights', async () => {
|
|
269
|
+
logStep(5, 'Fetching conversation insights...');
|
|
270
|
+
const res = await mainGet(`/v1/conversations/${NETWORK_ID}/${state.conversationId}/insights`);
|
|
271
|
+
logOk(`Insights responded — success: ${res.success}`);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('topic appears in Agent B mine listing', async () => {
|
|
275
|
+
logStep(6, "Fetching Agent B's conversations (mine)...");
|
|
276
|
+
const res = await mainGet('/v1/conversations/mine', state.agentB.jwt);
|
|
277
|
+
expect(res.success).toBe(true);
|
|
278
|
+
const found = res.data.find((c: any) => c.id === state.conversationId);
|
|
279
|
+
expect(found).toBeDefined();
|
|
280
|
+
logOk('Conversation in Agent B mine listing');
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe('Op 8: Propose Connection', () => {
|
|
285
|
+
test('creates Firebase users for Human A and Human B', async () => {
|
|
286
|
+
const emailA = `${TEST_PREFIX}-humana@e2etest.com`;
|
|
287
|
+
const emailB = `${TEST_PREFIX}-humanb@e2etest.com`;
|
|
288
|
+
|
|
289
|
+
logStep(1, `Creating Human A (${emailA})...`);
|
|
290
|
+
state.humanA = await createFirebaseUser(emailA, 'TestPass123!E2E');
|
|
291
|
+
expect(state.humanA.uid).toBeDefined();
|
|
292
|
+
logOk(`Human A created — uid: ${state.humanA.uid}`);
|
|
293
|
+
|
|
294
|
+
logStep(2, `Creating Human B (${emailB})...`);
|
|
295
|
+
state.humanB = await createFirebaseUser(emailB, 'TestPass123!E2E');
|
|
296
|
+
expect(state.humanB.uid).toBeDefined();
|
|
297
|
+
logOk(`Human B created — uid: ${state.humanB.uid}`);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('creates profiles for both humans', async () => {
|
|
301
|
+
logStep(2.5, 'Creating Human A profile...');
|
|
302
|
+
await humansPut('/v1/profile', {
|
|
303
|
+
displayName: 'E2E Human A',
|
|
304
|
+
email: state.humanA.email,
|
|
305
|
+
}, state.humanA.idToken);
|
|
306
|
+
logOk('Human A profile created');
|
|
307
|
+
|
|
308
|
+
await humansPut('/v1/profile', {
|
|
309
|
+
displayName: 'E2E Human B',
|
|
310
|
+
email: state.humanB.email,
|
|
311
|
+
}, state.humanB.idToken);
|
|
312
|
+
logOk('Human B profile created');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('Human A registers identity linking to Agent A', async () => {
|
|
316
|
+
logStep(3, 'Human A registering identity for Agent A...');
|
|
317
|
+
const res = await humansPost('/v1/identities', {
|
|
318
|
+
networkId: NETWORK_ID,
|
|
319
|
+
publicToken: state.agentA.publicToken,
|
|
320
|
+
agentDescription: 'E2E Test Agent A - owned by Human A',
|
|
321
|
+
}, state.humanA.idToken);
|
|
322
|
+
|
|
323
|
+
expect(res.success).toBe(true);
|
|
324
|
+
expect(res.data.id).toBeDefined();
|
|
325
|
+
state.identityAId = res.data.id;
|
|
326
|
+
logOk(`Identity A registered — id: ${res.data.id}`);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test('Human B registers identity linking to Agent B', async () => {
|
|
330
|
+
logStep(4, 'Human B registering identity for Agent B...');
|
|
331
|
+
const res = await humansPost('/v1/identities', {
|
|
332
|
+
networkId: NETWORK_ID,
|
|
333
|
+
publicToken: state.agentB.publicToken,
|
|
334
|
+
agentDescription: 'E2E Test Agent B - owned by Human B',
|
|
335
|
+
}, state.humanB.idToken);
|
|
336
|
+
|
|
337
|
+
expect(res.success).toBe(true);
|
|
338
|
+
state.identityBId = res.data.id;
|
|
339
|
+
logOk(`Identity B registered — id: ${res.data.id}`);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('Agent A proposes connection to Agent B', async () => {
|
|
343
|
+
logStep(5, 'Agent A proposing connection to Agent B...');
|
|
344
|
+
const res = await humansPost('/v1/connections', {
|
|
345
|
+
toAgentToken: state.agentB.publicToken,
|
|
346
|
+
networkId: NETWORK_ID,
|
|
347
|
+
fromExplanation: 'E2E test: Agent A wants to connect with Agent B.',
|
|
348
|
+
}, state.agentA.jwt);
|
|
349
|
+
|
|
350
|
+
expect(res.success).toBe(true);
|
|
351
|
+
expect(res.data.id).toBeDefined();
|
|
352
|
+
expect(res.data.status).toBe('proposed');
|
|
353
|
+
|
|
354
|
+
state.connectionId = res.data.id;
|
|
355
|
+
logOk(`Connection proposed — id: ${res.data.id}`);
|
|
356
|
+
logDetail(`Status: ${res.data.status}`);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test('connection appears in Agent B pending list', async () => {
|
|
360
|
+
logStep(6, 'Checking Agent B pending connections...');
|
|
361
|
+
const res = await humansGet('/v1/connections/pending', state.agentB.jwt);
|
|
362
|
+
expect(res.success).toBe(true);
|
|
363
|
+
const found = res.data.find((c: any) => c.id === state.connectionId);
|
|
364
|
+
expect(found).toBeDefined();
|
|
365
|
+
logOk('Connection found in Agent B pending list');
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe('Op 9: Accept Connection', () => {
|
|
370
|
+
test('Agent B accepts the connection', async () => {
|
|
371
|
+
logStep(1, `Agent B accepting connection ${state.connectionId}...`);
|
|
372
|
+
const res = await humansPost(
|
|
373
|
+
`/v1/connections/${state.connectionId}/agent-accept`,
|
|
374
|
+
{ toExplanation: 'E2E test: Agent B accepts the connection.' },
|
|
375
|
+
state.agentB.jwt
|
|
376
|
+
);
|
|
377
|
+
expect(res.success).toBe(true);
|
|
378
|
+
expect(res.data.status).toBe('agent_accepted');
|
|
379
|
+
logOk(`Connection accepted — status: ${res.data.status}`);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test('Human A accepts the connection', async () => {
|
|
383
|
+
logStep(2, 'Human A accepting connection...');
|
|
384
|
+
const res = await humansPost(
|
|
385
|
+
`/v1/connections/${state.connectionId}/human-accept`,
|
|
386
|
+
{},
|
|
387
|
+
state.humanA.idToken
|
|
388
|
+
);
|
|
389
|
+
expect(res.success).toBe(true);
|
|
390
|
+
expect(['human_pending', 'connected']).toContain(res.data.status);
|
|
391
|
+
logOk(`Human A accepted — status: ${res.data.status}`);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test('Human B accepts the connection → connected', async () => {
|
|
395
|
+
logStep(3, 'Human B accepting connection...');
|
|
396
|
+
const res = await humansPost(
|
|
397
|
+
`/v1/connections/${state.connectionId}/human-accept`,
|
|
398
|
+
{},
|
|
399
|
+
state.humanB.idToken
|
|
400
|
+
);
|
|
401
|
+
expect(res.success).toBe(true);
|
|
402
|
+
expect(res.data.status).toBe('connected');
|
|
403
|
+
logOk('Connection is now CONNECTED!');
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
describe('Op 10: Verify Final State & Cleanup', () => {
|
|
408
|
+
test('lists contacts for Human A', async () => {
|
|
409
|
+
logStep(1, "Listing Human A's contacts...");
|
|
410
|
+
const res = await humansGet('/v1/contacts', state.humanA.idToken);
|
|
411
|
+
expect(res.success).toBe(true);
|
|
412
|
+
expect(res.data.length).toBeGreaterThanOrEqual(1);
|
|
413
|
+
logOk(`Human A has ${res.data.length} contact(s)`);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('lists contacts for Human B', async () => {
|
|
417
|
+
logStep(2, "Listing Human B's contacts...");
|
|
418
|
+
const res = await humansGet('/v1/contacts', state.humanB.idToken);
|
|
419
|
+
expect(res.success).toBe(true);
|
|
420
|
+
expect(res.data.length).toBeGreaterThanOrEqual(1);
|
|
421
|
+
logOk(`Human B has ${res.data.length} contact(s)`);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('lists mine networks for Agent A', async () => {
|
|
425
|
+
logStep(3, "Fetching Agent A's networks (mine)...");
|
|
426
|
+
const res = await mainGet('/v1/networks/mine', state.agentA.jwt);
|
|
427
|
+
expect(res.success).toBe(true);
|
|
428
|
+
const found = res.data.find((n: any) => n.id === NETWORK_ID);
|
|
429
|
+
expect(found).toBeDefined();
|
|
430
|
+
logOk('gamers-united found in Agent A mine networks');
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('Agent A can re-login', async () => {
|
|
434
|
+
logStep(4, 'Re-logging Agent A...');
|
|
435
|
+
const res = await mainPost('/v1/auth/login', {
|
|
436
|
+
networkId: NETWORK_ID,
|
|
437
|
+
publicToken: state.agentA.publicToken,
|
|
438
|
+
privateKey: state.agentA.privateKey,
|
|
439
|
+
});
|
|
440
|
+
expect(res.success).toBe(true);
|
|
441
|
+
expect(res.data.jwt).toBeDefined();
|
|
442
|
+
expect(res.data.publicToken).toBe(state.agentA.publicToken);
|
|
443
|
+
logOk('Agent A re-login successful');
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test('Agent B can re-login', async () => {
|
|
447
|
+
logStep(5, 'Re-logging Agent B...');
|
|
448
|
+
const res = await mainPost('/v1/auth/login', {
|
|
449
|
+
networkId: NETWORK_ID,
|
|
450
|
+
publicToken: state.agentB.publicToken,
|
|
451
|
+
privateKey: state.agentB.privateKey,
|
|
452
|
+
});
|
|
453
|
+
expect(res.success).toBe(true);
|
|
454
|
+
logOk('Agent B re-login successful');
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
test('connection status is connected', async () => {
|
|
458
|
+
logStep(6, "Checking Human A's connections...");
|
|
459
|
+
const res = await humansGet('/v1/connections/mine', state.humanA.idToken);
|
|
460
|
+
expect(res.success).toBe(true);
|
|
461
|
+
const conn = res.data.find((c: any) => c.id === state.connectionId);
|
|
462
|
+
expect(conn).toBeDefined();
|
|
463
|
+
expect(conn.status).toBe('connected');
|
|
464
|
+
logOk('Connection confirmed connected');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('cleans up identities and Firebase users', async () => {
|
|
468
|
+
logStep(7, 'Cleaning up test data...');
|
|
469
|
+
await cleanupIdentities(state.humanA.idToken, state.identityAId);
|
|
470
|
+
logOk('Identity A removed');
|
|
471
|
+
await cleanupIdentities(state.humanB.idToken, state.identityBId);
|
|
472
|
+
logOk('Identity B removed');
|
|
473
|
+
await deleteFirebaseUser(state.humanA.idToken);
|
|
474
|
+
logOk('Human A deleted');
|
|
475
|
+
await deleteFirebaseUser(state.humanB.idToken);
|
|
476
|
+
logOk('Human B deleted');
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
|
|
6
|
+
RED='\033[0;31m'
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
YELLOW='\033[1;33m'
|
|
9
|
+
CYAN='\033[0;36m'
|
|
10
|
+
NC='\033[0m'
|
|
11
|
+
|
|
12
|
+
log() { echo -e "${CYAN}[e2e]${NC} $1"; }
|
|
13
|
+
ok() { echo -e "${GREEN}[e2e]${NC} $1"; }
|
|
14
|
+
warn() { echo -e "${YELLOW}[e2e]${NC} $1"; }
|
|
15
|
+
fail() { echo -e "${RED}[e2e]${NC} $1"; }
|
|
16
|
+
|
|
17
|
+
log "Running pre-test cleanup..."
|
|
18
|
+
npx ts-node "$SCRIPT_DIR/cleanup.ts" 2>/dev/null || warn "Pre-cleanup had warnings (may be empty DB)"
|
|
19
|
+
|
|
20
|
+
MODE="${1:-cli}"
|
|
21
|
+
|
|
22
|
+
if [ "$MODE" = "cli" ]; then
|
|
23
|
+
log "Running CLI-based e2e integration tests..."
|
|
24
|
+
TEST_FILE="tests/integration/e2e-cli.test.ts"
|
|
25
|
+
elif [ "$MODE" = "api" ]; then
|
|
26
|
+
TEST_FILE="tests/integration/e2e.test.ts"
|
|
27
|
+
else
|
|
28
|
+
fail "Unknown mode: $MODE. Use 'cli' or 'api'."
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
TEST_EXIT=0
|
|
33
|
+
npx jest --runInBand "$TEST_FILE" || TEST_EXIT=$?
|
|
34
|
+
|
|
35
|
+
log "Running post-test cleanup..."
|
|
36
|
+
npx ts-node "$SCRIPT_DIR/cleanup.ts" || warn "Post-cleanup had warnings"
|
|
37
|
+
|
|
38
|
+
if [ $TEST_EXIT -eq 0 ]; then
|
|
39
|
+
ok "All tests passed. State cleaned."
|
|
40
|
+
else
|
|
41
|
+
fail "Tests failed (exit $TEST_EXIT). State cleaned."
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
exit $TEST_EXIT
|