@venizia/ignis-docs 0.0.8-3 → 0.1.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.
Files changed (213) hide show
  1. package/README.md +7 -7
  2. package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
  3. package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
  4. package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
  5. package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
  9. package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
  11. package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
  14. package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
  16. package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
  17. package/{wiki → content}/best-practices/data-modeling.md +44 -20
  18. package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
  19. package/{wiki → content}/best-practices/error-handling.md +48 -24
  20. package/{wiki → content}/best-practices/index.md +5 -5
  21. package/{wiki → content}/best-practices/performance-optimization.md +36 -28
  22. package/{wiki → content}/best-practices/security-guidelines.md +52 -23
  23. package/{wiki → content}/best-practices/testing-strategies.md +65 -51
  24. package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
  25. package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
  26. package/{wiki → content}/extensions/components/authentication/api.md +19 -19
  27. package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
  28. package/{wiki → content}/extensions/components/authentication/index.md +10 -8
  29. package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
  30. package/{wiki → content}/extensions/components/authorization/api.md +45 -25
  31. package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
  32. package/{wiki → content}/extensions/components/authorization/index.md +11 -10
  33. package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
  34. package/{wiki → content}/extensions/components/health-check.md +1 -1
  35. package/{wiki → content}/extensions/components/index.md +5 -5
  36. package/{wiki → content}/extensions/components/mail/errors.md +15 -15
  37. package/{wiki → content}/extensions/components/mail/index.md +1 -2
  38. package/{wiki → content}/extensions/components/mail/usage.md +1 -1
  39. package/{wiki → content}/extensions/components/request-tracker.md +1 -1
  40. package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
  41. package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
  42. package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
  43. package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
  44. package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
  45. package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
  46. package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
  47. package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
  48. package/{wiki → content}/extensions/components/template/index.md +2 -2
  49. package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
  50. package/{wiki → content}/extensions/components/websocket/api.md +3 -3
  51. package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
  52. package/{wiki → content}/extensions/components/websocket/index.md +5 -5
  53. package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
  54. package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
  55. package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
  56. package/{wiki → content}/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +283 -0
  58. package/{wiki → content}/extensions/helpers/index.md +2 -3
  59. package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
  60. package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
  61. package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
  62. package/{wiki → content}/extensions/helpers/network/index.md +6 -0
  63. package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
  64. package/content/extensions/helpers/redis/index.md +713 -0
  65. package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
  66. package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
  67. package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
  68. package/{wiki → content}/extensions/helpers/template/index.md +6 -3
  69. package/{wiki → content}/extensions/helpers/types/index.md +11 -8
  70. package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
  71. package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
  72. package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
  73. package/{wiki → content}/extensions/index.md +3 -4
  74. package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
  75. package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
  76. package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
  77. package/{wiki → content}/guides/core-concepts/components.md +19 -10
  78. package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
  79. package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
  80. package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
  81. package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
  82. package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
  83. package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
  84. package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
  85. package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
  86. package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
  87. package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
  88. package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
  89. package/content/guides/core-concepts/services.md +389 -0
  90. package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
  91. package/{wiki → content}/guides/get-started/philosophy.md +36 -36
  92. package/{wiki → content}/guides/get-started/setup.md +3 -3
  93. package/{wiki → content}/guides/index.md +3 -3
  94. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  95. package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
  96. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  97. package/{wiki → content}/guides/reference/glossary.md +19 -12
  98. package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
  99. package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
  100. package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
  101. package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
  102. package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
  103. package/content/guides/tutorials/testing.md +264 -0
  104. package/content/index.md +5 -0
  105. package/content/public/apple-touch-icon.png +0 -0
  106. package/content/public/og-image.png +0 -0
  107. package/content/public/site.webmanifest +11 -0
  108. package/{wiki → content}/references/base/application.md +4 -5
  109. package/{wiki → content}/references/base/bootstrapping.md +18 -5
  110. package/{wiki → content}/references/base/components.md +149 -120
  111. package/content/references/base/connectors.md +178 -0
  112. package/{wiki → content}/references/base/controllers.md +41 -30
  113. package/content/references/base/datasources.md +527 -0
  114. package/{wiki → content}/references/base/dependency-injection.md +34 -22
  115. package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
  116. package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
  117. package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
  118. package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
  119. package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
  120. package/{wiki → content}/references/base/filter-system/index.md +6 -3
  121. package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
  122. package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
  123. package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
  124. package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
  125. package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
  126. package/{wiki → content}/references/base/filter-system/tips.md +5 -5
  127. package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
  128. package/{wiki → content}/references/base/grpc-controllers.md +13 -13
  129. package/{wiki → content}/references/base/index.md +24 -12
  130. package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
  131. package/{wiki → content}/references/base/models.md +63 -49
  132. package/{wiki → content}/references/base/providers.md +136 -130
  133. package/{wiki → content}/references/base/repositories/advanced.md +59 -58
  134. package/{wiki → content}/references/base/repositories/index.md +115 -91
  135. package/content/references/base/repositories/mixins.md +99 -0
  136. package/{wiki → content}/references/base/repositories/relations.md +54 -64
  137. package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
  138. package/content/references/base/services.md +404 -0
  139. package/{wiki → content}/references/configuration/environment-variables.md +46 -30
  140. package/{wiki → content}/references/configuration/index.md +6 -6
  141. package/{wiki → content}/references/index.md +17 -12
  142. package/{wiki → content}/references/quick-reference.md +65 -106
  143. package/content/references/utilities/crypto.md +98 -0
  144. package/{wiki → content}/references/utilities/index.md +3 -3
  145. package/{wiki → content}/references/utilities/jsx.md +6 -4
  146. package/content/references/utilities/module.md +90 -0
  147. package/{wiki → content}/references/utilities/parse.md +4 -14
  148. package/{wiki → content}/references/utilities/promise.md +9 -7
  149. package/{wiki → content}/references/utilities/schema.md +5 -3
  150. package/dist/mcp-server/common/guards.d.ts +8 -0
  151. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  152. package/dist/mcp-server/common/guards.js +14 -0
  153. package/dist/mcp-server/common/guards.js.map +1 -0
  154. package/dist/mcp-server/common/index.d.ts +1 -0
  155. package/dist/mcp-server/common/index.d.ts.map +1 -1
  156. package/dist/mcp-server/common/index.js +1 -0
  157. package/dist/mcp-server/common/index.js.map +1 -1
  158. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  159. package/dist/mcp-server/common/paths.js +2 -2
  160. package/dist/mcp-server/common/paths.js.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  162. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  163. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  164. package/dist/mcp-server/helpers/github.helper.js +1 -1
  165. package/dist/mcp-server/index.js +7 -2
  166. package/dist/mcp-server/index.js.map +1 -1
  167. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  168. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  169. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  170. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  175. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  178. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  179. package/package.json +12 -12
  180. package/wiki/extensions/helpers/error/index.md +0 -227
  181. package/wiki/extensions/helpers/redis/index.md +0 -488
  182. package/wiki/extensions/helpers/testing/index.md +0 -510
  183. package/wiki/guides/core-concepts/services.md +0 -119
  184. package/wiki/guides/tutorials/testing.md +0 -722
  185. package/wiki/index.md +0 -183
  186. package/wiki/references/base/datasources.md +0 -454
  187. package/wiki/references/base/middlewares.md +0 -590
  188. package/wiki/references/base/repositories/mixins.md +0 -335
  189. package/wiki/references/base/services.md +0 -201
  190. package/wiki/references/utilities/crypto.md +0 -56
  191. package/wiki/references/utilities/module.md +0 -42
  192. /package/{wiki → content}/extensions/components/mail/api.md +0 -0
  193. /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
  194. /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
  195. /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
  196. /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
  197. /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
  198. /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
  199. /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
  200. /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
  201. /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
  202. /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
  203. /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
  204. /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
  205. /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
  206. /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
  207. /package/{wiki → content}/public/logo.svg +0 -0
  208. /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
  209. /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
  210. /package/{wiki → content}/references/utilities/date.md +0 -0
  211. /package/{wiki → content}/references/utilities/performance.md +0 -0
  212. /package/{wiki → content}/references/utilities/request.md +0 -0
  213. /package/{wiki → content}/references/utilities/statuses.md +0 -0
@@ -1,722 +0,0 @@
1
- # Testing Your Ignis Application
2
-
3
- This guide shows you how to write tests for your Ignis application.
4
-
5
- **Time to Complete:** ~30 minutes
6
-
7
- ## Choose Your Test Framework
8
-
9
- **Ignis works with any test framework.** You can use whichever testing tool you prefer:
10
-
11
- | Framework | Description |
12
- |-----------|-------------|
13
- | **Jest** | Popular, feature-rich testing framework |
14
- | **Vitest** | Fast, Vite-native testing framework |
15
- | **Bun Test** | Built-in test runner for Bun |
16
- | **Playwright** | End-to-end testing for web applications |
17
- | **node:test** | Node.js native test module |
18
- | **Mocha** | Flexible testing framework |
19
- | **Any other** | All test frameworks work with Ignis |
20
-
21
- Since Ignis is just a TypeScript/JavaScript application framework, you can test it with any tool that supports TypeScript.
22
-
23
- > [!TIP] IGNIS Testing Extension
24
- > IGNIS provides its own testing utilities built on `node:test`. These utilities (`TestPlan`, `TestCase`, `TestCaseHandler`) offer a structured approach for organizing tests with lifecycle hooks and shared context. This is optional — use it if you prefer this pattern, or use your favorite test framework directly.
25
-
26
- ## Prerequisites
27
-
28
- Before starting, ensure you have:
29
- - A working Ignis application (see [Building a CRUD API](./building-a-crud-api.md))
30
- - Basic understanding of [Controllers](../core-concepts/rest-controllers.md) and [Repositories](../core-concepts/persistent/)
31
-
32
- ## Quick Examples with Popular Frameworks
33
-
34
- ### Using Vitest
35
-
36
- ```typescript
37
- // __tests__/todo.test.ts
38
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
39
- import { app } from '../src/application';
40
-
41
- describe('Todo API', () => {
42
- beforeAll(async () => {
43
- // Setup: start server, seed database, etc.
44
- });
45
-
46
- afterAll(async () => {
47
- // Cleanup: close connections
48
- });
49
-
50
- it('should return list of todos', async () => {
51
- const response = await app.request('/api/todos', { method: 'GET' });
52
-
53
- expect(response.status).toBe(200);
54
- const body = await response.json();
55
- expect(Array.isArray(body)).toBe(true);
56
- });
57
-
58
- it('should create a new todo', async () => {
59
- const response = await app.request('/api/todos', {
60
- method: 'POST',
61
- headers: { 'Content-Type': 'application/json' },
62
- body: JSON.stringify({ title: 'Test Todo' }),
63
- });
64
-
65
- expect(response.status).toBe(201);
66
- const body = await response.json();
67
- expect(body.title).toBe('Test Todo');
68
- });
69
- });
70
- ```
71
-
72
- ### Using Jest
73
-
74
- ```typescript
75
- // __tests__/todo.test.ts
76
- import { app } from '../src/application';
77
-
78
- describe('Todo API', () => {
79
- it('should return list of todos', async () => {
80
- const response = await app.request('/api/todos', { method: 'GET' });
81
-
82
- expect(response.status).toBe(200);
83
- const body = await response.json();
84
- expect(Array.isArray(body)).toBe(true);
85
- });
86
- });
87
- ```
88
-
89
- ### Using Bun Test
90
-
91
- ```typescript
92
- // __tests__/todo.test.ts
93
- import { describe, it, expect } from 'bun:test';
94
- import { app } from '../src/application';
95
-
96
- describe('Todo API', () => {
97
- it('should return list of todos', async () => {
98
- const response = await app.request('/api/todos', { method: 'GET' });
99
-
100
- expect(response.status).toBe(200);
101
- const body = await response.json();
102
- expect(Array.isArray(body)).toBe(true);
103
- });
104
- });
105
- ```
106
-
107
- ### Using Playwright (E2E)
108
-
109
- ```typescript
110
- // e2e/todo.spec.ts
111
- import { test, expect } from '@playwright/test';
112
-
113
- test.describe('Todo Application', () => {
114
- test('should display todo list', async ({ request }) => {
115
- const response = await request.get('http://localhost:3000/api/todos');
116
-
117
- expect(response.ok()).toBeTruthy();
118
- const todos = await response.json();
119
- expect(Array.isArray(todos)).toBe(true);
120
- });
121
- });
122
- ```
123
-
124
- ## Using IGNIS Testing Extension
125
-
126
- IGNIS provides its own testing utilities built on `node:test` for a more structured approach.
127
-
128
- ### 1. Create Your First Test
129
-
130
- Create a test file in your project:
131
-
132
- ```typescript
133
- // __tests__/hello.test.ts
134
- import {
135
- TestPlan,
136
- TestDescribe,
137
- TestCase,
138
- TestCaseHandler,
139
- TestCaseDecisions,
140
- } from '@venizia/ignis-helpers';
141
-
142
- // Step 1: Define a Test Handler
143
- class HelloHandler extends TestCaseHandler {
144
- async execute() {
145
- // The action to test
146
- const message = 'Hello, Ignis!';
147
- return { message };
148
- }
149
-
150
- getValidator() {
151
- // Validate the result
152
- return (result: { message: string }) => {
153
- if (result.message === 'Hello, Ignis!') {
154
- return TestCaseDecisions.SUCCESS;
155
- }
156
- return TestCaseDecisions.FAIL;
157
- };
158
- }
159
- }
160
-
161
- // Step 2: Create a Test Plan
162
- const helloTestPlan = TestPlan.newInstance({
163
- scope: 'Hello World Tests',
164
- testCases: [
165
- TestCase.withOptions({
166
- code: 'HELLO-001',
167
- description: 'Should return greeting message',
168
- expectation: 'Message equals "Hello, Ignis!"',
169
- handler: new HelloHandler({ context: {} as any }),
170
- }),
171
- ],
172
- });
173
-
174
- // Step 3: Run the Test
175
- TestDescribe.withTestPlan({ testPlan: helloTestPlan }).run();
176
- ```
177
-
178
- ### 2. Run Tests
179
-
180
- ```bash
181
- # Using Bun
182
- bun test
183
-
184
- # Using Node.js
185
- node --test __tests__/*.test.ts
186
- ```
187
-
188
- ## Core Concepts
189
-
190
- ### Test Framework Components
191
-
192
- | Component | Purpose |
193
- |-----------|---------|
194
- | **TestPlan** | Organizes a test suite with lifecycle hooks and shared context |
195
- | **TestCase** | A single test unit with code, description, and handler |
196
- | **TestCaseHandler** | Encapsulates test execution and validation logic |
197
- | **TestDescribe** | Runs test plans using `node:test` |
198
-
199
- ### Test Case Decisions
200
-
201
- | Decision | Meaning |
202
- |----------|---------|
203
- | `TestCaseDecisions.SUCCESS` | Test passed |
204
- | `TestCaseDecisions.FAIL` | Test failed |
205
- | `TestCaseDecisions.UNKNOWN` | Result undetermined |
206
-
207
- ### Lifecycle Hooks
208
-
209
- | Hook | When | Use Case |
210
- |------|------|----------|
211
- | `before` | Before all tests | Start server, seed database |
212
- | `after` | After all tests | Close connections, cleanup |
213
- | `beforeEach` | Before each test | Reset state |
214
- | `afterEach` | After each test | Clear test data |
215
-
216
- ## Testing Controllers
217
-
218
- Here's how to test an HTTP controller:
219
-
220
- ```typescript
221
- // __tests__/todo.controller.test.ts
222
- import {
223
- TestPlan,
224
- TestDescribe,
225
- TestCase,
226
- TestCaseHandler,
227
- TestCaseDecisions,
228
- } from '@venizia/ignis-helpers';
229
- import { app } from '../src/application'; // Your Ignis app
230
-
231
- // Handler for testing GET /todos
232
- class GetTodosHandler extends TestCaseHandler {
233
- async execute() {
234
- // Make HTTP request to your app
235
- const response = await app.request('/api/todos', {
236
- method: 'GET',
237
- });
238
-
239
- return {
240
- status: response.status,
241
- body: await response.json(),
242
- };
243
- }
244
-
245
- getValidator() {
246
- return (result: { status: number; body: any }) => {
247
- // Validate status code
248
- if (result.status !== 200) {
249
- return TestCaseDecisions.FAIL;
250
- }
251
-
252
- // Validate response is an array
253
- if (!Array.isArray(result.body)) {
254
- return TestCaseDecisions.FAIL;
255
- }
256
-
257
- return TestCaseDecisions.SUCCESS;
258
- };
259
- }
260
- }
261
-
262
- // Handler for testing POST /todos
263
- class CreateTodoHandler extends TestCaseHandler {
264
- async execute() {
265
- const response = await app.request('/api/todos', {
266
- method: 'POST',
267
- headers: { 'Content-Type': 'application/json' },
268
- body: JSON.stringify({
269
- title: 'Test Todo',
270
- description: 'Created by test',
271
- }),
272
- });
273
-
274
- return {
275
- status: response.status,
276
- body: await response.json(),
277
- };
278
- }
279
-
280
- getValidator() {
281
- return (result: { status: number; body: any }) => {
282
- if (result.status !== 201) {
283
- return TestCaseDecisions.FAIL;
284
- }
285
-
286
- if (result.body.title !== 'Test Todo') {
287
- return TestCaseDecisions.FAIL;
288
- }
289
-
290
- return TestCaseDecisions.SUCCESS;
291
- };
292
- }
293
- }
294
-
295
- // Create test plan
296
- const todoControllerTests = TestPlan.newInstance({
297
- scope: 'Todo Controller',
298
- hooks: {
299
- before: async () => {
300
- console.log('Setting up Todo controller tests...');
301
- // Start server or setup test database
302
- },
303
- after: async () => {
304
- console.log('Cleaning up...');
305
- // Cleanup resources
306
- },
307
- },
308
- testCases: [
309
- TestCase.withOptions({
310
- code: 'TODO-001',
311
- description: 'GET /todos returns list of todos',
312
- expectation: 'Status 200 with array response',
313
- handler: new GetTodosHandler({ context: {} as any }),
314
- }),
315
- TestCase.withOptions({
316
- code: 'TODO-002',
317
- description: 'POST /todos creates a new todo',
318
- expectation: 'Status 201 with created todo',
319
- handler: new CreateTodoHandler({ context: {} as any }),
320
- }),
321
- ],
322
- });
323
-
324
- TestDescribe.withTestPlan({ testPlan: todoControllerTests }).run();
325
- ```
326
-
327
- ## Testing with Shared Context
328
-
329
- Use the test plan's context to share data between tests (like authentication tokens):
330
-
331
- ```typescript
332
- // __tests__/auth.test.ts
333
- import {
334
- TestPlan,
335
- TestDescribe,
336
- TestCase,
337
- TestCaseHandler,
338
- TestCaseDecisions,
339
- ITestContext,
340
- } from '@venizia/ignis-helpers';
341
-
342
- // Define context shape
343
- interface AuthContext {
344
- token: string;
345
- userId: string;
346
- }
347
-
348
- // Handler that uses shared context
349
- class SecureEndpointHandler extends TestCaseHandler<AuthContext> {
350
- async execute() {
351
- // Get token from context (set in before hook)
352
- const token = this.context.getSync<string>({ key: 'token' });
353
-
354
- const response = await app.request('/api/profile', {
355
- method: 'GET',
356
- headers: {
357
- Authorization: `Bearer ${token}`,
358
- },
359
- });
360
-
361
- return {
362
- status: response.status,
363
- body: await response.json(),
364
- };
365
- }
366
-
367
- getValidator() {
368
- return (result: { status: number; body: any }) => {
369
- if (result.status === 200 && result.body.id) {
370
- return TestCaseDecisions.SUCCESS;
371
- }
372
- return TestCaseDecisions.FAIL;
373
- };
374
- }
375
- }
376
-
377
- const authTests = TestPlan.newInstance<AuthContext>({
378
- scope: 'Authentication Tests',
379
- hooks: {
380
- before: async (testPlan: ITestContext<AuthContext>) => {
381
- // Login and store token in context
382
- const loginResponse = await app.request('/api/auth/login', {
383
- method: 'POST',
384
- headers: { 'Content-Type': 'application/json' },
385
- body: JSON.stringify({
386
- email: 'test@example.com',
387
- password: 'password123',
388
- }),
389
- });
390
-
391
- const { token, userId } = await loginResponse.json();
392
-
393
- // Bind to context for use in test cases
394
- testPlan.bind({ key: 'token', value: token });
395
- testPlan.bind({ key: 'userId', value: userId });
396
- },
397
- },
398
- testCases: [
399
- TestCase.withOptions({
400
- code: 'AUTH-001',
401
- description: 'Authenticated user can access profile',
402
- expectation: 'Returns user profile with status 200',
403
- handler: new SecureEndpointHandler({ context: {} as any }),
404
- }),
405
- ],
406
- });
407
-
408
- TestDescribe.withTestPlan({ testPlan: authTests }).run();
409
- ```
410
-
411
- ## Testing Repositories
412
-
413
- Test your data access layer directly:
414
-
415
- ```typescript
416
- // __tests__/todo.repository.test.ts
417
- import {
418
- TestPlan,
419
- TestDescribe,
420
- TestCase,
421
- TestCaseHandler,
422
- TestCaseDecisions,
423
- } from '@venizia/ignis-helpers';
424
- import { TodoRepository } from '../src/repositories/todo.repository';
425
- import { Container } from '@venizia/ignis-inversion';
426
-
427
- // Setup container for DI
428
- const container = new Container();
429
-
430
- class CreateTodoRepoHandler extends TestCaseHandler {
431
- async execute() {
432
- const todoRepo = container.get<TodoRepository>('repositories.TodoRepository');
433
-
434
- const created = await todoRepo.create({
435
- title: 'Repository Test',
436
- description: 'Testing repository layer',
437
- isCompleted: false,
438
- });
439
-
440
- return { todo: created };
441
- }
442
-
443
- getValidator() {
444
- return (result: { todo: any }) => {
445
- if (result.todo && result.todo.id && result.todo.title === 'Repository Test') {
446
- return TestCaseDecisions.SUCCESS;
447
- }
448
- return TestCaseDecisions.FAIL;
449
- };
450
- }
451
- }
452
-
453
- class FindTodoRepoHandler extends TestCaseHandler {
454
- async execute() {
455
- const todoRepo = container.get<TodoRepository>('repositories.TodoRepository');
456
-
457
- const todos = await todoRepo.find({
458
- where: { isCompleted: false },
459
- limit: 10,
460
- });
461
-
462
- return { todos, count: todos.length };
463
- }
464
-
465
- getValidator() {
466
- return (result: { todos: any[]; count: number }) => {
467
- if (Array.isArray(result.todos) && result.count >= 0) {
468
- return TestCaseDecisions.SUCCESS;
469
- }
470
- return TestCaseDecisions.FAIL;
471
- };
472
- }
473
- }
474
-
475
- const repoTests = TestPlan.newInstance({
476
- scope: 'Todo Repository',
477
- hooks: {
478
- before: async () => {
479
- // Setup DI container and database connection
480
- container.bind('repositories.TodoRepository').toClass(TodoRepository);
481
- },
482
- after: async () => {
483
- // Cleanup test data
484
- },
485
- },
486
- testCases: [
487
- TestCase.withOptions({
488
- code: 'REPO-001',
489
- description: 'Can create a todo via repository',
490
- expectation: 'Returns created todo with ID',
491
- handler: new CreateTodoRepoHandler({ context: {} as any }),
492
- }),
493
- TestCase.withOptions({
494
- code: 'REPO-002',
495
- description: 'Can find todos with filters',
496
- expectation: 'Returns array of matching todos',
497
- handler: new FindTodoRepoHandler({ context: {} as any }),
498
- }),
499
- ],
500
- });
501
-
502
- TestDescribe.withTestPlan({ testPlan: repoTests }).run();
503
- ```
504
-
505
- ## Testing Services
506
-
507
- Test business logic in isolation:
508
-
509
- ```typescript
510
- // __tests__/todo.service.test.ts
511
- import {
512
- TestPlan,
513
- TestDescribe,
514
- TestCase,
515
- TestCaseHandler,
516
- TestCaseDecisions,
517
- } from '@venizia/ignis-helpers';
518
- import { TodoService } from '../src/services/todo.service';
519
-
520
- class CompleteTodoHandler extends TestCaseHandler {
521
- async execute() {
522
- const todoService = new TodoService();
523
-
524
- // Create a todo first
525
- const todo = await todoService.create({
526
- title: 'Test completion',
527
- isCompleted: false,
528
- });
529
-
530
- // Mark as complete
531
- const completed = await todoService.markAsComplete(todo.id);
532
-
533
- return { original: todo, completed };
534
- }
535
-
536
- getValidator() {
537
- return (result: { original: any; completed: any }) => {
538
- // Original should be incomplete
539
- if (result.original.isCompleted !== false) {
540
- return TestCaseDecisions.FAIL;
541
- }
542
-
543
- // Completed should be complete
544
- if (result.completed.isCompleted !== true) {
545
- return TestCaseDecisions.FAIL;
546
- }
547
-
548
- return TestCaseDecisions.SUCCESS;
549
- };
550
- }
551
- }
552
-
553
- const serviceTests = TestPlan.newInstance({
554
- scope: 'Todo Service',
555
- testCases: [
556
- TestCase.withOptions({
557
- code: 'SVC-001',
558
- description: 'Can mark todo as complete',
559
- expectation: 'Todo isCompleted changes from false to true',
560
- handler: new CompleteTodoHandler({ context: {} as any }),
561
- }),
562
- ],
563
- });
564
-
565
- TestDescribe.withTestPlan({ testPlan: serviceTests }).run();
566
- ```
567
-
568
- ## Project Structure
569
-
570
- Organize your tests alongside your source code:
571
-
572
- ```
573
- my-ignis-app/
574
- ├── src/
575
- │ ├── controllers/
576
- │ ├── services/
577
- │ └── repositories/
578
- ├── __tests__/
579
- │ ├── controllers/
580
- │ │ └── todo.controller.test.ts
581
- │ ├── services/
582
- │ │ └── todo.service.test.ts
583
- │ ├── repositories/
584
- │ │ └── todo.repository.test.ts
585
- │ └── integration/
586
- │ └── auth-flow.test.ts
587
- └── package.json
588
- ```
589
-
590
- ### Package.json Scripts
591
-
592
- Choose scripts based on your preferred test framework:
593
-
594
- **Bun Test:**
595
- ```json
596
- {
597
- "scripts": {
598
- "test": "bun test",
599
- "test:watch": "bun test --watch",
600
- "test:coverage": "bun test --coverage"
601
- }
602
- }
603
- ```
604
-
605
- **Vitest:**
606
- ```json
607
- {
608
- "scripts": {
609
- "test": "vitest run",
610
- "test:watch": "vitest",
611
- "test:coverage": "vitest run --coverage"
612
- }
613
- }
614
- ```
615
-
616
- **Jest:**
617
- ```json
618
- {
619
- "scripts": {
620
- "test": "jest",
621
- "test:watch": "jest --watch",
622
- "test:coverage": "jest --coverage"
623
- }
624
- }
625
- ```
626
-
627
- **Playwright (E2E):**
628
- ```json
629
- {
630
- "scripts": {
631
- "test:e2e": "playwright test",
632
- "test:e2e:ui": "playwright test --ui"
633
- }
634
- }
635
- ```
636
-
637
- ## Best Practices
638
-
639
- ### 1. Use Descriptive Test Codes
640
-
641
- ```typescript
642
- TestCase.withOptions({
643
- code: 'AUTH-LOGIN-001', // Feature-Action-Number
644
- description: 'User can login with valid credentials',
645
- expectation: 'Returns JWT token and user ID',
646
- // ...
647
- });
648
- ```
649
-
650
- ### 2. Isolate Test Data
651
-
652
- ```typescript
653
- hooks: {
654
- beforeEach: async (testPlan) => {
655
- // Create fresh test data for each test
656
- const testTodo = await createTestTodo();
657
- testPlan.bind({ key: 'testTodoId', value: testTodo.id });
658
- },
659
- afterEach: async (testPlan) => {
660
- // Clean up after each test
661
- const todoId = testPlan.getSync({ key: 'testTodoId' });
662
- await deleteTestTodo(todoId);
663
- },
664
- }
665
- ```
666
-
667
- ### 3. Test Edge Cases
668
-
669
- ```typescript
670
- // Test empty results
671
- TestCase.withOptions({
672
- code: 'TODO-FIND-002',
673
- description: 'Returns empty array when no todos match filter',
674
- expectation: 'Empty array with status 200',
675
- handler: new FindNonExistentHandler({ context: {} as any }),
676
- });
677
-
678
- // Test validation errors
679
- TestCase.withOptions({
680
- code: 'TODO-CREATE-003',
681
- description: 'Rejects todo without title',
682
- expectation: 'Status 400 with validation error',
683
- handler: new CreateInvalidTodoHandler({ context: {} as any }),
684
- });
685
- ```
686
-
687
- ### 4. Keep Handlers Focused
688
-
689
- Each handler should test one specific behavior:
690
-
691
- ```typescript
692
- // Good: Focused on one behavior
693
- class CreateTodoHandler extends TestCaseHandler {
694
- async execute() { /* only create logic */ }
695
- }
696
-
697
- // Avoid: Multiple behaviors in one handler
698
- class CreateAndUpdateAndDeleteHandler extends TestCaseHandler {
699
- async execute() { /* too many things */ }
700
- }
701
- ```
702
-
703
- ## Next Steps
704
-
705
- - [Testing Reference](../../extensions/helpers/testing/) - Complete API documentation
706
- - [Best Practices](../../best-practices/code-style-standards/) - Code quality standards
707
- - [Troubleshooting](../../best-practices/troubleshooting-tips.md) - Common issues
708
-
709
- ## Summary
710
-
711
- | What to Test | How |
712
- |--------------|-----|
713
- | **Controllers** | Use `app.request()` to make HTTP calls |
714
- | **Services** | Instantiate and call methods directly |
715
- | **Repositories** | Use DI container, test with real/mock DB |
716
- | **Integration** | Chain multiple operations with shared context |
717
- | **E2E** | Use Playwright or similar for full flow testing |
718
-
719
- **Key Takeaways:**
720
- - Use any test framework you prefer (Jest, Vitest, Bun Test, Playwright, etc.)
721
- - IGNIS provides optional testing utilities (`TestPlan`, `TestCase`, `TestCaseHandler`) built on `node:test`
722
- - All frameworks work seamlessly with Ignis applications