odac 0.9.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/.editorconfig +21 -0
  2. package/.github/workflows/auto-pr-description.yml +49 -0
  3. package/.github/workflows/release.yml +32 -0
  4. package/.github/workflows/test-coverage.yml +58 -0
  5. package/.husky/pre-commit +2 -0
  6. package/.kiro/steering/code-style.md +56 -0
  7. package/.kiro/steering/product.md +20 -0
  8. package/.kiro/steering/structure.md +77 -0
  9. package/.kiro/steering/tech.md +87 -0
  10. package/.prettierrc +10 -0
  11. package/.releaserc.js +134 -0
  12. package/AGENTS.md +84 -0
  13. package/CHANGELOG.md +181 -0
  14. package/CODE_OF_CONDUCT.md +83 -0
  15. package/CONTRIBUTING.md +63 -0
  16. package/LICENSE +661 -0
  17. package/README.md +57 -0
  18. package/SECURITY.md +26 -0
  19. package/bin/candy +10 -0
  20. package/bin/candypack +10 -0
  21. package/cli/index.js +3 -0
  22. package/cli/src/Cli.js +348 -0
  23. package/cli/src/Connector.js +93 -0
  24. package/cli/src/Monitor.js +416 -0
  25. package/core/Candy.js +87 -0
  26. package/core/Commands.js +239 -0
  27. package/core/Config.js +1094 -0
  28. package/core/Lang.js +52 -0
  29. package/core/Log.js +43 -0
  30. package/core/Process.js +26 -0
  31. package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
  32. package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
  33. package/docs/backend/01-overview/03-development-server.md +79 -0
  34. package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
  35. package/docs/backend/03-config/00-configuration-overview.md +214 -0
  36. package/docs/backend/03-config/01-database-connection.md +60 -0
  37. package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
  38. package/docs/backend/03-config/03-request-timeout.md +11 -0
  39. package/docs/backend/03-config/04-environment-variables.md +227 -0
  40. package/docs/backend/03-config/05-early-hints.md +352 -0
  41. package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
  42. package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
  43. package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
  44. package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
  45. package/docs/backend/04-routing/05-advanced-routing.md +14 -0
  46. package/docs/backend/04-routing/06-error-pages.md +101 -0
  47. package/docs/backend/04-routing/07-cron-jobs.md +149 -0
  48. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
  49. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
  50. package/docs/backend/05-controllers/03-controller-classes.md +93 -0
  51. package/docs/backend/05-forms/01-custom-forms.md +395 -0
  52. package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
  53. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
  54. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
  55. package/docs/backend/07-views/01-the-view-directory.md +73 -0
  56. package/docs/backend/07-views/02-rendering-a-view.md +179 -0
  57. package/docs/backend/07-views/03-template-syntax.md +181 -0
  58. package/docs/backend/07-views/03-variables.md +328 -0
  59. package/docs/backend/07-views/04-request-data.md +231 -0
  60. package/docs/backend/07-views/05-conditionals.md +290 -0
  61. package/docs/backend/07-views/06-loops.md +353 -0
  62. package/docs/backend/07-views/07-translations.md +358 -0
  63. package/docs/backend/07-views/08-backend-javascript.md +398 -0
  64. package/docs/backend/07-views/09-comments.md +297 -0
  65. package/docs/backend/08-database/01-database-connection.md +99 -0
  66. package/docs/backend/08-database/02-using-mysql.md +322 -0
  67. package/docs/backend/09-validation/01-the-validator-service.md +424 -0
  68. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
  69. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
  70. package/docs/backend/10-authentication/03-register.md +134 -0
  71. package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
  72. package/docs/backend/10-authentication/05-session-management.md +159 -0
  73. package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
  74. package/docs/backend/11-mail/01-the-mail-service.md +42 -0
  75. package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
  76. package/docs/backend/13-utilities/01-candy-var.md +504 -0
  77. package/docs/frontend/01-overview/01-introduction.md +146 -0
  78. package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
  79. package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
  80. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
  81. package/docs/frontend/03-forms/01-form-handling.md +420 -0
  82. package/docs/frontend/04-api-requests/01-get-post.md +443 -0
  83. package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
  84. package/docs/index.json +452 -0
  85. package/docs/server/01-installation/01-quick-install.md +19 -0
  86. package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
  87. package/docs/server/02-get-started/01-core-concepts.md +7 -0
  88. package/docs/server/02-get-started/02-basic-commands.md +57 -0
  89. package/docs/server/02-get-started/03-cli-reference.md +276 -0
  90. package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
  91. package/docs/server/03-service/01-start-a-new-service.md +57 -0
  92. package/docs/server/03-service/02-delete-a-service.md +48 -0
  93. package/docs/server/04-web/01-create-a-website.md +36 -0
  94. package/docs/server/04-web/02-list-websites.md +9 -0
  95. package/docs/server/04-web/03-delete-a-website.md +29 -0
  96. package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
  97. package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
  98. package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
  99. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
  100. package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
  101. package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
  102. package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
  103. package/docs/server/07-mail/04-change-account-password.md +23 -0
  104. package/eslint.config.mjs +120 -0
  105. package/framework/index.js +4 -0
  106. package/framework/src/Auth.js +309 -0
  107. package/framework/src/Candy.js +81 -0
  108. package/framework/src/Config.js +79 -0
  109. package/framework/src/Env.js +60 -0
  110. package/framework/src/Lang.js +57 -0
  111. package/framework/src/Mail.js +83 -0
  112. package/framework/src/Mysql.js +575 -0
  113. package/framework/src/Request.js +301 -0
  114. package/framework/src/Route/Cron.js +128 -0
  115. package/framework/src/Route/Internal.js +439 -0
  116. package/framework/src/Route.js +455 -0
  117. package/framework/src/Server.js +15 -0
  118. package/framework/src/Stream.js +163 -0
  119. package/framework/src/Token.js +37 -0
  120. package/framework/src/Validator.js +271 -0
  121. package/framework/src/Var.js +211 -0
  122. package/framework/src/View/EarlyHints.js +190 -0
  123. package/framework/src/View/Form.js +600 -0
  124. package/framework/src/View.js +513 -0
  125. package/framework/web/candy.js +838 -0
  126. package/jest.config.js +22 -0
  127. package/locale/de-DE.json +80 -0
  128. package/locale/en-US.json +79 -0
  129. package/locale/es-ES.json +80 -0
  130. package/locale/fr-FR.json +80 -0
  131. package/locale/pt-BR.json +80 -0
  132. package/locale/ru-RU.json +80 -0
  133. package/locale/tr-TR.json +85 -0
  134. package/locale/zh-CN.json +80 -0
  135. package/package.json +86 -0
  136. package/server/index.js +5 -0
  137. package/server/src/Api.js +88 -0
  138. package/server/src/DNS.js +940 -0
  139. package/server/src/Hub.js +535 -0
  140. package/server/src/Mail.js +571 -0
  141. package/server/src/SSL.js +180 -0
  142. package/server/src/Server.js +27 -0
  143. package/server/src/Service.js +248 -0
  144. package/server/src/Subdomain.js +64 -0
  145. package/server/src/Web/Firewall.js +170 -0
  146. package/server/src/Web/Proxy.js +134 -0
  147. package/server/src/Web.js +451 -0
  148. package/server/src/mail/imap.js +1091 -0
  149. package/server/src/mail/server.js +32 -0
  150. package/server/src/mail/smtp.js +786 -0
  151. package/test/cli/Cli.test.js +36 -0
  152. package/test/core/Candy.test.js +234 -0
  153. package/test/core/Commands.test.js +538 -0
  154. package/test/core/Config.test.js +1435 -0
  155. package/test/core/Lang.test.js +250 -0
  156. package/test/core/Process.test.js +156 -0
  157. package/test/framework/Route.test.js +239 -0
  158. package/test/framework/View/EarlyHints.test.js +282 -0
  159. package/test/scripts/check-coverage.js +132 -0
  160. package/test/server/Api.test.js +647 -0
  161. package/test/server/Client.test.js +338 -0
  162. package/test/server/DNS.test.js +2050 -0
  163. package/test/server/DNS.test.js.bak +2084 -0
  164. package/test/server/Log.test.js +73 -0
  165. package/test/server/Mail.account.test_.js +460 -0
  166. package/test/server/Mail.init.test_.js +411 -0
  167. package/test/server/Mail.test_.js +1340 -0
  168. package/test/server/SSL.test_.js +1491 -0
  169. package/test/server/Server.test.js +765 -0
  170. package/test/server/Service.test_.js +1127 -0
  171. package/test/server/Subdomain.test.js +440 -0
  172. package/test/server/Web/Firewall.test.js +175 -0
  173. package/test/server/Web.test_.js +1562 -0
  174. package/test/server/__mocks__/acme-client.js +17 -0
  175. package/test/server/__mocks__/bcrypt.js +50 -0
  176. package/test/server/__mocks__/child_process.js +389 -0
  177. package/test/server/__mocks__/crypto.js +432 -0
  178. package/test/server/__mocks__/fs.js +450 -0
  179. package/test/server/__mocks__/globalCandy.js +227 -0
  180. package/test/server/__mocks__/http-proxy.js +105 -0
  181. package/test/server/__mocks__/http.js +575 -0
  182. package/test/server/__mocks__/https.js +272 -0
  183. package/test/server/__mocks__/index.js +249 -0
  184. package/test/server/__mocks__/mail/server.js +100 -0
  185. package/test/server/__mocks__/mail/smtp.js +31 -0
  186. package/test/server/__mocks__/mailparser.js +81 -0
  187. package/test/server/__mocks__/net.js +369 -0
  188. package/test/server/__mocks__/node-forge.js +328 -0
  189. package/test/server/__mocks__/os.js +320 -0
  190. package/test/server/__mocks__/path.js +291 -0
  191. package/test/server/__mocks__/selfsigned.js +8 -0
  192. package/test/server/__mocks__/server/src/mail/server.js +100 -0
  193. package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
  194. package/test/server/__mocks__/smtp-server.js +106 -0
  195. package/test/server/__mocks__/sqlite3.js +394 -0
  196. package/test/server/__mocks__/testFactories.js +299 -0
  197. package/test/server/__mocks__/testHelpers.js +363 -0
  198. package/test/server/__mocks__/tls.js +229 -0
  199. package/watchdog/index.js +3 -0
  200. package/watchdog/src/Watchdog.js +156 -0
  201. package/web/config.json +5 -0
  202. package/web/controller/page/about.js +27 -0
  203. package/web/controller/page/index.js +34 -0
  204. package/web/package.json +18 -0
  205. package/web/public/assets/css/style.css +1835 -0
  206. package/web/public/assets/js/app.js +96 -0
  207. package/web/route/www.js +19 -0
  208. package/web/skeleton/main.html +22 -0
  209. package/web/view/content/about.html +65 -0
  210. package/web/view/content/home.html +205 -0
  211. package/web/view/footer/main.html +11 -0
  212. package/web/view/head/main.html +5 -0
  213. package/web/view/header/main.html +14 -0
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Shared test utilities for common assertions and setup patterns
3
+ * Provides helper functions to reduce boilerplate in server tests
4
+ */
5
+
6
+ const {mockCandy, mockLangGet} = require('./globalCandy')
7
+
8
+ /**
9
+ * Sets up the global Candy mock and __ function for tests
10
+ * Should be called in beforeEach or beforeAll
11
+ */
12
+ const setupGlobalMocks = () => {
13
+ // Set up global Candy mock
14
+ global.Candy = mockCandy
15
+
16
+ // Set up global __ function mock
17
+ global.__ = mockLangGet
18
+
19
+ // Clear all previous mock calls
20
+ mockCandy.clearMocks()
21
+ mockLangGet.mockClear()
22
+ }
23
+
24
+ /**
25
+ * Cleans up global mocks after tests
26
+ * Should be called in afterEach or afterAll
27
+ */
28
+ const cleanupGlobalMocks = () => {
29
+ mockCandy.resetMocks()
30
+ mockLangGet.mockClear()
31
+
32
+ // Reset global references
33
+ delete global.Candy
34
+ delete global.__
35
+ }
36
+
37
+ /**
38
+ * Creates a mock timer that can be controlled in tests
39
+ */
40
+ const createMockTimer = () => {
41
+ const timer = {
42
+ id: Math.floor(Math.random() * 1000),
43
+ callback: null,
44
+ interval: 0,
45
+ active: false,
46
+ unref: jest.fn(() => timer)
47
+ }
48
+
49
+ return timer
50
+ }
51
+
52
+ /**
53
+ * Helper to mock setTimeout and setInterval
54
+ */
55
+ const mockTimers = () => {
56
+ const timers = new Map()
57
+ let timerId = 1
58
+
59
+ const mockSetTimeout = jest.fn((callback, delay) => {
60
+ const id = timerId++
61
+ timers.set(id, {callback, delay, type: 'timeout'})
62
+ return id
63
+ })
64
+
65
+ const mockSetInterval = jest.fn((callback, delay) => {
66
+ const id = timerId++
67
+ const timer = {callback, delay, type: 'interval', unref: jest.fn()}
68
+ timers.set(id, timer)
69
+ return timer
70
+ })
71
+
72
+ const mockClearTimeout = jest.fn(id => {
73
+ timers.delete(id)
74
+ })
75
+
76
+ const mockClearInterval = jest.fn(id => {
77
+ timers.delete(id)
78
+ })
79
+
80
+ // Helper to execute timer callbacks
81
+ const executeTimer = id => {
82
+ const timer = timers.get(id)
83
+ if (timer && timer.callback) {
84
+ timer.callback()
85
+ }
86
+ }
87
+
88
+ // Helper to execute all timers
89
+ const executeAllTimers = () => {
90
+ timers.forEach((timer, id) => {
91
+ if (timer.callback) {
92
+ timer.callback()
93
+ }
94
+ })
95
+ }
96
+
97
+ return {
98
+ setTimeout: mockSetTimeout,
99
+ setInterval: mockSetInterval,
100
+ clearTimeout: mockClearTimeout,
101
+ clearInterval: mockClearInterval,
102
+ executeTimer,
103
+ executeAllTimers,
104
+ getTimers: () => timers
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Helper to create a mock EventEmitter
110
+ */
111
+ const createMockEventEmitter = () => {
112
+ const listeners = new Map()
113
+
114
+ const emitter = {
115
+ on: jest.fn((event, listener) => {
116
+ if (!listeners.has(event)) {
117
+ listeners.set(event, [])
118
+ }
119
+ listeners.get(event).push(listener)
120
+ return emitter
121
+ }),
122
+
123
+ once: jest.fn((event, listener) => {
124
+ const onceWrapper = (...args) => {
125
+ listener(...args)
126
+ emitter.removeListener(event, onceWrapper)
127
+ }
128
+ return emitter.on(event, onceWrapper)
129
+ }),
130
+
131
+ emit: jest.fn((event, ...args) => {
132
+ const eventListeners = listeners.get(event)
133
+ if (eventListeners) {
134
+ eventListeners.forEach(listener => listener(...args))
135
+ return true
136
+ }
137
+ return false
138
+ }),
139
+
140
+ removeListener: jest.fn((event, listener) => {
141
+ const eventListeners = listeners.get(event)
142
+ if (eventListeners) {
143
+ const index = eventListeners.indexOf(listener)
144
+ if (index > -1) {
145
+ eventListeners.splice(index, 1)
146
+ }
147
+ }
148
+ return emitter
149
+ }),
150
+
151
+ removeAllListeners: jest.fn(event => {
152
+ if (event) {
153
+ listeners.delete(event)
154
+ } else {
155
+ listeners.clear()
156
+ }
157
+ return emitter
158
+ }),
159
+
160
+ listenerCount: jest.fn(event => {
161
+ const eventListeners = listeners.get(event)
162
+ return eventListeners ? eventListeners.length : 0
163
+ }),
164
+
165
+ // Helper methods for testing
166
+ getListeners: event => listeners.get(event) || [],
167
+ getAllListeners: () => listeners
168
+ }
169
+
170
+ return emitter
171
+ }
172
+
173
+ /**
174
+ * Helper to wait for async operations in tests
175
+ */
176
+ const waitFor = (ms = 0) => {
177
+ return new Promise(resolve => setTimeout(resolve, ms))
178
+ }
179
+
180
+ /**
181
+ * Helper to wait for a condition to be true
182
+ */
183
+ const waitForCondition = async (condition, timeout = 1000, interval = 10) => {
184
+ const start = Date.now()
185
+
186
+ while (Date.now() - start < timeout) {
187
+ if (await condition()) {
188
+ return true
189
+ }
190
+ await waitFor(interval)
191
+ }
192
+
193
+ throw new Error(`Condition not met within ${timeout}ms`)
194
+ }
195
+
196
+ /**
197
+ * Helper to capture console output during tests
198
+ */
199
+ const captureConsole = () => {
200
+ const originalLog = console.log
201
+ const originalError = console.error
202
+ const originalWarn = console.warn
203
+ const originalInfo = console.info
204
+
205
+ const logs = []
206
+ const errors = []
207
+ const warnings = []
208
+ const infos = []
209
+
210
+ console.log = jest.fn((...args) => {
211
+ logs.push(args.join(' '))
212
+ })
213
+
214
+ console.error = jest.fn((...args) => {
215
+ errors.push(args.join(' '))
216
+ })
217
+
218
+ console.warn = jest.fn((...args) => {
219
+ warnings.push(args.join(' '))
220
+ })
221
+
222
+ console.info = jest.fn((...args) => {
223
+ infos.push(args.join(' '))
224
+ })
225
+
226
+ const restore = () => {
227
+ console.log = originalLog
228
+ console.error = originalError
229
+ console.warn = originalWarn
230
+ console.info = originalInfo
231
+ }
232
+
233
+ return {
234
+ logs,
235
+ errors,
236
+ warnings,
237
+ infos,
238
+ restore
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Helper to create a mock stream
244
+ */
245
+ const createMockStream = (readable = true, writable = true) => {
246
+ const stream = createMockEventEmitter()
247
+
248
+ Object.assign(stream, {
249
+ readable,
250
+ writable,
251
+ destroyed: false,
252
+
253
+ read: jest.fn(),
254
+ write: jest.fn((chunk, encoding, callback) => {
255
+ if (typeof encoding === 'function') {
256
+ callback = encoding
257
+ }
258
+ if (callback) callback()
259
+ return true
260
+ }),
261
+ end: jest.fn((chunk, encoding, callback) => {
262
+ if (chunk) stream.write(chunk, encoding)
263
+ if (typeof encoding === 'function') {
264
+ callback = encoding
265
+ }
266
+ if (typeof chunk === 'function') {
267
+ callback = chunk
268
+ }
269
+ if (callback) callback()
270
+ stream.emit('end')
271
+ }),
272
+ destroy: jest.fn(error => {
273
+ stream.destroyed = true
274
+ if (error) {
275
+ stream.emit('error', error)
276
+ }
277
+ stream.emit('close')
278
+ }),
279
+ pipe: jest.fn(destination => {
280
+ return destination
281
+ })
282
+ })
283
+
284
+ return stream
285
+ }
286
+
287
+ /**
288
+ * Helper assertions for common test patterns
289
+ */
290
+ const assertions = {
291
+ /**
292
+ * Assert that a mock function was called with specific arguments
293
+ */
294
+ toHaveBeenCalledWithArgs: (mockFn, ...expectedArgs) => {
295
+ expect(mockFn).toHaveBeenCalledWith(...expectedArgs)
296
+ },
297
+
298
+ /**
299
+ * Assert that an object has all required properties
300
+ */
301
+ toHaveRequiredProperties: (obj, properties) => {
302
+ properties.forEach(prop => {
303
+ expect(obj).toHaveProperty(prop)
304
+ })
305
+ },
306
+
307
+ /**
308
+ * Assert that a function throws a specific error
309
+ */
310
+ toThrowErrorWithMessage: (fn, expectedMessage) => {
311
+ expect(fn).toThrow(expectedMessage)
312
+ },
313
+
314
+ /**
315
+ * Assert that an async function resolves to a specific value
316
+ */
317
+ toResolveWith: async (promise, expectedValue) => {
318
+ const result = await promise
319
+ expect(result).toEqual(expectedValue)
320
+ },
321
+
322
+ /**
323
+ * Assert that an async function rejects with a specific error
324
+ */
325
+ toRejectWith: async (promise, expectedError) => {
326
+ await expect(promise).rejects.toThrow(expectedError)
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Helper to create a test suite with common setup/teardown
332
+ */
333
+ const createTestSuite = (suiteName, setupFn, teardownFn) => {
334
+ return testFn => {
335
+ describe(suiteName, () => {
336
+ beforeEach(() => {
337
+ setupGlobalMocks()
338
+ if (setupFn) setupFn()
339
+ })
340
+
341
+ afterEach(() => {
342
+ if (teardownFn) teardownFn()
343
+ cleanupGlobalMocks()
344
+ })
345
+
346
+ testFn()
347
+ })
348
+ }
349
+ }
350
+
351
+ module.exports = {
352
+ setupGlobalMocks,
353
+ cleanupGlobalMocks,
354
+ createMockTimer,
355
+ mockTimers,
356
+ createMockEventEmitter,
357
+ waitFor,
358
+ waitForCondition,
359
+ captureConsole,
360
+ createMockStream,
361
+ assertions,
362
+ createTestSuite
363
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Mock implementation of tls module for server tests
3
+ */
4
+
5
+ const {createMockEventEmitter, createMockStream} = require('./testHelpers')
6
+
7
+ const createMockSecureContext = (options = {}) => {
8
+ return {
9
+ context: 'mock-secure-context',
10
+ options: options,
11
+ // Mock methods that might be called on the context
12
+ setCert: jest.fn(),
13
+ setKey: jest.fn(),
14
+ addCACert: jest.fn(),
15
+ addCRL: jest.fn(),
16
+ addRootCerts: jest.fn()
17
+ }
18
+ }
19
+
20
+ const createMockTLSSocket = (socket, options = {}) => {
21
+ const tlsSocket = Object.assign(createMockStream(true, true), createMockEventEmitter())
22
+
23
+ Object.assign(tlsSocket, {
24
+ // TLS-specific properties
25
+ authorized: true,
26
+ authorizationError: null,
27
+ encrypted: true,
28
+
29
+ // Certificate information
30
+ getPeerCertificate: jest.fn((detailed = false) => {
31
+ return {
32
+ subject: {
33
+ CN: 'example.com',
34
+ O: 'Test Organization',
35
+ C: 'US'
36
+ },
37
+ issuer: {
38
+ CN: 'Test CA',
39
+ O: 'Test CA Organization',
40
+ C: 'US'
41
+ },
42
+ valid_from: 'Jan 1 00:00:00 2023 GMT',
43
+ valid_to: 'Jan 1 00:00:00 2024 GMT',
44
+ fingerprint: 'AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD',
45
+ serialNumber: '01',
46
+ raw: Buffer.from('mock-certificate-data')
47
+ }
48
+ }),
49
+
50
+ getCipher: jest.fn(() => {
51
+ return {
52
+ name: 'ECDHE-RSA-AES128-GCM-SHA256',
53
+ version: 'TLSv1.2'
54
+ }
55
+ }),
56
+
57
+ getProtocol: jest.fn(() => 'TLSv1.2'),
58
+
59
+ getSession: jest.fn(() => Buffer.from('mock-session-data')),
60
+
61
+ getTLSTicket: jest.fn(() => Buffer.from('mock-tls-ticket')),
62
+
63
+ renegotiate: jest.fn((options, callback) => {
64
+ if (typeof options === 'function') {
65
+ callback = options
66
+ options = {}
67
+ }
68
+ setTimeout(() => {
69
+ if (callback) callback(null)
70
+ }, 0)
71
+ return true
72
+ }),
73
+
74
+ setMaxSendFragment: jest.fn(size => {
75
+ return true
76
+ }),
77
+
78
+ // Underlying socket
79
+ socket: socket || createMockStream(true, true)
80
+ })
81
+
82
+ return tlsSocket
83
+ }
84
+
85
+ const createMockServer = (options = {}, secureConnectionListener) => {
86
+ if (typeof options === 'function') {
87
+ secureConnectionListener = options
88
+ options = {}
89
+ }
90
+
91
+ const server = Object.assign(createMockEventEmitter(), {
92
+ listening: false,
93
+ maxConnections: null,
94
+ connections: 0,
95
+
96
+ listen: jest.fn((port, hostname, backlog, callback) => {
97
+ // Handle various argument combinations
98
+ if (typeof port === 'function') {
99
+ callback = port
100
+ port = 0
101
+ } else if (typeof hostname === 'function') {
102
+ callback = hostname
103
+ hostname = undefined
104
+ } else if (typeof backlog === 'function') {
105
+ callback = backlog
106
+ backlog = undefined
107
+ }
108
+
109
+ server.listening = true
110
+
111
+ setTimeout(() => {
112
+ server.emit('listening')
113
+ if (callback) callback()
114
+ }, 0)
115
+
116
+ return server
117
+ }),
118
+
119
+ close: jest.fn(callback => {
120
+ server.listening = false
121
+
122
+ setTimeout(() => {
123
+ server.emit('close')
124
+ if (callback) callback()
125
+ }, 0)
126
+
127
+ return server
128
+ }),
129
+
130
+ address: jest.fn(() => {
131
+ return {
132
+ port: 443,
133
+ family: 'IPv4',
134
+ address: '0.0.0.0'
135
+ }
136
+ }),
137
+
138
+ getConnections: jest.fn(callback => {
139
+ setTimeout(() => {
140
+ callback(null, server.connections)
141
+ }, 0)
142
+ }),
143
+
144
+ // Test helper methods
145
+ simulateConnection: socket => {
146
+ const tlsSocket = createMockTLSSocket(socket)
147
+ server.connections++
148
+
149
+ if (secureConnectionListener) {
150
+ secureConnectionListener(tlsSocket)
151
+ }
152
+
153
+ server.emit('secureConnection', tlsSocket)
154
+ return tlsSocket
155
+ },
156
+
157
+ simulateError: error => {
158
+ server.emit('error', error)
159
+ }
160
+ })
161
+
162
+ return server
163
+ }
164
+
165
+ const tls = {
166
+ // Create secure context
167
+ createSecureContext: jest.fn((options = {}) => {
168
+ return createMockSecureContext(options)
169
+ }),
170
+
171
+ // Create TLS server
172
+ createServer: jest.fn((options, secureConnectionListener) => {
173
+ return createMockServer(options, secureConnectionListener)
174
+ }),
175
+
176
+ // Create TLS connection
177
+ connect: jest.fn((port, host, options, callback) => {
178
+ // Handle various argument combinations
179
+ if (typeof port === 'object') {
180
+ options = port
181
+ port = options.port
182
+ host = options.host
183
+ } else if (typeof host === 'object') {
184
+ callback = options
185
+ options = host
186
+ host = 'localhost'
187
+ } else if (typeof host === 'function') {
188
+ callback = host
189
+ host = 'localhost'
190
+ options = {}
191
+ } else if (typeof options === 'function') {
192
+ callback = options
193
+ options = {}
194
+ }
195
+
196
+ const socket = createMockTLSSocket()
197
+
198
+ setTimeout(() => {
199
+ socket.emit('connect')
200
+ socket.emit('secureConnect')
201
+ if (callback) callback()
202
+ }, 0)
203
+
204
+ return socket
205
+ }),
206
+
207
+ // Get ciphers
208
+ getCiphers: jest.fn(() => {
209
+ return [
210
+ 'ECDHE-RSA-AES128-GCM-SHA256',
211
+ 'ECDHE-RSA-AES256-GCM-SHA384',
212
+ 'ECDHE-RSA-AES128-SHA256',
213
+ 'ECDHE-RSA-AES256-SHA384',
214
+ 'AES128-GCM-SHA256',
215
+ 'AES256-GCM-SHA384'
216
+ ]
217
+ }),
218
+
219
+ // Constants
220
+ CLIENT_RENEG_LIMIT: 3,
221
+ CLIENT_RENEG_WINDOW: 600,
222
+
223
+ // Test helpers
224
+ __createMockSecureContext: createMockSecureContext,
225
+ __createMockTLSSocket: createMockTLSSocket,
226
+ __createMockServer: createMockServer
227
+ }
228
+
229
+ module.exports = tls
@@ -0,0 +1,3 @@
1
+ require('../core/Candy.js')
2
+
3
+ Candy.watchdog('Watchdog')