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,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of the path module for server tests
|
|
3
|
+
* Provides comprehensive mocking of path manipulation utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = {
|
|
7
|
+
// Path separator
|
|
8
|
+
sep: '/',
|
|
9
|
+
delimiter: ':',
|
|
10
|
+
|
|
11
|
+
// POSIX and Windows path objects
|
|
12
|
+
posix: null, // Will be set to this object
|
|
13
|
+
win32: null, // Will be set to a Windows version
|
|
14
|
+
|
|
15
|
+
// Path manipulation functions
|
|
16
|
+
basename: jest.fn((path, ext) => {
|
|
17
|
+
if (typeof path !== 'string') {
|
|
18
|
+
throw new TypeError('Path must be a string')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const lastSlash = path.lastIndexOf('/')
|
|
22
|
+
let base = lastSlash === -1 ? path : path.slice(lastSlash + 1)
|
|
23
|
+
|
|
24
|
+
if (ext && base.endsWith(ext)) {
|
|
25
|
+
base = base.slice(0, -ext.length)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return base
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
dirname: jest.fn(path => {
|
|
32
|
+
if (typeof path !== 'string') {
|
|
33
|
+
throw new TypeError('Path must be a string')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (path === '/') return '/'
|
|
37
|
+
if (path === '') return '.'
|
|
38
|
+
|
|
39
|
+
const lastSlash = path.lastIndexOf('/')
|
|
40
|
+
if (lastSlash === -1) return '.'
|
|
41
|
+
if (lastSlash === 0) return '/'
|
|
42
|
+
|
|
43
|
+
return path.slice(0, lastSlash)
|
|
44
|
+
}),
|
|
45
|
+
|
|
46
|
+
extname: jest.fn(path => {
|
|
47
|
+
if (typeof path !== 'string') {
|
|
48
|
+
throw new TypeError('Path must be a string')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const lastDot = path.lastIndexOf('.')
|
|
52
|
+
const lastSlash = path.lastIndexOf('/')
|
|
53
|
+
|
|
54
|
+
if (lastDot === -1 || lastDot < lastSlash) {
|
|
55
|
+
return ''
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return path.slice(lastDot)
|
|
59
|
+
}),
|
|
60
|
+
|
|
61
|
+
format: jest.fn(pathObject => {
|
|
62
|
+
if (typeof pathObject !== 'object' || pathObject === null) {
|
|
63
|
+
throw new TypeError('Path object must be an object')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const {dir, root, base, name, ext} = pathObject
|
|
67
|
+
|
|
68
|
+
if (base) {
|
|
69
|
+
return dir ? `${dir}/${base}` : base
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const filename = name + (ext || '')
|
|
73
|
+
|
|
74
|
+
if (dir) {
|
|
75
|
+
return `${dir}/${filename}`
|
|
76
|
+
} else if (root) {
|
|
77
|
+
return `${root}${filename}`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return filename
|
|
81
|
+
}),
|
|
82
|
+
|
|
83
|
+
isAbsolute: jest.fn(path => {
|
|
84
|
+
if (typeof path !== 'string') {
|
|
85
|
+
throw new TypeError('Path must be a string')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return path.startsWith('/')
|
|
89
|
+
}),
|
|
90
|
+
|
|
91
|
+
join: jest.fn((...paths) => {
|
|
92
|
+
if (paths.length === 0) return '.'
|
|
93
|
+
|
|
94
|
+
const filteredPaths = paths.filter(p => typeof p === 'string' && p !== '')
|
|
95
|
+
if (filteredPaths.length === 0) return '.'
|
|
96
|
+
|
|
97
|
+
let joined = filteredPaths.join('/')
|
|
98
|
+
|
|
99
|
+
// Normalize the path
|
|
100
|
+
return path.normalize(joined)
|
|
101
|
+
}),
|
|
102
|
+
|
|
103
|
+
normalize: jest.fn(path => {
|
|
104
|
+
if (typeof path !== 'string') {
|
|
105
|
+
throw new TypeError('Path must be a string')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (path === '') return '.'
|
|
109
|
+
|
|
110
|
+
const isAbsolute = path.startsWith('/')
|
|
111
|
+
const trailingSlash = path.endsWith('/')
|
|
112
|
+
|
|
113
|
+
// Split path into segments and process
|
|
114
|
+
const segments = path.split('/').filter(segment => segment !== '' && segment !== '.')
|
|
115
|
+
const normalizedSegments = []
|
|
116
|
+
|
|
117
|
+
for (const segment of segments) {
|
|
118
|
+
if (segment === '..') {
|
|
119
|
+
if (normalizedSegments.length > 0 && normalizedSegments[normalizedSegments.length - 1] !== '..') {
|
|
120
|
+
normalizedSegments.pop()
|
|
121
|
+
} else if (!isAbsolute) {
|
|
122
|
+
normalizedSegments.push('..')
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
normalizedSegments.push(segment)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let result = normalizedSegments.join('/')
|
|
130
|
+
|
|
131
|
+
if (isAbsolute) {
|
|
132
|
+
result = '/' + result
|
|
133
|
+
} else if (result === '') {
|
|
134
|
+
result = '.'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (trailingSlash && result !== '/' && result !== '.') {
|
|
138
|
+
result += '/'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result
|
|
142
|
+
}),
|
|
143
|
+
|
|
144
|
+
parse: jest.fn(path => {
|
|
145
|
+
if (typeof path !== 'string') {
|
|
146
|
+
throw new TypeError('Path must be a string')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const isAbsolute = path.startsWith('/')
|
|
150
|
+
const lastSlash = path.lastIndexOf('/')
|
|
151
|
+
const lastDot = path.lastIndexOf('.')
|
|
152
|
+
|
|
153
|
+
let dir = ''
|
|
154
|
+
let base = ''
|
|
155
|
+
let ext = ''
|
|
156
|
+
let name = ''
|
|
157
|
+
let root = ''
|
|
158
|
+
|
|
159
|
+
if (isAbsolute) {
|
|
160
|
+
root = '/'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (lastSlash === -1) {
|
|
164
|
+
base = path
|
|
165
|
+
dir = isAbsolute ? '/' : ''
|
|
166
|
+
} else {
|
|
167
|
+
dir = path.slice(0, lastSlash) || (isAbsolute ? '/' : '')
|
|
168
|
+
base = path.slice(lastSlash + 1)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (lastDot > lastSlash && lastDot > 0) {
|
|
172
|
+
ext = path.slice(lastDot)
|
|
173
|
+
name = base.slice(0, base.length - ext.length)
|
|
174
|
+
} else {
|
|
175
|
+
name = base
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {root, dir, base, ext, name}
|
|
179
|
+
}),
|
|
180
|
+
|
|
181
|
+
relative: jest.fn((from, to) => {
|
|
182
|
+
if (typeof from !== 'string' || typeof to !== 'string') {
|
|
183
|
+
throw new TypeError('From and to must be strings')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (from === to) return ''
|
|
187
|
+
|
|
188
|
+
const fromParts = path
|
|
189
|
+
.resolve(from)
|
|
190
|
+
.split('/')
|
|
191
|
+
.filter(p => p !== '')
|
|
192
|
+
const toParts = path
|
|
193
|
+
.resolve(to)
|
|
194
|
+
.split('/')
|
|
195
|
+
.filter(p => p !== '')
|
|
196
|
+
|
|
197
|
+
// Find common prefix
|
|
198
|
+
let commonLength = 0
|
|
199
|
+
const minLength = Math.min(fromParts.length, toParts.length)
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < minLength; i++) {
|
|
202
|
+
if (fromParts[i] === toParts[i]) {
|
|
203
|
+
commonLength++
|
|
204
|
+
} else {
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Build relative path
|
|
210
|
+
const upCount = fromParts.length - commonLength
|
|
211
|
+
const relativeParts = Array(upCount).fill('..').concat(toParts.slice(commonLength))
|
|
212
|
+
|
|
213
|
+
return relativeParts.join('/') || '.'
|
|
214
|
+
}),
|
|
215
|
+
|
|
216
|
+
resolve: jest.fn((...paths) => {
|
|
217
|
+
let resolvedPath = ''
|
|
218
|
+
let resolvedAbsolute = false
|
|
219
|
+
|
|
220
|
+
for (let i = paths.length - 1; i >= 0 && !resolvedAbsolute; i--) {
|
|
221
|
+
const path = paths[i]
|
|
222
|
+
|
|
223
|
+
if (typeof path !== 'string') {
|
|
224
|
+
throw new TypeError('Path must be a string')
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (path === '') continue
|
|
228
|
+
|
|
229
|
+
resolvedPath = path + '/' + resolvedPath
|
|
230
|
+
resolvedAbsolute = path.startsWith('/')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!resolvedAbsolute) {
|
|
234
|
+
resolvedPath = '/mock/cwd/' + resolvedPath
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Normalize and remove trailing slash
|
|
238
|
+
resolvedPath = path.normalize(resolvedPath)
|
|
239
|
+
if (resolvedPath.endsWith('/') && resolvedPath !== '/') {
|
|
240
|
+
resolvedPath = resolvedPath.slice(0, -1)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return resolvedPath
|
|
244
|
+
}),
|
|
245
|
+
|
|
246
|
+
// Test helpers
|
|
247
|
+
__setPosix: () => {
|
|
248
|
+
// Current implementation is already POSIX-like
|
|
249
|
+
return path
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
__setWin32: () => {
|
|
253
|
+
// Create a Windows version of path methods
|
|
254
|
+
const win32Path = {...path}
|
|
255
|
+
|
|
256
|
+
win32Path.sep = '\\'
|
|
257
|
+
win32Path.delimiter = ';'
|
|
258
|
+
|
|
259
|
+
win32Path.isAbsolute = jest.fn(path => {
|
|
260
|
+
if (typeof path !== 'string') {
|
|
261
|
+
throw new TypeError('Path must be a string')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return /^[a-zA-Z]:\\/.test(path) || path.startsWith('\\\\')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
win32Path.normalize = jest.fn(path => {
|
|
268
|
+
if (typeof path !== 'string') {
|
|
269
|
+
throw new TypeError('Path must be a string')
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return path.replace(/\//g, '\\')
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
return win32Path
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
__resetMocks: () => {
|
|
279
|
+
Object.values(path).forEach(fn => {
|
|
280
|
+
if (jest.isMockFunction(fn)) {
|
|
281
|
+
fn.mockClear()
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Set up posix and win32 references
|
|
288
|
+
path.posix = path
|
|
289
|
+
path.win32 = path.__setWin32()
|
|
290
|
+
|
|
291
|
+
module.exports = path
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const mockSelfsigned = {
|
|
2
|
+
generate: jest.fn().mockReturnValue({
|
|
3
|
+
private: '-----BEGIN PRIVATE KEY-----\nmock-private-key\n-----END PRIVATE KEY-----',
|
|
4
|
+
cert: '-----BEGIN CERTIFICATE-----\nmock-certificate\n-----END CERTIFICATE-----'
|
|
5
|
+
})
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
module.exports = mockSelfsigned
|
|
@@ -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
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of smtp-server for server tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const {createMockEventEmitter} = require('./testHelpers')
|
|
6
|
+
|
|
7
|
+
class MockSMTPServer {
|
|
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.onData = options.onData
|
|
18
|
+
this.onAppend = options.onAppend
|
|
19
|
+
this.onExpunge = options.onExpunge
|
|
20
|
+
this.onCreate = options.onCreate
|
|
21
|
+
this.onDelete = options.onDelete
|
|
22
|
+
this.onRename = options.onRename
|
|
23
|
+
this.onFetch = options.onFetch
|
|
24
|
+
this.onList = options.onList
|
|
25
|
+
this.onLsub = options.onLsub
|
|
26
|
+
this.onMailFrom = options.onMailFrom
|
|
27
|
+
this.onRcptTo = options.onRcptTo
|
|
28
|
+
this.onSelect = options.onSelect
|
|
29
|
+
this.onStore = options.onStore
|
|
30
|
+
this.onError = options.onError
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
listen(port, hostname, callback) {
|
|
34
|
+
if (typeof hostname === 'function') {
|
|
35
|
+
callback = hostname
|
|
36
|
+
hostname = undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.port = port
|
|
40
|
+
this.listening = true
|
|
41
|
+
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
this.emit('listening')
|
|
44
|
+
if (callback) callback()
|
|
45
|
+
}, 0)
|
|
46
|
+
|
|
47
|
+
return this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
close(callback) {
|
|
51
|
+
this.listening = false
|
|
52
|
+
this.port = null
|
|
53
|
+
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
this.emit('close')
|
|
56
|
+
if (callback) callback()
|
|
57
|
+
}, 0)
|
|
58
|
+
|
|
59
|
+
return this
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Test helper methods
|
|
63
|
+
simulateConnection(session = {}) {
|
|
64
|
+
const mockSession = {
|
|
65
|
+
remoteAddress: '127.0.0.1',
|
|
66
|
+
remotePort: 12345,
|
|
67
|
+
user: null,
|
|
68
|
+
...session
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.emit('connect', mockSession)
|
|
72
|
+
return mockSession
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
simulateAuth(auth, session, callback) {
|
|
76
|
+
if (this.onAuth) {
|
|
77
|
+
this.onAuth(auth, session, callback)
|
|
78
|
+
} else if (callback) {
|
|
79
|
+
callback(null, {user: auth.username})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
simulateData(stream, session, callback) {
|
|
84
|
+
if (this.onData) {
|
|
85
|
+
this.onData(stream, session, callback)
|
|
86
|
+
} else if (callback) {
|
|
87
|
+
callback()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
simulateError(error) {
|
|
92
|
+
if (this.onError) {
|
|
93
|
+
this.onError(error)
|
|
94
|
+
}
|
|
95
|
+
this.emit('error', error)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const SMTPServer = jest.fn().mockImplementation(options => {
|
|
100
|
+
return new MockSMTPServer(options)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
SMTPServer,
|
|
105
|
+
MockSMTPServer
|
|
106
|
+
}
|