@seeka-labs/cli-apps 3.8.10 → 3.9.2

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.
Files changed (72) hide show
  1. package/README.md +286 -0
  2. package/dist/index.cjs +821 -612
  3. package/dist/init-template.zip +0 -0
  4. package/package.json +5 -3
  5. package/dist/ai-context/common/README.md +0 -110
  6. package/dist/ai-context/common/architecture/overview.md +0 -140
  7. package/dist/ai-context/common/guides/common-patterns.md +0 -471
  8. package/dist/ai-context/common/guides/data-flow.md +0 -455
  9. package/dist/ai-context/common/guides/getting-started.md +0 -349
  10. package/dist/ai-context/common/guides/testing.md +0 -592
  11. package/dist/ai-context/common/reference/cli-commands.md +0 -231
  12. package/dist/ai-context/common/reference/config-schema.md +0 -345
  13. package/dist/ai-context/common/reference/sdk-api.md +0 -445
  14. package/dist/ai-context/public/architecture/app-structure.md +0 -385
  15. package/dist/index.cjs.map +0 -7
  16. package/dist/init-template/.github/workflows/deploy-azurefunc.yml +0 -198
  17. package/dist/init-template/.gitlab-ci.yml +0 -67
  18. package/dist/init-template/.nvmrc +0 -1
  19. package/dist/init-template/AGENTS.md +0 -99
  20. package/dist/init-template/README.azurefunc.md +0 -238
  21. package/dist/init-template/app/.eslintrc.cjs +0 -13
  22. package/dist/init-template/app/browser/README.md +0 -1
  23. package/dist/init-template/app/browser/package.json +0 -36
  24. package/dist/init-template/app/browser/scripts/esbuild/build-browser-plugin.mjs +0 -130
  25. package/dist/init-template/app/browser/scripts/esbuild/plugins/importAsGlobals.mjs +0 -39
  26. package/dist/init-template/app/browser/src/browser.ts +0 -12
  27. package/dist/init-template/app/browser/src/plugin/index.ts +0 -61
  28. package/dist/init-template/app/browser/tsconfig.json +0 -34
  29. package/dist/init-template/app/lib/package.json +0 -46
  30. package/dist/init-template/app/lib/src/index.ts +0 -4
  31. package/dist/init-template/app/lib/src/models/index.ts +0 -29
  32. package/dist/init-template/app/lib/src/validation/index.ts +0 -14
  33. package/dist/init-template/app/lib/tsconfig.json +0 -22
  34. package/dist/init-template/app/server-azurefunc/.eslintrc.cjs +0 -4
  35. package/dist/init-template/app/server-azurefunc/.funcignore +0 -20
  36. package/dist/init-template/app/server-azurefunc/README.md +0 -105
  37. package/dist/init-template/app/server-azurefunc/host.json +0 -31
  38. package/dist/init-template/app/server-azurefunc/local.settings.template.json +0 -30
  39. package/dist/init-template/app/server-azurefunc/package.json +0 -68
  40. package/dist/init-template/app/server-azurefunc/scripts/build.mjs +0 -67
  41. package/dist/init-template/app/server-azurefunc/scripts/dev-queue-setup.js +0 -55
  42. package/dist/init-template/app/server-azurefunc/src/app/api/router.ts +0 -15
  43. package/dist/init-template/app/server-azurefunc/src/app/api/routes/getInstallationSettings.ts +0 -13
  44. package/dist/init-template/app/server-azurefunc/src/app/api/routes/setInstallationSettings.ts +0 -35
  45. package/dist/init-template/app/server-azurefunc/src/app/jobs/index.ts +0 -61
  46. package/dist/init-template/app/server-azurefunc/src/app/logging/index.ts +0 -4
  47. package/dist/init-template/app/server-azurefunc/src/app/models/index.ts +0 -12
  48. package/dist/init-template/app/server-azurefunc/src/app/services/activites.ts +0 -8
  49. package/dist/init-template/app/server-azurefunc/src/functions/healthCheck.ts +0 -19
  50. package/dist/init-template/app/server-azurefunc/src/functions/seekaAppWebhook.ts +0 -204
  51. package/dist/init-template/app/server-azurefunc/src/functions/trackActivityQueueHandler.ts +0 -48
  52. package/dist/init-template/app/server-azurefunc/src/functions/ui.ts +0 -49
  53. package/dist/init-template/app/server-azurefunc/tsconfig.json +0 -24
  54. package/dist/init-template/app/ui/README.md +0 -40
  55. package/dist/init-template/app/ui/index.html +0 -21
  56. package/dist/init-template/app/ui/package.json +0 -72
  57. package/dist/init-template/app/ui/public/favicon.ico +0 -0
  58. package/dist/init-template/app/ui/scripts/copy-output.mjs +0 -30
  59. package/dist/init-template/app/ui/src/App.tsx +0 -72
  60. package/dist/init-template/app/ui/src/assets/app-icon.svg +0 -1
  61. package/dist/init-template/app/ui/src/components/setup/steps/complete/index.tsx +0 -32
  62. package/dist/init-template/app/ui/src/components/setup/steps/first/index.tsx +0 -27
  63. package/dist/init-template/app/ui/src/components/setup/steps/index.tsx +0 -22
  64. package/dist/init-template/app/ui/src/components/setup/steps/second/index.tsx +0 -38
  65. package/dist/init-template/app/ui/src/index.tsx +0 -45
  66. package/dist/init-template/app/ui/src/routes/home/index.tsx +0 -21
  67. package/dist/init-template/app/ui/src/vite-env.d.ts +0 -13
  68. package/dist/init-template/app/ui/tsconfig.json +0 -35
  69. package/dist/init-template/app/ui/tsconfig.node.json +0 -10
  70. package/dist/init-template/app/ui/vite.config.mts +0 -48
  71. package/dist/init-template/package.json +0 -46
  72. package/dist/init-template/tsconfig.json +0 -24
@@ -1,592 +0,0 @@
1
- # Testing Seeka Apps
2
-
3
- This guide covers testing strategies and best practices for Seeka apps.
4
-
5
- ## Testing Stack
6
-
7
- Seeka apps use the following testing tools:
8
-
9
- | Tool | Purpose |
10
- |------|---------|
11
- | Jest | Test runner and assertion library |
12
- | ts-jest | TypeScript support for Jest |
13
-
14
- ## Test Structure
15
-
16
- ```
17
- app/
18
- ├── server/
19
- │ └── src/
20
- │ └── app/
21
- │ └── services/
22
- │ ├── myService.ts
23
- │ └── __tests__/
24
- │ └── myService.test.ts
25
- └── lib/
26
- └── src/
27
- ├── validation/
28
- │ ├── index.ts
29
- │ └── __tests__/
30
- │ └── index.test.ts
31
- └── models/
32
- └── __tests__/
33
- └── index.test.ts
34
- ```
35
-
36
- ## Unit Testing
37
-
38
- ### Testing Services
39
-
40
- ```typescript
41
- // services/__tests__/externalPlatform.test.ts
42
- import { ExternalPlatformService } from '../externalPlatform';
43
-
44
- // Mock logger
45
- const mockLogger = {
46
- debug: jest.fn(),
47
- info: jest.fn(),
48
- warn: jest.fn(),
49
- error: jest.fn()
50
- } as any;
51
-
52
- describe('ExternalPlatformService', () => {
53
- beforeEach(() => {
54
- jest.clearAllMocks();
55
- });
56
-
57
- describe('sendEvent', () => {
58
- it('should send event successfully', async () => {
59
- // Arrange
60
- global.fetch = jest.fn().mockResolvedValue({
61
- ok: true,
62
- json: () => Promise.resolve({ success: true })
63
- });
64
-
65
- const service = new ExternalPlatformService({
66
- apiKey: 'test-api-key',
67
- accountId: 'test-account'
68
- }, mockLogger);
69
-
70
- // Act
71
- const result = await service.sendEvent({
72
- event_name: 'Purchase',
73
- value: 100
74
- });
75
-
76
- // Assert
77
- expect(result).toBe(true);
78
- expect(fetch).toHaveBeenCalledWith(
79
- expect.stringContaining('/events'),
80
- expect.objectContaining({
81
- method: 'POST',
82
- headers: expect.objectContaining({
83
- 'Authorization': 'Bearer test-api-key'
84
- })
85
- })
86
- );
87
- });
88
-
89
- it('should handle API errors', async () => {
90
- // Arrange
91
- global.fetch = jest.fn().mockResolvedValue({
92
- ok: false,
93
- status: 400,
94
- text: () => Promise.resolve('Bad request')
95
- });
96
-
97
- const service = new ExternalPlatformService({
98
- apiKey: 'test-api-key',
99
- accountId: 'test-account'
100
- }, mockLogger);
101
-
102
- // Act & Assert
103
- await expect(service.sendEvent({ event_name: 'Test' }))
104
- .rejects.toThrow('API error');
105
-
106
- expect(mockLogger.error).toHaveBeenCalled();
107
- });
108
-
109
- it('should handle network errors', async () => {
110
- // Arrange
111
- global.fetch = jest.fn().mockRejectedValue(new Error('Network error'));
112
-
113
- const service = new ExternalPlatformService({
114
- apiKey: 'test-api-key',
115
- accountId: 'test-account'
116
- }, mockLogger);
117
-
118
- // Act & Assert
119
- await expect(service.sendEvent({ event_name: 'Test' }))
120
- .rejects.toThrow('Network error');
121
- });
122
- });
123
- });
124
- ```
125
-
126
- ### Testing Validation
127
-
128
- ```typescript
129
- // validation/__tests__/index.test.ts
130
- import { validateInstallationSettings } from '../index';
131
-
132
- const mockLogger = {
133
- debug: jest.fn(),
134
- info: jest.fn(),
135
- warn: jest.fn(),
136
- error: jest.fn()
137
- } as any;
138
-
139
- describe('validateInstallationSettings', () => {
140
- beforeEach(() => {
141
- jest.clearAllMocks();
142
- });
143
-
144
- it('should return null for valid settings', async () => {
145
- const settings = {
146
- apiKey: 'valid-api-key',
147
- accountId: 'valid-account-id'
148
- };
149
-
150
- const result = await validateInstallationSettings(settings, mockLogger);
151
-
152
- expect(result).toBeNull();
153
- });
154
-
155
- it('should return error for missing apiKey', async () => {
156
- const settings = {
157
- accountId: 'valid-account-id'
158
- };
159
-
160
- const result = await validateInstallationSettings(settings as any, mockLogger);
161
-
162
- expect(result).toBe('API Key is required');
163
- });
164
-
165
- it('should return error for missing accountId', async () => {
166
- const settings = {
167
- apiKey: 'valid-api-key'
168
- };
169
-
170
- const result = await validateInstallationSettings(settings as any, mockLogger);
171
-
172
- expect(result).toBe('Account ID is required');
173
- });
174
- });
175
- ```
176
-
177
- ### Testing Data Transformations
178
-
179
- ```typescript
180
- // services/__tests__/dataMapper.test.ts
181
- import { mapActivityToPlatformEvent } from '../dataMapper';
182
-
183
- describe('mapActivityToPlatformEvent', () => {
184
- it('should map activity to platform event format', () => {
185
- const activity = {
186
- activity: {
187
- activityId: 'act-123',
188
- activityName: 'Purchase',
189
- timestamp: '2024-01-15T10:30:00Z',
190
- properties: {
191
- currency: 'USD',
192
- value: 99.99
193
- }
194
- },
195
- person: {
196
- personId: 'person-123',
197
- emailHashed: 'abc123hash',
198
- phoneHashed: 'def456hash'
199
- }
200
- };
201
-
202
- const result = mapActivityToPlatformEvent(activity);
203
-
204
- expect(result).toEqual({
205
- event_name: 'Purchase',
206
- event_time: expect.any(Number),
207
- user_data: {
208
- em: ['abc123hash'],
209
- ph: ['def456hash']
210
- },
211
- custom_data: {
212
- currency: 'USD',
213
- value: 99.99,
214
- content_ids: undefined
215
- }
216
- });
217
- });
218
-
219
- it('should handle missing person data', () => {
220
- const activity = {
221
- activity: {
222
- activityId: 'act-123',
223
- activityName: 'PageView',
224
- timestamp: '2024-01-15T10:30:00Z'
225
- }
226
- };
227
-
228
- const result = mapActivityToPlatformEvent(activity);
229
-
230
- expect(result.user_data.em).toBeUndefined();
231
- expect(result.user_data.ph).toBeUndefined();
232
- });
233
-
234
- it('should map activity names correctly', () => {
235
- const testCases = [
236
- { input: 'PageView', expected: 'ViewContent' },
237
- { input: 'Purchase', expected: 'Purchase' },
238
- { input: 'AddToCart', expected: 'AddToCart' },
239
- { input: 'CustomEvent', expected: 'CustomEvent' }
240
- ];
241
-
242
- testCases.forEach(({ input, expected }) => {
243
- const activity = {
244
- activity: {
245
- activityId: 'act-123',
246
- activityName: input,
247
- timestamp: '2024-01-15T10:30:00Z'
248
- }
249
- };
250
-
251
- const result = mapActivityToPlatformEvent(activity);
252
- expect(result.event_name).toBe(expected);
253
- });
254
- });
255
- });
256
- ```
257
-
258
- ## Integration Testing
259
-
260
- ### Testing Webhook Handlers
261
-
262
- ```typescript
263
- // functions/__tests__/seekaAppWebhook.test.ts
264
- import { seekaAppWebhook } from '../seekaAppWebhook';
265
- import { InvocationContext } from '@azure/functions';
266
-
267
- // Mock SDK functions
268
- jest.mock('@seeka-labs/sdk-apps-server', () => ({
269
- throwOnInvalidWebhookSignature: jest.fn(),
270
- SeekaWebhookCallType: {
271
- Probe: 'Probe',
272
- AppInstalled: 'AppInstalled',
273
- ActivityAccepted: 'ActivityAccepted'
274
- }
275
- }));
276
-
277
- jest.mock('@seeka-labs/sdk-apps-server-host', () => ({
278
- tryGetInstallation: jest.fn(),
279
- createOrUpdateInstallation: jest.fn(),
280
- startServices: jest.fn(),
281
- webhookLogger: jest.fn(() => mockLogger),
282
- childLogger: jest.fn(() => mockLogger)
283
- }));
284
-
285
- const mockLogger = {
286
- debug: jest.fn(),
287
- info: jest.fn(),
288
- verbose: jest.fn(),
289
- warn: jest.fn(),
290
- error: jest.fn(),
291
- profile: jest.fn()
292
- };
293
-
294
- describe('seekaAppWebhook', () => {
295
- const mockContext = {
296
- invocationId: 'test-invocation-id'
297
- } as InvocationContext;
298
-
299
- beforeEach(() => {
300
- jest.clearAllMocks();
301
- });
302
-
303
- it('should return 204 for probe requests', async () => {
304
- const req = createMockRequest({
305
- type: 'Probe'
306
- });
307
-
308
- const result = await seekaAppWebhook(req, mockContext);
309
-
310
- expect(result.status).toBe(204);
311
- });
312
-
313
- it('should return 400 for missing body', async () => {
314
- const req = createMockRequest(null);
315
-
316
- const result = await seekaAppWebhook(req, mockContext);
317
-
318
- expect(result.status).toBe(400);
319
- });
320
-
321
- it('should return 401 for invalid signature', async () => {
322
- const { throwOnInvalidWebhookSignature } = require('@seeka-labs/sdk-apps-server');
323
- throwOnInvalidWebhookSignature.mockImplementation(() => {
324
- throw new Error('Invalid signature');
325
- });
326
-
327
- const req = createMockRequest({
328
- type: 'AppInstalled',
329
- context: { applicationInstallId: 'test-id' }
330
- });
331
-
332
- const result = await seekaAppWebhook(req, mockContext);
333
-
334
- expect(result.status).toBe(401);
335
- });
336
- });
337
-
338
- function createMockRequest(body: any) {
339
- return {
340
- text: () => Promise.resolve(body ? JSON.stringify(body) : ''),
341
- headers: new Map([
342
- ['x-seeka-signature', 'test-signature']
343
- ])
344
- } as any;
345
- }
346
- ```
347
-
348
- ### Testing Queue Handlers
349
-
350
- ```typescript
351
- // functions/__tests__/trackActivityQueueHandler.test.ts
352
- import { processQueueItem } from '../trackActivityQueueHandler';
353
-
354
- jest.mock('@seeka-labs/sdk-apps-server-host', () => ({
355
- tryGetInstallation: jest.fn(),
356
- startServices: jest.fn(),
357
- childLogger: jest.fn(() => mockLogger)
358
- }));
359
-
360
- jest.mock('../../app/services/activities', () => ({
361
- processActivity: jest.fn()
362
- }));
363
-
364
- const mockLogger = {
365
- debug: jest.fn(),
366
- info: jest.fn(),
367
- error: jest.fn()
368
- };
369
-
370
- describe('trackActivityQueueHandler', () => {
371
- beforeEach(() => {
372
- jest.clearAllMocks();
373
- });
374
-
375
- it('should process activity successfully', async () => {
376
- const { tryGetInstallation } = require('@seeka-labs/sdk-apps-server-host');
377
- const { processActivity } = require('../../app/services/activities');
378
-
379
- tryGetInstallation.mockResolvedValue({
380
- applicationInstallId: 'test-install-id',
381
- installationState: {
382
- installationSettings: { apiKey: 'test-key' }
383
- }
384
- });
385
-
386
- const queueItem = {
387
- payload: { activity: { activityId: 'act-123' } },
388
- applicationInstallId: 'test-install-id'
389
- };
390
-
391
- await processQueueItem(queueItem, mockLogger);
392
-
393
- expect(processActivity).toHaveBeenCalled();
394
- });
395
-
396
- it('should handle missing installation', async () => {
397
- const { tryGetInstallation } = require('@seeka-labs/sdk-apps-server-host');
398
- tryGetInstallation.mockResolvedValue(null);
399
-
400
- const queueItem = {
401
- payload: { activity: { activityId: 'act-123' } },
402
- applicationInstallId: 'unknown-id'
403
- };
404
-
405
- await processQueueItem(queueItem, mockLogger);
406
-
407
- expect(mockLogger.error).toHaveBeenCalledWith(
408
- 'Installation not found',
409
- expect.any(Object)
410
- );
411
- });
412
- });
413
- ```
414
-
415
- ## Mocking Patterns
416
-
417
- ### Mocking External APIs
418
-
419
- ```typescript
420
- // __mocks__/externalApi.ts
421
- export const mockExternalApi = {
422
- sendEvent: jest.fn().mockResolvedValue({ success: true }),
423
- getAccount: jest.fn().mockResolvedValue({ id: 'account-123' })
424
- };
425
-
426
- // In test file
427
- jest.mock('../services/externalApi', () => mockExternalApi);
428
- ```
429
-
430
- ### Mocking Fetch
431
-
432
- ```typescript
433
- // Global fetch mock
434
- beforeEach(() => {
435
- global.fetch = jest.fn();
436
- });
437
-
438
- afterEach(() => {
439
- jest.restoreAllMocks();
440
- });
441
-
442
- // Success response
443
- (fetch as jest.Mock).mockResolvedValue({
444
- ok: true,
445
- status: 200,
446
- json: () => Promise.resolve({ data: 'test' })
447
- });
448
-
449
- // Error response
450
- (fetch as jest.Mock).mockResolvedValue({
451
- ok: false,
452
- status: 500,
453
- text: () => Promise.resolve('Server error')
454
- });
455
-
456
- // Network error
457
- (fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
458
- ```
459
-
460
- ### Mocking SDK Functions
461
-
462
- ```typescript
463
- jest.mock('@seeka-labs/sdk-apps-server-host', () => ({
464
- getInstallation: jest.fn().mockResolvedValue({
465
- applicationInstallId: 'test-id',
466
- installationState: {
467
- installationSettings: {
468
- apiKey: 'test-key'
469
- }
470
- }
471
- }),
472
- createOrUpdateInstallation: jest.fn().mockResolvedValue(undefined),
473
- triggerBackgroundJob: jest.fn().mockResolvedValue(undefined)
474
- }));
475
- ```
476
-
477
- ## Running Tests
478
-
479
- ### Commands
480
-
481
- ```bash
482
- # Run all tests
483
- yarn test
484
-
485
- # Run tests in watch mode
486
- yarn test --watch
487
-
488
- # Run specific test file
489
- yarn test myService.test.ts
490
-
491
- # Run with coverage
492
- yarn test --coverage
493
-
494
- # Run tests matching pattern
495
- yarn test --testNamePattern="should send event"
496
- ```
497
-
498
- ### Jest Configuration
499
-
500
- ```javascript
501
- // jest.config.js
502
- module.exports = {
503
- preset: 'ts-jest',
504
- testEnvironment: 'node',
505
- roots: ['<rootDir>/src'],
506
- testMatch: ['**/__tests__/**/*.test.ts'],
507
- collectCoverageFrom: [
508
- 'src/**/*.ts',
509
- '!src/**/*.d.ts',
510
- '!src/**/__tests__/**'
511
- ],
512
- coverageThreshold: {
513
- global: {
514
- branches: 80,
515
- functions: 80,
516
- lines: 80,
517
- statements: 80
518
- }
519
- }
520
- };
521
- ```
522
-
523
- ## Best Practices
524
-
525
- ### 1. Test Organization
526
-
527
- - Place tests in `__tests__` directories next to source files
528
- - Name test files with `.test.ts` suffix
529
- - Group related tests with `describe` blocks
530
- - Use clear, descriptive test names
531
-
532
- ### 2. Test Independence
533
-
534
- ```typescript
535
- // Each test should be independent
536
- beforeEach(() => {
537
- jest.clearAllMocks();
538
- // Reset any shared state
539
- });
540
- ```
541
-
542
- ### 3. Arrange-Act-Assert Pattern
543
-
544
- ```typescript
545
- it('should do something', async () => {
546
- // Arrange - set up test data and mocks
547
- const input = { data: 'test' };
548
- mockService.process.mockResolvedValue({ success: true });
549
-
550
- // Act - execute the code under test
551
- const result = await myFunction(input);
552
-
553
- // Assert - verify the results
554
- expect(result.success).toBe(true);
555
- expect(mockService.process).toHaveBeenCalledWith(input);
556
- });
557
- ```
558
-
559
- ### 4. Test Edge Cases
560
-
561
- ```typescript
562
- describe('edge cases', () => {
563
- it('should handle empty input', async () => { /* ... */ });
564
- it('should handle null values', async () => { /* ... */ });
565
- it('should handle very large inputs', async () => { /* ... */ });
566
- it('should handle special characters', async () => { /* ... */ });
567
- });
568
- ```
569
-
570
- ### 5. Error Testing
571
-
572
- ```typescript
573
- it('should throw on invalid input', async () => {
574
- await expect(myFunction(null))
575
- .rejects.toThrow('Input is required');
576
- });
577
-
578
- it('should handle API errors gracefully', async () => {
579
- mockApi.call.mockRejectedValue(new Error('API error'));
580
-
581
- const result = await myFunction(input);
582
-
583
- expect(result.success).toBe(false);
584
- expect(result.error).toBe('API error');
585
- });
586
- ```
587
-
588
- ## See Also
589
-
590
- - `getting-started.md` - Initial setup guide
591
- - `common-patterns.md` - Implementation patterns
592
- - Jest documentation: https://jestjs.io/docs/getting-started