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.
- package/.editorconfig +21 -0
- package/.github/workflows/auto-pr-description.yml +49 -0
- package/.github/workflows/release.yml +32 -0
- package/.github/workflows/test-coverage.yml +58 -0
- package/.husky/pre-commit +2 -0
- package/.kiro/steering/code-style.md +56 -0
- package/.kiro/steering/product.md +20 -0
- package/.kiro/steering/structure.md +77 -0
- package/.kiro/steering/tech.md +87 -0
- package/.prettierrc +10 -0
- package/.releaserc.js +134 -0
- package/AGENTS.md +84 -0
- package/CHANGELOG.md +181 -0
- package/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +63 -0
- package/LICENSE +661 -0
- package/README.md +57 -0
- package/SECURITY.md +26 -0
- package/bin/candy +10 -0
- package/bin/candypack +10 -0
- package/cli/index.js +3 -0
- package/cli/src/Cli.js +348 -0
- package/cli/src/Connector.js +93 -0
- package/cli/src/Monitor.js +416 -0
- package/core/Candy.js +87 -0
- package/core/Commands.js +239 -0
- package/core/Config.js +1094 -0
- package/core/Lang.js +52 -0
- package/core/Log.js +43 -0
- package/core/Process.js +26 -0
- package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
- package/docs/backend/01-overview/03-development-server.md +79 -0
- package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
- package/docs/backend/03-config/00-configuration-overview.md +214 -0
- package/docs/backend/03-config/01-database-connection.md +60 -0
- package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
- package/docs/backend/03-config/03-request-timeout.md +11 -0
- package/docs/backend/03-config/04-environment-variables.md +227 -0
- package/docs/backend/03-config/05-early-hints.md +352 -0
- package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
- package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
- package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
- package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
- package/docs/backend/04-routing/05-advanced-routing.md +14 -0
- package/docs/backend/04-routing/06-error-pages.md +101 -0
- package/docs/backend/04-routing/07-cron-jobs.md +149 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
- package/docs/backend/05-controllers/03-controller-classes.md +93 -0
- package/docs/backend/05-forms/01-custom-forms.md +395 -0
- package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
- package/docs/backend/07-views/01-the-view-directory.md +73 -0
- package/docs/backend/07-views/02-rendering-a-view.md +179 -0
- package/docs/backend/07-views/03-template-syntax.md +181 -0
- package/docs/backend/07-views/03-variables.md +328 -0
- package/docs/backend/07-views/04-request-data.md +231 -0
- package/docs/backend/07-views/05-conditionals.md +290 -0
- package/docs/backend/07-views/06-loops.md +353 -0
- package/docs/backend/07-views/07-translations.md +358 -0
- package/docs/backend/07-views/08-backend-javascript.md +398 -0
- package/docs/backend/07-views/09-comments.md +297 -0
- package/docs/backend/08-database/01-database-connection.md +99 -0
- package/docs/backend/08-database/02-using-mysql.md +322 -0
- package/docs/backend/09-validation/01-the-validator-service.md +424 -0
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
- package/docs/backend/10-authentication/03-register.md +134 -0
- package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
- package/docs/backend/10-authentication/05-session-management.md +159 -0
- package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
- package/docs/backend/11-mail/01-the-mail-service.md +42 -0
- package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
- package/docs/backend/13-utilities/01-candy-var.md +504 -0
- package/docs/frontend/01-overview/01-introduction.md +146 -0
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
- package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
- package/docs/frontend/03-forms/01-form-handling.md +420 -0
- package/docs/frontend/04-api-requests/01-get-post.md +443 -0
- package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
- package/docs/index.json +452 -0
- package/docs/server/01-installation/01-quick-install.md +19 -0
- package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
- package/docs/server/02-get-started/01-core-concepts.md +7 -0
- package/docs/server/02-get-started/02-basic-commands.md +57 -0
- package/docs/server/02-get-started/03-cli-reference.md +276 -0
- package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
- package/docs/server/03-service/01-start-a-new-service.md +57 -0
- package/docs/server/03-service/02-delete-a-service.md +48 -0
- package/docs/server/04-web/01-create-a-website.md +36 -0
- package/docs/server/04-web/02-list-websites.md +9 -0
- package/docs/server/04-web/03-delete-a-website.md +29 -0
- package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
- package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
- package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
- package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
- package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
- package/docs/server/07-mail/04-change-account-password.md +23 -0
- package/eslint.config.mjs +120 -0
- package/framework/index.js +4 -0
- package/framework/src/Auth.js +309 -0
- package/framework/src/Candy.js +81 -0
- package/framework/src/Config.js +79 -0
- package/framework/src/Env.js +60 -0
- package/framework/src/Lang.js +57 -0
- package/framework/src/Mail.js +83 -0
- package/framework/src/Mysql.js +575 -0
- package/framework/src/Request.js +301 -0
- package/framework/src/Route/Cron.js +128 -0
- package/framework/src/Route/Internal.js +439 -0
- package/framework/src/Route.js +455 -0
- package/framework/src/Server.js +15 -0
- package/framework/src/Stream.js +163 -0
- package/framework/src/Token.js +37 -0
- package/framework/src/Validator.js +271 -0
- package/framework/src/Var.js +211 -0
- package/framework/src/View/EarlyHints.js +190 -0
- package/framework/src/View/Form.js +600 -0
- package/framework/src/View.js +513 -0
- package/framework/web/candy.js +838 -0
- package/jest.config.js +22 -0
- package/locale/de-DE.json +80 -0
- package/locale/en-US.json +79 -0
- package/locale/es-ES.json +80 -0
- package/locale/fr-FR.json +80 -0
- package/locale/pt-BR.json +80 -0
- package/locale/ru-RU.json +80 -0
- package/locale/tr-TR.json +85 -0
- package/locale/zh-CN.json +80 -0
- package/package.json +86 -0
- package/server/index.js +5 -0
- package/server/src/Api.js +88 -0
- package/server/src/DNS.js +940 -0
- package/server/src/Hub.js +535 -0
- package/server/src/Mail.js +571 -0
- package/server/src/SSL.js +180 -0
- package/server/src/Server.js +27 -0
- package/server/src/Service.js +248 -0
- package/server/src/Subdomain.js +64 -0
- package/server/src/Web/Firewall.js +170 -0
- package/server/src/Web/Proxy.js +134 -0
- package/server/src/Web.js +451 -0
- package/server/src/mail/imap.js +1091 -0
- package/server/src/mail/server.js +32 -0
- package/server/src/mail/smtp.js +786 -0
- package/test/cli/Cli.test.js +36 -0
- package/test/core/Candy.test.js +234 -0
- package/test/core/Commands.test.js +538 -0
- package/test/core/Config.test.js +1435 -0
- package/test/core/Lang.test.js +250 -0
- package/test/core/Process.test.js +156 -0
- package/test/framework/Route.test.js +239 -0
- package/test/framework/View/EarlyHints.test.js +282 -0
- package/test/scripts/check-coverage.js +132 -0
- package/test/server/Api.test.js +647 -0
- package/test/server/Client.test.js +338 -0
- package/test/server/DNS.test.js +2050 -0
- package/test/server/DNS.test.js.bak +2084 -0
- package/test/server/Log.test.js +73 -0
- package/test/server/Mail.account.test_.js +460 -0
- package/test/server/Mail.init.test_.js +411 -0
- package/test/server/Mail.test_.js +1340 -0
- package/test/server/SSL.test_.js +1491 -0
- package/test/server/Server.test.js +765 -0
- package/test/server/Service.test_.js +1127 -0
- package/test/server/Subdomain.test.js +440 -0
- package/test/server/Web/Firewall.test.js +175 -0
- package/test/server/Web.test_.js +1562 -0
- package/test/server/__mocks__/acme-client.js +17 -0
- package/test/server/__mocks__/bcrypt.js +50 -0
- package/test/server/__mocks__/child_process.js +389 -0
- package/test/server/__mocks__/crypto.js +432 -0
- package/test/server/__mocks__/fs.js +450 -0
- package/test/server/__mocks__/globalCandy.js +227 -0
- package/test/server/__mocks__/http-proxy.js +105 -0
- package/test/server/__mocks__/http.js +575 -0
- package/test/server/__mocks__/https.js +272 -0
- package/test/server/__mocks__/index.js +249 -0
- package/test/server/__mocks__/mail/server.js +100 -0
- package/test/server/__mocks__/mail/smtp.js +31 -0
- package/test/server/__mocks__/mailparser.js +81 -0
- package/test/server/__mocks__/net.js +369 -0
- package/test/server/__mocks__/node-forge.js +328 -0
- package/test/server/__mocks__/os.js +320 -0
- package/test/server/__mocks__/path.js +291 -0
- package/test/server/__mocks__/selfsigned.js +8 -0
- package/test/server/__mocks__/server/src/mail/server.js +100 -0
- package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
- package/test/server/__mocks__/smtp-server.js +106 -0
- package/test/server/__mocks__/sqlite3.js +394 -0
- package/test/server/__mocks__/testFactories.js +299 -0
- package/test/server/__mocks__/testHelpers.js +363 -0
- package/test/server/__mocks__/tls.js +229 -0
- package/watchdog/index.js +3 -0
- package/watchdog/src/Watchdog.js +156 -0
- package/web/config.json +5 -0
- package/web/controller/page/about.js +27 -0
- package/web/controller/page/index.js +34 -0
- package/web/package.json +18 -0
- package/web/public/assets/css/style.css +1835 -0
- package/web/public/assets/js/app.js +96 -0
- package/web/route/www.js +19 -0
- package/web/skeleton/main.html +22 -0
- package/web/view/content/about.html +65 -0
- package/web/view/content/home.html +205 -0
- package/web/view/footer/main.html +11 -0
- package/web/view/head/main.html +5 -0
- package/web/view/header/main.html +14 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of the https module for server tests
|
|
3
|
+
* Extends the http mock with SSL/TLS specific functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const http = require('./http')
|
|
7
|
+
const {createMockEventEmitter} = require('./testHelpers')
|
|
8
|
+
|
|
9
|
+
// Track active HTTPS servers
|
|
10
|
+
const activeHttpsServers = new Map()
|
|
11
|
+
let nextHttpsServerId = 1
|
|
12
|
+
|
|
13
|
+
const createMockHttpsServer = (options = {}) => {
|
|
14
|
+
const server = createMockEventEmitter()
|
|
15
|
+
const serverId = nextHttpsServerId++
|
|
16
|
+
|
|
17
|
+
// Inherit from HTTP server mock
|
|
18
|
+
const httpServer = http.createServer()
|
|
19
|
+
Object.setPrototypeOf(server, httpServer)
|
|
20
|
+
Object.assign(server, httpServer)
|
|
21
|
+
|
|
22
|
+
// Override and add HTTPS-specific properties
|
|
23
|
+
Object.assign(server, {
|
|
24
|
+
// SSL/TLS properties
|
|
25
|
+
cert: options.cert || null,
|
|
26
|
+
key: options.key || null,
|
|
27
|
+
ca: options.ca || null,
|
|
28
|
+
ciphers: options.ciphers || null,
|
|
29
|
+
secureProtocol: options.secureProtocol || 'TLSv1_2_method',
|
|
30
|
+
|
|
31
|
+
// SNI callback
|
|
32
|
+
SNICallback: options.SNICallback || null,
|
|
33
|
+
|
|
34
|
+
// Override listen to handle HTTPS port default
|
|
35
|
+
listen: jest.fn((port, hostname, backlog, callback) => {
|
|
36
|
+
// Handle different argument patterns
|
|
37
|
+
if (typeof port === 'function') {
|
|
38
|
+
callback = port
|
|
39
|
+
port = 443
|
|
40
|
+
} else if (typeof hostname === 'function') {
|
|
41
|
+
callback = hostname
|
|
42
|
+
hostname = undefined
|
|
43
|
+
} else if (typeof backlog === 'function') {
|
|
44
|
+
callback = backlog
|
|
45
|
+
backlog = undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
server._port = port || 443
|
|
49
|
+
server._hostname = hostname || '0.0.0.0'
|
|
50
|
+
server.listening = true
|
|
51
|
+
|
|
52
|
+
if (callback) {
|
|
53
|
+
server.once('listening', callback)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
server.emit('listening')
|
|
58
|
+
}, 0)
|
|
59
|
+
|
|
60
|
+
return server
|
|
61
|
+
}),
|
|
62
|
+
|
|
63
|
+
// SSL/TLS specific methods
|
|
64
|
+
addContext: jest.fn((hostname, context) => {
|
|
65
|
+
if (!server._contexts) {
|
|
66
|
+
server._contexts = new Map()
|
|
67
|
+
}
|
|
68
|
+
server._contexts.set(hostname, context)
|
|
69
|
+
return server
|
|
70
|
+
}),
|
|
71
|
+
|
|
72
|
+
// Test helpers
|
|
73
|
+
__simulateSecureConnection: (socket, options = {}) => {
|
|
74
|
+
const secureSocket = Object.assign(socket, {
|
|
75
|
+
encrypted: true,
|
|
76
|
+
authorized: options.authorized !== false,
|
|
77
|
+
authorizationError: options.authorizationError || null,
|
|
78
|
+
getPeerCertificate: jest.fn(() => ({
|
|
79
|
+
subject: {CN: options.commonName || 'localhost'},
|
|
80
|
+
issuer: {CN: 'Test CA'},
|
|
81
|
+
valid_from: new Date().toISOString(),
|
|
82
|
+
valid_to: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
|
|
83
|
+
fingerprint: 'AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD'
|
|
84
|
+
})),
|
|
85
|
+
getCipher: jest.fn(() => ({
|
|
86
|
+
name: 'ECDHE-RSA-AES128-GCM-SHA256',
|
|
87
|
+
version: 'TLSv1.2'
|
|
88
|
+
})),
|
|
89
|
+
getProtocol: jest.fn(() => 'TLSv1.2'),
|
|
90
|
+
getSession: jest.fn(() => Buffer.alloc(0))
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
server.emit('secureConnection', secureSocket)
|
|
95
|
+
}, 0)
|
|
96
|
+
|
|
97
|
+
return secureSocket
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
__simulateClientError: (error, socket) => {
|
|
101
|
+
server.emit('clientError', error, socket)
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
__getContexts: () => server._contexts || new Map()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
activeHttpsServers.set(serverId, server)
|
|
108
|
+
return server
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const createMockHttpsClientRequest = (options = {}) => {
|
|
112
|
+
const request = createMockEventEmitter()
|
|
113
|
+
|
|
114
|
+
// Inherit from HTTP client request mock
|
|
115
|
+
const httpRequest = http.request(options)
|
|
116
|
+
Object.setPrototypeOf(request, httpRequest)
|
|
117
|
+
Object.assign(request, httpRequest)
|
|
118
|
+
|
|
119
|
+
// Add HTTPS-specific properties
|
|
120
|
+
Object.assign(request, {
|
|
121
|
+
// SSL/TLS properties
|
|
122
|
+
rejectUnauthorized: options.rejectUnauthorized !== false,
|
|
123
|
+
servername: options.servername || options.hostname,
|
|
124
|
+
|
|
125
|
+
// Override end to simulate SSL handshake
|
|
126
|
+
end: jest.fn((data, encoding, callback) => {
|
|
127
|
+
if (data) {
|
|
128
|
+
request.write(data, encoding)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (typeof encoding === 'function') {
|
|
132
|
+
callback = encoding
|
|
133
|
+
}
|
|
134
|
+
if (typeof data === 'function') {
|
|
135
|
+
callback = data
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
request.finished = true
|
|
139
|
+
request.writable = false
|
|
140
|
+
request.writableEnded = true
|
|
141
|
+
|
|
142
|
+
// Simulate SSL handshake and response
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
// Emit secureConnect event
|
|
145
|
+
request.emit('secureConnect')
|
|
146
|
+
|
|
147
|
+
const response = http.IncomingMessage()
|
|
148
|
+
Object.assign(response, {
|
|
149
|
+
statusCode: 200,
|
|
150
|
+
statusMessage: 'OK',
|
|
151
|
+
headers: {'content-type': 'text/plain'},
|
|
152
|
+
connection: {
|
|
153
|
+
encrypted: true,
|
|
154
|
+
authorized: true,
|
|
155
|
+
getPeerCertificate: jest.fn(() => ({
|
|
156
|
+
subject: {CN: options.servername || 'localhost'},
|
|
157
|
+
issuer: {CN: 'Test CA'},
|
|
158
|
+
valid_from: new Date().toISOString(),
|
|
159
|
+
valid_to: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
|
|
160
|
+
}))
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
request.emit('response', response)
|
|
165
|
+
|
|
166
|
+
// Simulate response data
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
response.__simulateData('Mock HTTPS response data')
|
|
169
|
+
response.__simulateEnd()
|
|
170
|
+
}, 0)
|
|
171
|
+
|
|
172
|
+
if (callback) callback()
|
|
173
|
+
}, 0)
|
|
174
|
+
|
|
175
|
+
return request
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return request
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const https = {
|
|
183
|
+
// Server creation
|
|
184
|
+
createServer: jest.fn((options, requestListener) => {
|
|
185
|
+
if (typeof options === 'function') {
|
|
186
|
+
requestListener = options
|
|
187
|
+
options = {}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const server = createMockHttpsServer(options)
|
|
191
|
+
|
|
192
|
+
if (requestListener) {
|
|
193
|
+
server.on('request', requestListener)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return server
|
|
197
|
+
}),
|
|
198
|
+
|
|
199
|
+
// Client requests
|
|
200
|
+
request: jest.fn((options, callback) => {
|
|
201
|
+
if (typeof options === 'string') {
|
|
202
|
+
const url = new URL(options)
|
|
203
|
+
options = {
|
|
204
|
+
hostname: url.hostname,
|
|
205
|
+
port: url.port || 443,
|
|
206
|
+
path: url.pathname + url.search,
|
|
207
|
+
method: 'GET'
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const req = createMockHttpsClientRequest(options)
|
|
212
|
+
|
|
213
|
+
if (callback) {
|
|
214
|
+
req.once('response', callback)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return req
|
|
218
|
+
}),
|
|
219
|
+
|
|
220
|
+
get: jest.fn((options, callback) => {
|
|
221
|
+
const req = https.request(options, callback)
|
|
222
|
+
req.end()
|
|
223
|
+
return req
|
|
224
|
+
}),
|
|
225
|
+
|
|
226
|
+
// Classes
|
|
227
|
+
Server: jest.fn(function (options, requestListener) {
|
|
228
|
+
return createMockHttpsServer(options, requestListener)
|
|
229
|
+
}),
|
|
230
|
+
|
|
231
|
+
Agent: jest.fn(function (options = {}) {
|
|
232
|
+
return {
|
|
233
|
+
maxSockets: options.maxSockets || Infinity,
|
|
234
|
+
maxFreeSockets: options.maxFreeSockets || 256,
|
|
235
|
+
timeout: options.timeout || 0,
|
|
236
|
+
keepAlive: options.keepAlive || false,
|
|
237
|
+
keepAliveMsecs: options.keepAliveMsecs || 1000,
|
|
238
|
+
maxCachedSessions: options.maxCachedSessions || 100,
|
|
239
|
+
servername: options.servername,
|
|
240
|
+
|
|
241
|
+
// Methods
|
|
242
|
+
createConnection: jest.fn(),
|
|
243
|
+
keepSocketAlive: jest.fn(),
|
|
244
|
+
reuseSocket: jest.fn(),
|
|
245
|
+
destroy: jest.fn()
|
|
246
|
+
}
|
|
247
|
+
}),
|
|
248
|
+
|
|
249
|
+
// Global agent
|
|
250
|
+
globalAgent: {
|
|
251
|
+
maxSockets: Infinity,
|
|
252
|
+
maxFreeSockets: 256,
|
|
253
|
+
timeout: 0,
|
|
254
|
+
keepAlive: false,
|
|
255
|
+
keepAliveMsecs: 1000,
|
|
256
|
+
maxCachedSessions: 100
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
// Inherit HTTP constants and methods
|
|
260
|
+
METHODS: http.METHODS,
|
|
261
|
+
STATUS_CODES: http.STATUS_CODES,
|
|
262
|
+
|
|
263
|
+
// Test helpers
|
|
264
|
+
__getActiveServers: () => new Map(activeHttpsServers),
|
|
265
|
+
__clearAll: () => {
|
|
266
|
+
activeHttpsServers.clear()
|
|
267
|
+
nextHttpsServerId = 1
|
|
268
|
+
http.__clearAll()
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = https
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index file for all server test mocks
|
|
3
|
+
* Provides easy access to all mock modules and utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import all mock modules
|
|
7
|
+
const fs = require('./fs')
|
|
8
|
+
const net = require('./net')
|
|
9
|
+
const http = require('./http')
|
|
10
|
+
const https = require('./https')
|
|
11
|
+
const child_process = require('./child_process')
|
|
12
|
+
const crypto = require('./crypto')
|
|
13
|
+
const os = require('./os')
|
|
14
|
+
const path = require('./path')
|
|
15
|
+
|
|
16
|
+
// Import test utilities
|
|
17
|
+
const {mockCandy, mockLangGet, MockCandyPack} = require('./globalCandy')
|
|
18
|
+
const testFactories = require('./testFactories')
|
|
19
|
+
const testHelpers = require('./testHelpers')
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Setup all Node.js module mocks for Jest
|
|
23
|
+
* Call this in your test setup to mock all Node.js built-in modules
|
|
24
|
+
*/
|
|
25
|
+
const setupNodeMocks = () => {
|
|
26
|
+
jest.mock('fs', () => require('./fs'))
|
|
27
|
+
jest.mock('net', () => require('./net'))
|
|
28
|
+
jest.mock('http', () => require('./http'))
|
|
29
|
+
jest.mock('https', () => require('./https'))
|
|
30
|
+
jest.mock('child_process', () => require('./child_process'))
|
|
31
|
+
jest.mock('crypto', () => require('./crypto'))
|
|
32
|
+
jest.mock('os', () => require('./os'))
|
|
33
|
+
jest.mock('path', () => require('./path'))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Setup global mocks (Candy and __ function)
|
|
38
|
+
* Call this in beforeEach to set up global mocks
|
|
39
|
+
*/
|
|
40
|
+
const setupGlobalMocks = () => {
|
|
41
|
+
global.Candy = mockCandy
|
|
42
|
+
global.__ = mockLangGet
|
|
43
|
+
|
|
44
|
+
// Clear all previous mock calls
|
|
45
|
+
mockCandy.clearMocks()
|
|
46
|
+
mockLangGet.mockClear()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Cleanup all mocks
|
|
51
|
+
* Call this in afterEach to clean up mocks
|
|
52
|
+
*/
|
|
53
|
+
const cleanupAllMocks = () => {
|
|
54
|
+
// Clear global mocks
|
|
55
|
+
mockCandy.resetMocks()
|
|
56
|
+
mockLangGet.mockClear()
|
|
57
|
+
|
|
58
|
+
// Clear Node.js module mocks
|
|
59
|
+
fs.__resetFileSystem()
|
|
60
|
+
net.__clearAll()
|
|
61
|
+
http.__clearAll()
|
|
62
|
+
https.__clearAll()
|
|
63
|
+
child_process.__clearAll()
|
|
64
|
+
crypto.__resetMocks()
|
|
65
|
+
os.__resetMocks()
|
|
66
|
+
path.__resetMocks()
|
|
67
|
+
|
|
68
|
+
// Reset global references
|
|
69
|
+
delete global.Candy
|
|
70
|
+
delete global.__
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create a complete test environment with all mocks
|
|
75
|
+
* Returns an object with all mock modules and utilities
|
|
76
|
+
*/
|
|
77
|
+
const createTestEnvironment = () => {
|
|
78
|
+
setupNodeMocks()
|
|
79
|
+
setupGlobalMocks()
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
// Mock modules
|
|
83
|
+
mocks: {
|
|
84
|
+
fs,
|
|
85
|
+
net,
|
|
86
|
+
http,
|
|
87
|
+
https,
|
|
88
|
+
child_process,
|
|
89
|
+
crypto,
|
|
90
|
+
os,
|
|
91
|
+
path
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Global mocks
|
|
95
|
+
global: {
|
|
96
|
+
Candy: mockCandy,
|
|
97
|
+
__: mockLangGet
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Test utilities
|
|
101
|
+
factories: testFactories,
|
|
102
|
+
helpers: testHelpers,
|
|
103
|
+
|
|
104
|
+
// Cleanup function
|
|
105
|
+
cleanup: cleanupAllMocks
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Timer mock utilities for controlling time in tests
|
|
111
|
+
*/
|
|
112
|
+
const createTimerMocks = () => {
|
|
113
|
+
const timers = new Map()
|
|
114
|
+
let timerId = 1
|
|
115
|
+
|
|
116
|
+
const mockSetTimeout = jest.fn((callback, delay) => {
|
|
117
|
+
const id = timerId++
|
|
118
|
+
timers.set(id, {callback, delay, type: 'timeout'})
|
|
119
|
+
return id
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const mockSetInterval = jest.fn((callback, delay) => {
|
|
123
|
+
const id = timerId++
|
|
124
|
+
const timer = {
|
|
125
|
+
callback,
|
|
126
|
+
delay,
|
|
127
|
+
type: 'interval',
|
|
128
|
+
unref: jest.fn(() => timer)
|
|
129
|
+
}
|
|
130
|
+
timers.set(id, timer)
|
|
131
|
+
return timer
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const mockClearTimeout = jest.fn(id => {
|
|
135
|
+
timers.delete(id)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const mockClearInterval = jest.fn(id => {
|
|
139
|
+
timers.delete(id)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// Apply mocks to global
|
|
143
|
+
global.setTimeout = mockSetTimeout
|
|
144
|
+
global.setInterval = mockSetInterval
|
|
145
|
+
global.clearTimeout = mockClearTimeout
|
|
146
|
+
global.clearInterval = mockClearInterval
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
setTimeout: mockSetTimeout,
|
|
150
|
+
setInterval: mockSetInterval,
|
|
151
|
+
clearTimeout: mockClearTimeout,
|
|
152
|
+
clearInterval: mockClearInterval,
|
|
153
|
+
|
|
154
|
+
// Helper functions
|
|
155
|
+
executeTimer: id => {
|
|
156
|
+
const timer = timers.get(id)
|
|
157
|
+
if (timer && timer.callback) {
|
|
158
|
+
timer.callback()
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
executeAllTimers: () => {
|
|
163
|
+
timers.forEach(timer => {
|
|
164
|
+
if (timer.callback) {
|
|
165
|
+
timer.callback()
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
getTimers: () => new Map(timers),
|
|
171
|
+
|
|
172
|
+
clearAllTimers: () => {
|
|
173
|
+
timers.clear()
|
|
174
|
+
timerId = 1
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Process mock utilities for controlling process behavior
|
|
181
|
+
*/
|
|
182
|
+
const createProcessMocks = () => {
|
|
183
|
+
const originalProcess = {...process}
|
|
184
|
+
|
|
185
|
+
const mockProcess = {
|
|
186
|
+
...process,
|
|
187
|
+
exit: jest.fn(),
|
|
188
|
+
kill: jest.fn(),
|
|
189
|
+
nextTick: jest.fn(callback => {
|
|
190
|
+
setTimeout(callback, 0)
|
|
191
|
+
}),
|
|
192
|
+
env: {...process.env},
|
|
193
|
+
argv: [...process.argv],
|
|
194
|
+
cwd: jest.fn(() => '/mock/cwd'),
|
|
195
|
+
chdir: jest.fn(directory => {
|
|
196
|
+
mockProcess._cwd = directory
|
|
197
|
+
}),
|
|
198
|
+
|
|
199
|
+
// Test helpers
|
|
200
|
+
__setEnv: env => {
|
|
201
|
+
mockProcess.env = {...env}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
__setArgv: argv => {
|
|
205
|
+
mockProcess.argv = [...argv]
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
__setCwd: cwd => {
|
|
209
|
+
mockProcess.cwd.mockReturnValue(cwd)
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
__reset: () => {
|
|
213
|
+
Object.assign(mockProcess, originalProcess)
|
|
214
|
+
mockProcess.env = {...originalProcess.env}
|
|
215
|
+
mockProcess.argv = [...originalProcess.argv]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return mockProcess
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
// Mock modules
|
|
224
|
+
fs,
|
|
225
|
+
net,
|
|
226
|
+
http,
|
|
227
|
+
https,
|
|
228
|
+
child_process,
|
|
229
|
+
crypto,
|
|
230
|
+
os,
|
|
231
|
+
path,
|
|
232
|
+
|
|
233
|
+
// Global mocks
|
|
234
|
+
mockCandy,
|
|
235
|
+
mockLangGet,
|
|
236
|
+
MockCandyPack,
|
|
237
|
+
|
|
238
|
+
// Test utilities
|
|
239
|
+
testFactories,
|
|
240
|
+
testHelpers,
|
|
241
|
+
|
|
242
|
+
// Setup functions
|
|
243
|
+
setupNodeMocks,
|
|
244
|
+
setupGlobalMocks,
|
|
245
|
+
cleanupAllMocks,
|
|
246
|
+
createTestEnvironment,
|
|
247
|
+
createTimerMocks,
|
|
248
|
+
createProcessMocks
|
|
249
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of mail/server for server tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const {createMockEventEmitter} = require('../testHelpers')
|
|
6
|
+
|
|
7
|
+
class MockMailServer {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
Object.assign(this, createMockEventEmitter())
|
|
10
|
+
|
|
11
|
+
this.options = options
|
|
12
|
+
this.listening = false
|
|
13
|
+
this.port = null
|
|
14
|
+
|
|
15
|
+
// Store callbacks for testing
|
|
16
|
+
this.onAuth = options.onAuth
|
|
17
|
+
this.onAppend = options.onAppend
|
|
18
|
+
this.onExpunge = options.onExpunge
|
|
19
|
+
this.onCreate = options.onCreate
|
|
20
|
+
this.onDelete = options.onDelete
|
|
21
|
+
this.onRename = options.onRename
|
|
22
|
+
this.onFetch = options.onFetch
|
|
23
|
+
this.onList = options.onList
|
|
24
|
+
this.onLsub = options.onLsub
|
|
25
|
+
this.onSelect = options.onSelect
|
|
26
|
+
this.onStore = options.onStore
|
|
27
|
+
this.onError = options.onError
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
listen(port, hostname, callback) {
|
|
31
|
+
if (typeof hostname === 'function') {
|
|
32
|
+
callback = hostname
|
|
33
|
+
hostname = undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.port = port
|
|
37
|
+
this.listening = true
|
|
38
|
+
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
this.emit('listening')
|
|
41
|
+
if (callback) callback()
|
|
42
|
+
}, 0)
|
|
43
|
+
|
|
44
|
+
return this
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
close(callback) {
|
|
48
|
+
this.listening = false
|
|
49
|
+
this.port = null
|
|
50
|
+
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
this.emit('close')
|
|
53
|
+
if (callback) callback()
|
|
54
|
+
}, 0)
|
|
55
|
+
|
|
56
|
+
return this
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Test helper methods
|
|
60
|
+
simulateConnection(session = {}) {
|
|
61
|
+
const mockSession = {
|
|
62
|
+
remoteAddress: '127.0.0.1',
|
|
63
|
+
remotePort: 12345,
|
|
64
|
+
user: null,
|
|
65
|
+
...session
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.emit('connect', mockSession)
|
|
69
|
+
return mockSession
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
simulateAuth(auth, session, callback) {
|
|
73
|
+
if (this.onAuth) {
|
|
74
|
+
this.onAuth(auth, session, callback)
|
|
75
|
+
} else if (callback) {
|
|
76
|
+
callback(null, {user: auth.username})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
simulateAppend(data, callback) {
|
|
81
|
+
if (this.onAppend) {
|
|
82
|
+
this.onAppend(data, callback)
|
|
83
|
+
} else if (callback) {
|
|
84
|
+
callback()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
simulateError(error) {
|
|
89
|
+
if (this.onError) {
|
|
90
|
+
this.onError(error)
|
|
91
|
+
}
|
|
92
|
+
this.emit('error', error)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const mockMailServer = jest.fn().mockImplementation(options => {
|
|
97
|
+
return new MockMailServer(options)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
module.exports = mockMailServer
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of mail/smtp for server tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const smtp = {
|
|
6
|
+
send: jest.fn(mailData => {
|
|
7
|
+
// Mock successful send
|
|
8
|
+
return Promise.resolve({
|
|
9
|
+
messageId: mailData.messageId || '<mock@example.com>',
|
|
10
|
+
accepted: mailData.to ? [mailData.to.value[0].address] : ['recipient@example.com'],
|
|
11
|
+
rejected: [],
|
|
12
|
+
pending: [],
|
|
13
|
+
response: '250 Message accepted'
|
|
14
|
+
})
|
|
15
|
+
}),
|
|
16
|
+
|
|
17
|
+
// Test helper methods
|
|
18
|
+
__setMockResponse: response => {
|
|
19
|
+
smtp.send.mockResolvedValue(response)
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
__setMockError: error => {
|
|
23
|
+
smtp.send.mockRejectedValue(error)
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
__resetMock: () => {
|
|
27
|
+
smtp.send.mockClear()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = smtp
|