@flancer32/teq-web 0.3.1 → 0.5.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/CHANGELOG.md +26 -1
- package/README.md +236 -127
- package/ai/AGENTS.md +36 -0
- package/ai/abstractions.md +75 -0
- package/ai/examples/minimal-server.md +78 -0
- package/ai/overview.md +27 -0
- package/ai/rules.md +41 -0
- package/package.json +43 -28
- package/src/Back/Api/Handler.mjs +26 -0
- package/src/Back/{Defaults.js → Defaults.mjs} +6 -1
- package/src/Back/Dto/Info.mjs +66 -0
- package/src/Back/Dto/{Handler/Source.js → Source.mjs} +26 -23
- package/src/Back/Enum/Server/Type.mjs +13 -0
- package/src/Back/Enum/Stage.mjs +13 -0
- package/src/Back/Handler/Pre/Log.mjs +59 -0
- package/src/Back/Handler/Static/A/{Config.js → Config.mjs} +14 -3
- package/src/Back/Handler/Static/A/{Fallback.js → Fallback.mjs} +16 -4
- package/src/Back/Handler/Static/A/{FileService.js → FileService.mjs} +31 -14
- package/src/Back/Handler/Static/A/{Registry.js → Registry.mjs} +18 -6
- package/src/Back/Handler/Static/A/{Resolver.js → Resolver.mjs} +13 -2
- package/src/Back/Handler/Static.mjs +83 -0
- package/src/Back/Helper/Cast.mjs +116 -0
- package/src/Back/Helper/{Mime.js → Mime.mjs} +2 -0
- package/src/Back/Helper/Order/Kahn.mjs +69 -0
- package/src/Back/Helper/{Respond.js → Respond.mjs} +14 -3
- package/src/Back/Logger.mjs +57 -0
- package/src/Back/PipelineEngine.mjs +228 -0
- package/src/Back/Server/Config/{Tls.js → Tls.mjs} +35 -33
- package/src/Back/Server/Config.mjs +69 -0
- package/src/Back/{Server.js → Server.mjs} +35 -24
- package/types.d.ts +30 -0
- package/.github/workflows/npm-publish.yml +0 -48
- package/AGENTS.md +0 -101
- package/eslint.config.js +0 -37
- package/src/AGENTS.md +0 -108
- package/src/Back/Api/Handler.js +0 -26
- package/src/Back/Dispatcher.js +0 -115
- package/src/Back/Dto/Handler/Info.js +0 -68
- package/src/Back/Enum/Server/Type.js +0 -12
- package/src/Back/Enum/Stage.js +0 -10
- package/src/Back/Handler/Pre/Log.js +0 -45
- package/src/Back/Handler/Static.js +0 -63
- package/src/Back/Helper/Cast.js +0 -114
- package/src/Back/Helper/Order/Kahn.js +0 -66
- package/src/Back/Logger.js +0 -53
- package/src/Back/Server/Config.js +0 -69
- package/teqfw.json +0 -8
- package/test/accept/ExternalServer.test.mjs +0 -66
- package/test/accept/Server.test.mjs +0 -203
- package/test/certs/ca.pem +0 -19
- package/test/certs/cert.pem +0 -19
- package/test/certs/key.pem +0 -28
- package/test/dev/app/Plugin/Start.js +0 -40
- package/test/dev/app.mjs +0 -65
- package/test/dev/web/favicon.ico +0 -0
- package/test/dev/web/index.html +0 -11
- package/test/unit/AGENTS.md +0 -106
- package/test/unit/Back/Dispatcher.test.mjs +0 -150
- package/test/unit/Back/Dto/Handler/Source.test.mjs +0 -40
- package/test/unit/Back/Handler/Pre/Log.test.mjs +0 -22
- package/test/unit/Back/Handler/Static/A/Config.test.mjs +0 -52
- package/test/unit/Back/Handler/Static/A/Fallback.test.mjs +0 -60
- package/test/unit/Back/Handler/Static/A/FileService.test.mjs +0 -225
- package/test/unit/Back/Handler/Static/A/Registry.test.mjs +0 -83
- package/test/unit/Back/Handler/Static/A/Resolver.test.mjs +0 -73
- package/test/unit/Back/Handler/Static/Static.test.mjs +0 -235
- package/test/unit/Back/Helper/Order/Kahn.test.mjs +0 -59
- package/test/unit/Back/Helper/Respond.test.mjs +0 -83
- package/test/unit/Back/Server.test.mjs +0 -112
- package/test/unit/common.js +0 -22
package/test/certs/ca.pem
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
|
2
|
-
MIIDCTCCAfGgAwIBAgIUBpZ2v9fA8XfqVF83Nw5byUa0ylswDQYJKoZIhvcNAQEL
|
|
3
|
-
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDYwODA5MDA1N1oXDTI2MDYw
|
|
4
|
-
ODA5MDA1N1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
|
5
|
-
AAOCAQ8AMIIBCgKCAQEAymikxJnbPFs3H/rRzetPjUdOPVuMOTfkz5oBDcP7s/2W
|
|
6
|
-
O7135UPbDGzClYyYJvwAZm9i1Vivzm6KrNDZR0lawqYVf6COGJSAtMrkzx+bFSh7
|
|
7
|
-
HWhuk4ByAdoZr6kFzpYBrxcfjSLU2oqaCyzb5vfksfrk1Yyr/i8DK6sw640OUlzP
|
|
8
|
-
1rtKTuRCpYm8We/wrxEvrcdzk7FVFYLCzQfGOZLPmZpoDI3pdUJ/wkY4tjPAPFLn
|
|
9
|
-
143L+07ZLN7Zuf0mpRAGpKve+MWzfId4xXoJ2XpGolLEkhJhfDNI9DCBjEZ3cYmJ
|
|
10
|
-
LYSkpevQuTJNQm/89ivPk3FV0p3H4KjKKgtvtUaOZQIDAQABo1MwUTAdBgNVHQ4E
|
|
11
|
-
FgQUjriVSWVYIJ/a21utkNLU2Cvjl1swHwYDVR0jBBgwFoAUjriVSWVYIJ/a21ut
|
|
12
|
-
kNLU2Cvjl1swDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkcHr
|
|
13
|
-
TPlqzehyG1KGGb7VAGwCoqhUJfz0Pd3z2p+UOoKq/EmC8b1FRgKDZxSQHk0hhQWE
|
|
14
|
-
M4f51tTbv2LgskfOF/S/tDoKBGnOwYS0oFmVqEstHuC2uto8ukj3FNvsZW58x4Q1
|
|
15
|
-
DOGLkUqlPj5vAh8Zhw8GWokorF11dZlnKdPTC3NCdqyPTWNQPeSqd2qGsFgMBB2i
|
|
16
|
-
q49QsUCNqiVc5ZfpbTBq+Jk1qHd1Pyf9Dlxm3Wi6RBguBcpHooZTAAYleSk+uhSa
|
|
17
|
-
xH9f8v9E2aeYAk/zsZQt2f3x9sZQCAfjHPDOzXydiv568FasLxACY12/GkMNGdvy
|
|
18
|
-
xlgkz6c0z3TS8kFxrQ==
|
|
19
|
-
-----END CERTIFICATE-----
|
package/test/certs/cert.pem
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
|
2
|
-
MIIDCTCCAfGgAwIBAgIUBpZ2v9fA8XfqVF83Nw5byUa0ylswDQYJKoZIhvcNAQEL
|
|
3
|
-
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDYwODA5MDA1N1oXDTI2MDYw
|
|
4
|
-
ODA5MDA1N1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
|
5
|
-
AAOCAQ8AMIIBCgKCAQEAymikxJnbPFs3H/rRzetPjUdOPVuMOTfkz5oBDcP7s/2W
|
|
6
|
-
O7135UPbDGzClYyYJvwAZm9i1Vivzm6KrNDZR0lawqYVf6COGJSAtMrkzx+bFSh7
|
|
7
|
-
HWhuk4ByAdoZr6kFzpYBrxcfjSLU2oqaCyzb5vfksfrk1Yyr/i8DK6sw640OUlzP
|
|
8
|
-
1rtKTuRCpYm8We/wrxEvrcdzk7FVFYLCzQfGOZLPmZpoDI3pdUJ/wkY4tjPAPFLn
|
|
9
|
-
143L+07ZLN7Zuf0mpRAGpKve+MWzfId4xXoJ2XpGolLEkhJhfDNI9DCBjEZ3cYmJ
|
|
10
|
-
LYSkpevQuTJNQm/89ivPk3FV0p3H4KjKKgtvtUaOZQIDAQABo1MwUTAdBgNVHQ4E
|
|
11
|
-
FgQUjriVSWVYIJ/a21utkNLU2Cvjl1swHwYDVR0jBBgwFoAUjriVSWVYIJ/a21ut
|
|
12
|
-
kNLU2Cvjl1swDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkcHr
|
|
13
|
-
TPlqzehyG1KGGb7VAGwCoqhUJfz0Pd3z2p+UOoKq/EmC8b1FRgKDZxSQHk0hhQWE
|
|
14
|
-
M4f51tTbv2LgskfOF/S/tDoKBGnOwYS0oFmVqEstHuC2uto8ukj3FNvsZW58x4Q1
|
|
15
|
-
DOGLkUqlPj5vAh8Zhw8GWokorF11dZlnKdPTC3NCdqyPTWNQPeSqd2qGsFgMBB2i
|
|
16
|
-
q49QsUCNqiVc5ZfpbTBq+Jk1qHd1Pyf9Dlxm3Wi6RBguBcpHooZTAAYleSk+uhSa
|
|
17
|
-
xH9f8v9E2aeYAk/zsZQt2f3x9sZQCAfjHPDOzXydiv568FasLxACY12/GkMNGdvy
|
|
18
|
-
xlgkz6c0z3TS8kFxrQ==
|
|
19
|
-
-----END CERTIFICATE-----
|
package/test/certs/key.pem
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
-----BEGIN PRIVATE KEY-----
|
|
2
|
-
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDKaKTEmds8Wzcf
|
|
3
|
-
+tHN60+NR049W4w5N+TPmgENw/uz/ZY7vXflQ9sMbMKVjJgm/ABmb2LVWK/Oboqs
|
|
4
|
-
0NlHSVrCphV/oI4YlIC0yuTPH5sVKHsdaG6TgHIB2hmvqQXOlgGvFx+NItTaipoL
|
|
5
|
-
LNvm9+Sx+uTVjKv+LwMrqzDrjQ5SXM/Wu0pO5EKlibxZ7/CvES+tx3OTsVUVgsLN
|
|
6
|
-
B8Y5ks+ZmmgMjel1Qn/CRji2M8A8UufXjcv7Ttks3tm5/SalEAakq974xbN8h3jF
|
|
7
|
-
egnZekaiUsSSEmF8M0j0MIGMRndxiYkthKSl69C5Mk1Cb/z2K8+TcVXSncfgqMoq
|
|
8
|
-
C2+1Ro5lAgMBAAECggEAEva0lUI6/aUoKC62Ox4++6QWIFPe8o9vjcRxMSp5qcvq
|
|
9
|
-
Uu+LTPzLXZclBfZAXSACzkDFAusbvEdeu8FCJ4EnvTvqoLoDW38yWH3367CbiuMa
|
|
10
|
-
NyT30zRXZMqxLw7ddJUDqbViEeA/uWKp+xO5Xfg/bNjDrtROsEe++szqY5n5dl2B
|
|
11
|
-
aJLPEtg2OQW7GWKF7PX9YyZmbZds+L7WFhTE+42zfO6MMQm5jaTr8er74u6FnEA2
|
|
12
|
-
ewrJmB7i9CS1DfYhCnJCwqOIxr9rPp9WMvRNfmL8wwqAr+4f1TidI7aKpA3vt2d0
|
|
13
|
-
HOrnNws+08tlcqQYHHyasLzBzsYGAZRTvCoCxjVd8wKBgQDkWjNxp9TmNxQXxvhB
|
|
14
|
-
J42lmo+9xBYzKUzmMMSvS7RoPFG8myf1s82qPTiwZZzGlpR6XIXfSZKQ45YhYz7S
|
|
15
|
-
8i8VbhjVtGyOmExBRYryI3y8pC1aVHk6bPQl8rATIkqsIGN8VHSiwCMeV+j6VWUP
|
|
16
|
-
ZYjb0iqS2zKhhXD9CVcXIT/SfwKBgQDi6lG64nI/ECjakXfrXa+J1T5NJE+OEP3J
|
|
17
|
-
AR9nqRiR/VcwAFMBiJwFfm07dv4WHLbfI1qdx+OU4aSsBohyLg3X9WO0Y8iGw2w4
|
|
18
|
-
V6GGiwGK009OfzdrwTM9QjVHhk23SlFeQFPib4uRGcRnjimMNY6M1E3qvXHpyVXV
|
|
19
|
-
rpvxyGElGwKBgGjcXwlPJ738BvcQQIoy7qHggyeCdytRSOXf+UICQrsnD+XLXiM/
|
|
20
|
-
SS9m47RlRQQQu+ggur0ZnPt590Qnvf7ChgqSP0dLjhpBJ6tFkxO0ZiB+R/FWH0FM
|
|
21
|
-
LSWL930h3yaBzQ2X/uOJ1damSe9C7aCPYLSJI1HC5NI1Y/hepKaTdypjAoGAEfkr
|
|
22
|
-
ZhkfoX0fL0jMbdkq2UkJuUSCBKe14mDzYtuS9aVSbZvo9zsh2JGOB2LCd2/o0D3V
|
|
23
|
-
pJ+7mARTbcjKr/iT4iIutpAcxwfdn4zZX3XNNnjMVFRhSGiyLUz8OWEa8MSzMzr3
|
|
24
|
-
Kf1Z2bFnzCgHhHKNivwZ+9jrl+/5m4ZMFdegUjcCgYEAu/cKxIBc3Lb4gVZx6nHp
|
|
25
|
-
bPr9flDbdCKQk20kpVPvNxq67FuVyKy3n0svRX+fZ9uPhuv20IWkTBue3yCGKpjD
|
|
26
|
-
kUJ2A9Y9FZGSwhU3LSo6grdEv3pIW1YLTl95nUO20bRWCWcwsoV5/GEhFkEZWq06
|
|
27
|
-
wZCQXFhIY78gzd4gk+qJQpg=
|
|
28
|
-
-----END PRIVATE KEY-----
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export default class App_Plugin_Start {
|
|
2
|
-
/**
|
|
3
|
-
* @param {typeof import('node:path')} path
|
|
4
|
-
* @param {typeof import('node:url')} url
|
|
5
|
-
* @param {Fl32_Web_Back_Dispatcher} dispatcher
|
|
6
|
-
* @param {Fl32_Web_Back_Handler_Pre_Log} hndlRequestLog
|
|
7
|
-
* @param {Fl32_Web_Back_Handler_Static} hndlStatic
|
|
8
|
-
* @param {Fl32_Web_Back_Dto_Handler_Source} dtoCfg
|
|
9
|
-
*/
|
|
10
|
-
constructor(
|
|
11
|
-
{
|
|
12
|
-
'node:path': path,
|
|
13
|
-
'node:url': url,
|
|
14
|
-
Fl32_Web_Back_Dispatcher$: dispatcher,
|
|
15
|
-
Fl32_Web_Back_Handler_Pre_Log$: hndlRequestLog,
|
|
16
|
-
Fl32_Web_Back_Handler_Static$: hndlStatic,
|
|
17
|
-
Fl32_Web_Back_Dto_Handler_Source$: dtoCfg,
|
|
18
|
-
}
|
|
19
|
-
) {
|
|
20
|
-
// VARS
|
|
21
|
-
const {dirname, join} = path;
|
|
22
|
-
const {fileURLToPath} = url;
|
|
23
|
-
|
|
24
|
-
// MAIN
|
|
25
|
-
/* Resolve a path to the root folder. */
|
|
26
|
-
const metaUrl = new URL(import.meta.url);
|
|
27
|
-
const script = fileURLToPath(metaUrl);
|
|
28
|
-
const cur = dirname(script);
|
|
29
|
-
const root = join(cur, '..', '..');
|
|
30
|
-
const webRoot = join(root, 'web');
|
|
31
|
-
|
|
32
|
-
return async function () {
|
|
33
|
-
const srcNpm = dtoCfg.create({root: 'node_modules', prefix: '/npm/', allow: {'@teqfw/di': ['src/Container.js']}});
|
|
34
|
-
const srcWeb = dtoCfg.create({root: webRoot, prefix: '/'});
|
|
35
|
-
await hndlStatic.init({sources: [srcNpm, srcWeb]});
|
|
36
|
-
dispatcher.addHandler(hndlRequestLog);
|
|
37
|
-
dispatcher.addHandler(hndlStatic);
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
package/test/dev/app.mjs
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
/**
|
|
4
|
-
* A test script to emulate an app that uses the web server.
|
|
5
|
-
*/
|
|
6
|
-
import {dirname, join} from 'node:path';
|
|
7
|
-
import {fileURLToPath} from 'node:url';
|
|
8
|
-
import {readFileSync} from 'node:fs';
|
|
9
|
-
import Container from '@teqfw/di';
|
|
10
|
-
|
|
11
|
-
// VARS
|
|
12
|
-
/* Resolve a path to the root folder. */
|
|
13
|
-
const url = new URL(import.meta.url);
|
|
14
|
-
const script = fileURLToPath(url);
|
|
15
|
-
const cur = dirname(script);
|
|
16
|
-
const root = join(cur, '..', '..');
|
|
17
|
-
|
|
18
|
-
// Create a new instance of the container
|
|
19
|
-
const container = new Container();
|
|
20
|
-
|
|
21
|
-
// Get the resolver from the container
|
|
22
|
-
const resolver = container.getResolver();
|
|
23
|
-
resolver.addNamespaceRoot('Fl32_Web_', join(root, 'src'));
|
|
24
|
-
resolver.addNamespaceRoot('App_', join(cur, 'app'));
|
|
25
|
-
|
|
26
|
-
// init the app (add the handlers to the Dispatcher)
|
|
27
|
-
/** @type {function} */
|
|
28
|
-
const appStart = await container.get('App_Plugin_Start$');
|
|
29
|
-
await appStart();
|
|
30
|
-
|
|
31
|
-
// order handlers in the Dispatcher
|
|
32
|
-
/** @type {Fl32_Web_Back_Dispatcher} */
|
|
33
|
-
const dispatcher = await container.get('Fl32_Web_Back_Dispatcher$');
|
|
34
|
-
dispatcher.orderHandlers();
|
|
35
|
-
|
|
36
|
-
// configure and run the server
|
|
37
|
-
/** @type {Fl32_Web_Back_Server} */
|
|
38
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
39
|
-
/** @type {typeof Fl32_Web_Back_Enum_Server_Type} */
|
|
40
|
-
const SERVER_TYPE = await container.get('Fl32_Web_Back_Enum_Server_Type$');
|
|
41
|
-
/** @type {Fl32_Web_Back_Server_Config} */
|
|
42
|
-
const factConfig = await container.get('Fl32_Web_Back_Server_Config$');
|
|
43
|
-
|
|
44
|
-
// Read TLS certificates
|
|
45
|
-
const certsDir = join(cur, '..', 'certs');
|
|
46
|
-
const key = readFileSync(join(certsDir, 'key.pem'), 'utf8');
|
|
47
|
-
const cert = readFileSync(join(certsDir, 'cert.pem'), 'utf8');
|
|
48
|
-
let ca;
|
|
49
|
-
try {
|
|
50
|
-
ca = readFileSync(join(certsDir, 'ca.pem'), 'utf8');
|
|
51
|
-
} catch (e) {
|
|
52
|
-
// CA certificate is optional
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log('Starting HTTPS server on port 3443...');
|
|
56
|
-
const cfg = factConfig.create({
|
|
57
|
-
port: 3443,
|
|
58
|
-
type: SERVER_TYPE.HTTPS,
|
|
59
|
-
tls: {
|
|
60
|
-
key,
|
|
61
|
-
cert,
|
|
62
|
-
ca
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
await server.start(cfg);
|
package/test/dev/web/favicon.ico
DELETED
|
Binary file
|
package/test/dev/web/index.html
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
6
|
-
<title>@flancer32/teq-web</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<p>Hello, from `@flancer32/teq-web`!</p>
|
|
10
|
-
</body>
|
|
11
|
-
</html>
|
package/test/unit/AGENTS.md
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# AI Agent Unit Testing Instructions for @flancer32/teq-web
|
|
2
|
-
|
|
3
|
-
This document outlines project-specific guidelines for writing and maintaining unit tests in the `@flancer32/teq-web` plugin. Follow these rules to ensure consistency and reliability.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Test File Location & Naming
|
|
8
|
-
|
|
9
|
-
* Mirror `src/` structure under `test/unit/`, using the same path and filenames.
|
|
10
|
-
* Use `.test.mjs` suffix for test files, e.g. `Config.test.mjs`, `Kahn.test.mjs`.
|
|
11
|
-
* Top-level `describe` must reference the full DI key:
|
|
12
|
-
|
|
13
|
-
```js
|
|
14
|
-
describe('Fl32_Web_Back_Handler_Static_A_Config', () => { /* ... */ });
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## 2. Dependency Injection via DI Container
|
|
18
|
-
|
|
19
|
-
* Always call `buildTestContainer()` from `test/unit/common.js` and then:
|
|
20
|
-
|
|
21
|
-
```js
|
|
22
|
-
/** @type {Fl32_Web_Back_Handler_Static_A_Config} */
|
|
23
|
-
const config = await container.get('Fl32_Web_Back_Handler_Static_A_Config$');
|
|
24
|
-
```
|
|
25
|
-
* **Do not** import production modules (`node:path`, etc.) directly—register or mock everything through the container.
|
|
26
|
-
* Use `container.register(depId, mock)` to override dependencies in test mode.
|
|
27
|
-
|
|
28
|
-
## 3. Asynchronous Subject Retrieval
|
|
29
|
-
|
|
30
|
-
* Declare your `it` or `beforeEach` callbacks as `async` if you call `await container.get(...)`.
|
|
31
|
-
* Always `await container.get('Your_Service_Key$')` to obtain the actual instance before invoking its methods.
|
|
32
|
-
|
|
33
|
-
## 4. Testing Patterns
|
|
34
|
-
|
|
35
|
-
* Use Node’s built-in test runner and assertion library:
|
|
36
|
-
|
|
37
|
-
```js
|
|
38
|
-
import { describe, it } from 'node:test';
|
|
39
|
-
import assert from 'node:assert/strict';
|
|
40
|
-
```
|
|
41
|
-
* **Success cases**:
|
|
42
|
-
|
|
43
|
-
* `assert.strictEqual(actual, expected)` for primitives.
|
|
44
|
-
* `assert.deepStrictEqual(actual, expected)` for objects/arrays.
|
|
45
|
-
* **Error cases**:
|
|
46
|
-
|
|
47
|
-
* `assert.throws(() => fn(), /message/)` matching a key fragment of the error.
|
|
48
|
-
|
|
49
|
-
## 5. DTO Shape & Defaults
|
|
50
|
-
|
|
51
|
-
* Pass a full DTO to `factory.create(dto)`.
|
|
52
|
-
* To test fallback/default logic, supply `[]` for optional arrays.
|
|
53
|
-
|
|
54
|
-
## 6. Comments & Documentation
|
|
55
|
-
|
|
56
|
-
* All inline comments must be in **English**.
|
|
57
|
-
* Comment only non-trivial logic; don’t restate obvious assertions.
|
|
58
|
-
|
|
59
|
-
## 7. Mocks & Helpers
|
|
60
|
-
|
|
61
|
-
* Use plain JS objects or small factory functions for mocks.
|
|
62
|
-
* No external mocking libraries—rely on `@teqfw/di` test mode.
|
|
63
|
-
|
|
64
|
-
## 8. Maintenance
|
|
65
|
-
|
|
66
|
-
* One behavior per `it` block—keep tests focused and concise.
|
|
67
|
-
* Update tests when API or default constants change.
|
|
68
|
-
* Ensure CI runs all tests automatically.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Test Mode Support in `@teqfw/di`
|
|
73
|
-
|
|
74
|
-
When you enable test mode, you can inject or override any dependency—built-in or custom—without touching production code:
|
|
75
|
-
|
|
76
|
-
```js
|
|
77
|
-
const container = buildTestContainer();
|
|
78
|
-
container.enableTestMode();
|
|
79
|
-
|
|
80
|
-
// override a service or Node builtin
|
|
81
|
-
container.register('Fl32_Web_Back_Logger$', mockLogger);
|
|
82
|
-
container.register('node:fs', { /* mock fs.promises.stat… */});
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
* **register** vs. **registerInstance**
|
|
86
|
-
Use `container.register(depId, instanceOrFactory)` to bind mocks. Avoid `registerInstance`, which is not part of the public test-mode API.
|
|
87
|
-
|
|
88
|
-
### Mocking Node.js Built-ins
|
|
89
|
-
|
|
90
|
-
```js
|
|
91
|
-
// simulate filesystem behavior
|
|
92
|
-
container.register('node:fs', {
|
|
93
|
-
promises: {
|
|
94
|
-
stat: async (p) => { /* … */ }
|
|
95
|
-
},
|
|
96
|
-
createReadStream: (p) => { /* … */ }
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// adjust path logic
|
|
100
|
-
container.register('node:path', {
|
|
101
|
-
join: (...parts) => parts.join('/'),
|
|
102
|
-
resolve: (p) => `/abs/${p}`
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
These overrides are injected into every module that asks for `node:fs` or `node:path`, enabling isolated, deterministic tests without side effects.
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {describe, it, beforeEach} from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {buildTestContainer} from '../common.js';
|
|
4
|
-
|
|
5
|
-
/** Collects execution order */
|
|
6
|
-
let log;
|
|
7
|
-
/** DI container for each test */
|
|
8
|
-
let container;
|
|
9
|
-
/** Dispatcher stages enum */
|
|
10
|
-
let STAGE;
|
|
11
|
-
/** Respond helper mock */
|
|
12
|
-
let respond;
|
|
13
|
-
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
log = [];
|
|
16
|
-
container = buildTestContainer();
|
|
17
|
-
|
|
18
|
-
// Mock logger to keep tests quiet
|
|
19
|
-
container.register('Fl32_Web_Back_Logger$', {
|
|
20
|
-
info: () => {},
|
|
21
|
-
error: () => {},
|
|
22
|
-
exception: () => {},
|
|
23
|
-
});
|
|
24
|
-
// Keep handler order as provided
|
|
25
|
-
container.register('Fl32_Web_Back_Helper_Order_Kahn$', {sort: arr => arr});
|
|
26
|
-
|
|
27
|
-
// Respond helper stub
|
|
28
|
-
respond = {
|
|
29
|
-
isWritable: res => !res.headersSent && !res.writableEnded,
|
|
30
|
-
code404_NotFound: ({res}) => { res.code = 404; res.headersSent = true; },
|
|
31
|
-
code500_InternalServerError: ({res}) => { res.code = 500; res.headersSent = true; },
|
|
32
|
-
};
|
|
33
|
-
container.register('Fl32_Web_Back_Helper_Respond$', respond);
|
|
34
|
-
|
|
35
|
-
STAGE = await container.get('Fl32_Web_Back_Enum_Stage$');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
function pre(name) {
|
|
39
|
-
return {
|
|
40
|
-
getRegistrationInfo: () => ({name, stage: STAGE.PRE}),
|
|
41
|
-
handle: async () => { log.push(name); },
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function proc(name, opts = {}) {
|
|
46
|
-
const {handled = true, throwErr = false, send = true} = opts;
|
|
47
|
-
return {
|
|
48
|
-
getRegistrationInfo: () => ({name, stage: STAGE.PROCESS}),
|
|
49
|
-
handle: async (req, res) => {
|
|
50
|
-
log.push(name);
|
|
51
|
-
if (throwErr) throw new Error('boom');
|
|
52
|
-
if (send) res.headersSent = true;
|
|
53
|
-
return handled;
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function post(name) {
|
|
59
|
-
return {
|
|
60
|
-
getRegistrationInfo: () => ({name, stage: STAGE.POST}),
|
|
61
|
-
handle: async () => { log.push(name); },
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
describe('Fl32_Web_Back_Dispatcher', () => {
|
|
66
|
-
it('calls pre-handlers even when a process handler fails', async () => {
|
|
67
|
-
const dispatcher = await container.get('Fl32_Web_Back_Dispatcher$');
|
|
68
|
-
dispatcher.addHandler(pre('pre'));
|
|
69
|
-
dispatcher.addHandler(proc('proc', {throwErr: true, send: false}));
|
|
70
|
-
dispatcher.orderHandlers();
|
|
71
|
-
|
|
72
|
-
const req = {url: '/'}; const res = {};
|
|
73
|
-
await dispatcher.onEventRequest(req, res);
|
|
74
|
-
|
|
75
|
-
assert.strictEqual(log[0], 'pre');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('executes post-handlers after a successful process handler', async () => {
|
|
79
|
-
const dispatcher = await container.get('Fl32_Web_Back_Dispatcher$');
|
|
80
|
-
dispatcher.addHandler(pre('pre'));
|
|
81
|
-
dispatcher.addHandler(proc('proc'));
|
|
82
|
-
dispatcher.addHandler(post('post'));
|
|
83
|
-
dispatcher.orderHandlers();
|
|
84
|
-
|
|
85
|
-
const req = {url: '/'}; const res = {};
|
|
86
|
-
await dispatcher.onEventRequest(req, res);
|
|
87
|
-
|
|
88
|
-
assert.deepStrictEqual(log, ['pre', 'proc', 'post']);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('returns 500 if a process handler throws', async () => {
|
|
92
|
-
const dispatcher = await container.get('Fl32_Web_Back_Dispatcher$');
|
|
93
|
-
dispatcher.addHandler(proc('proc', {throwErr: true, send: false}));
|
|
94
|
-
dispatcher.orderHandlers();
|
|
95
|
-
|
|
96
|
-
const req = {url: '/'}; const res = {};
|
|
97
|
-
await dispatcher.onEventRequest(req, res);
|
|
98
|
-
|
|
99
|
-
assert.strictEqual(res.code, 500);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('returns 404 if no process handler handles the request', async () => {
|
|
103
|
-
const dispatcher = await container.get('Fl32_Web_Back_Dispatcher$');
|
|
104
|
-
dispatcher.addHandler(pre('pre'));
|
|
105
|
-
dispatcher.addHandler(proc('p1', {handled: false, send: false}));
|
|
106
|
-
dispatcher.addHandler(post('post'));
|
|
107
|
-
dispatcher.orderHandlers();
|
|
108
|
-
|
|
109
|
-
const req = {url: '/missing'}; const res = {};
|
|
110
|
-
await dispatcher.onEventRequest(req, res);
|
|
111
|
-
|
|
112
|
-
assert.strictEqual(res.code, 404);
|
|
113
|
-
assert.deepStrictEqual(log, ['pre', 'p1', 'post']);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('orders handlers according to before/after dependencies', async () => {
|
|
117
|
-
const localLog = [];
|
|
118
|
-
const container2 = buildTestContainer();
|
|
119
|
-
|
|
120
|
-
container2.register('Fl32_Web_Back_Logger$', {
|
|
121
|
-
info: () => {},
|
|
122
|
-
error: () => {},
|
|
123
|
-
exception: () => {},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const respond2 = {
|
|
127
|
-
isWritable: res => !res.headersSent && !res.writableEnded,
|
|
128
|
-
code404_NotFound: ({res}) => { res.code = 404; res.headersSent = true; },
|
|
129
|
-
code500_InternalServerError: ({res}) => { res.code = 500; res.headersSent = true; },
|
|
130
|
-
};
|
|
131
|
-
container2.register('Fl32_Web_Back_Helper_Respond$', respond2);
|
|
132
|
-
|
|
133
|
-
const STAGE2 = await container2.get('Fl32_Web_Back_Enum_Stage$');
|
|
134
|
-
const mk = (name, after = []) => ({
|
|
135
|
-
getRegistrationInfo: () => ({name, stage: STAGE2.PRE, after}),
|
|
136
|
-
handle: async () => { localLog.push(name); },
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const dispatcher = await container2.get('Fl32_Web_Back_Dispatcher$');
|
|
140
|
-
dispatcher.addHandler(mk('a', ['c']));
|
|
141
|
-
dispatcher.addHandler(mk('b', ['a']));
|
|
142
|
-
dispatcher.addHandler(mk('c'));
|
|
143
|
-
dispatcher.orderHandlers();
|
|
144
|
-
|
|
145
|
-
await dispatcher.onEventRequest({}, {});
|
|
146
|
-
|
|
147
|
-
assert.deepStrictEqual(localLog, ['c', 'a', 'b']);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'assert';
|
|
3
|
-
import {buildTestContainer} from '../../../common.js';
|
|
4
|
-
|
|
5
|
-
test.describe('Fl32_Web_Back_Dto_Handler_Source', () => {
|
|
6
|
-
test('should create valid config DTO with casted fields', async () => {
|
|
7
|
-
const container = buildTestContainer();
|
|
8
|
-
container.register('Fl32_Web_Back_Helper_Cast$', {
|
|
9
|
-
string: (d) => typeof d === 'string' ? d : undefined,
|
|
10
|
-
stringArrayMap: (d) => d,
|
|
11
|
-
array: (d, item) => Array.isArray(d) ? d.map(item) : [],
|
|
12
|
-
});
|
|
13
|
-
const factory = await container.get('Fl32_Web_Back_Dto_Handler_Source$');
|
|
14
|
-
const dto = factory.create({
|
|
15
|
-
root: '/abs/path',
|
|
16
|
-
prefix: '/src/',
|
|
17
|
-
allow: { vue: ['dist/vue.global.js'] },
|
|
18
|
-
defaults: ['index.html'],
|
|
19
|
-
});
|
|
20
|
-
assert.strictEqual(dto.root, '/abs/path');
|
|
21
|
-
assert.strictEqual(dto.prefix, '/src/');
|
|
22
|
-
assert.deepStrictEqual(dto.allow, { vue: ['dist/vue.global.js'] });
|
|
23
|
-
assert.deepStrictEqual(dto.defaults, ['index.html']);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('should return undefined fields if values are invalid', async () => {
|
|
27
|
-
const container = buildTestContainer();
|
|
28
|
-
container.register('Fl32_Web_Back_Helper_Cast$', {
|
|
29
|
-
string: () => undefined,
|
|
30
|
-
stringArrayMap: () => ({}),
|
|
31
|
-
array: () => [],
|
|
32
|
-
});
|
|
33
|
-
const factory = await container.get('Fl32_Web_Back_Dto_Handler_Source$');
|
|
34
|
-
const dto = factory.create({});
|
|
35
|
-
assert.strictEqual(dto.root, undefined);
|
|
36
|
-
assert.strictEqual(dto.prefix, undefined);
|
|
37
|
-
assert.deepStrictEqual(dto.allow, {});
|
|
38
|
-
assert.deepStrictEqual(dto.defaults, []);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import {describe, it, beforeEach} from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {buildTestContainer} from '../../../common.js';
|
|
4
|
-
|
|
5
|
-
describe('Fl32_Web_Back_Handler_Pre_Log', () => {
|
|
6
|
-
let container;
|
|
7
|
-
const log = [];
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
log.length = 0;
|
|
11
|
-
container = buildTestContainer();
|
|
12
|
-
container.register('Fl32_Web_Back_Logger$', {
|
|
13
|
-
debug: (msg) => log.push(msg),
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('logs method and url', async () => {
|
|
18
|
-
const handler = await container.get('Fl32_Web_Back_Handler_Pre_Log$');
|
|
19
|
-
await handler.handle({method: 'GET', url: '/path'});
|
|
20
|
-
assert.deepStrictEqual(log, ['GET /path']);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import {describe, it} from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {buildTestContainer} from '../../../../common.js';
|
|
4
|
-
|
|
5
|
-
describe('Fl32_Web_Back_Handler_Static_A_Config', () => {
|
|
6
|
-
it('normalizes root, prefix, allow and defaults', async () => {
|
|
7
|
-
const container = buildTestContainer();
|
|
8
|
-
/** @type {Fl32_Web_Back_Handler_Static_A_Config} */
|
|
9
|
-
const factory = await container.get('Fl32_Web_Back_Handler_Static_A_Config$');
|
|
10
|
-
|
|
11
|
-
const dto = {
|
|
12
|
-
root: './r',
|
|
13
|
-
prefix: '/p',
|
|
14
|
-
allow: {'.': ['.']},
|
|
15
|
-
defaults: []
|
|
16
|
-
};
|
|
17
|
-
const cfg = factory.create(dto);
|
|
18
|
-
|
|
19
|
-
// root is resolved via node:path from the container
|
|
20
|
-
const {resolve} = await import('node:path');
|
|
21
|
-
assert.strictEqual(cfg.root, resolve('./r'));
|
|
22
|
-
|
|
23
|
-
// prefix is normalized to always end with a slash
|
|
24
|
-
assert.strictEqual(cfg.prefix, '/p/');
|
|
25
|
-
|
|
26
|
-
// allowed extensions are preserved
|
|
27
|
-
assert.deepStrictEqual(cfg.allow, {'.': ['.']});
|
|
28
|
-
|
|
29
|
-
// defaults fallback to built-in list when empty
|
|
30
|
-
assert.deepStrictEqual(
|
|
31
|
-
cfg.defaults,
|
|
32
|
-
['index.html', 'index.htm', 'index.txt']
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('throws on invalid data', async () => {
|
|
37
|
-
const container = buildTestContainer();
|
|
38
|
-
const factory = await container.get('Fl32_Web_Back_Handler_Static_A_Config$');
|
|
39
|
-
|
|
40
|
-
// missing root should throw an error about root
|
|
41
|
-
assert.throws(
|
|
42
|
-
() => factory.create({prefix: '/'}),
|
|
43
|
-
/Field 'root' must be a string/
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
// non-string prefix should throw an error about prefix
|
|
47
|
-
assert.throws(
|
|
48
|
-
() => factory.create({root: 'a', prefix: 5}),
|
|
49
|
-
/Field 'prefix' must be a string/
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import {describe, it, beforeEach} from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {buildTestContainer} from '../../../../common.js';
|
|
4
|
-
|
|
5
|
-
describe('Fl32_Web_Back_Handler_Static_A_Fallback', () => {
|
|
6
|
-
/** @type {{ promises: { stat: (p: string) => Promise<any> }, _add: (p: string, isFile: boolean) => void }} */
|
|
7
|
-
let mockFs;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
const storage = new Map();
|
|
11
|
-
|
|
12
|
-
mockFs = {
|
|
13
|
-
promises: {
|
|
14
|
-
stat: async p => {
|
|
15
|
-
// normalize path: backslashes → slashes, remove duplicate and trailing slash
|
|
16
|
-
const key = p.replace(/\\/g, '/').replace(/\/+/g, '/').replace(/\/$/, '');
|
|
17
|
-
if (!storage.has(key)) throw new Error('ENOENT');
|
|
18
|
-
return storage.get(key);
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
/** Adds a file or directory into the mock storage */
|
|
22
|
-
_add: (p, isFile) => {
|
|
23
|
-
const key = p.replace(/\\/g, '/').replace(/\/+/g, '/').replace(/\/$/, '');
|
|
24
|
-
storage.set(key, {
|
|
25
|
-
isFile: () => isFile,
|
|
26
|
-
isDirectory: () => !isFile
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
function addFile(p) { mockFs._add(p, true); }
|
|
33
|
-
|
|
34
|
-
function addDir(p) { mockFs._add(p, false); }
|
|
35
|
-
|
|
36
|
-
/** Creates and returns a configured Fallback instance */
|
|
37
|
-
async function getFallback() {
|
|
38
|
-
const container = buildTestContainer();
|
|
39
|
-
container.register('node:fs', mockFs);
|
|
40
|
-
/** @type {Fl32_Web_Back_Handler_Static_A_Fallback} */
|
|
41
|
-
return container.get('Fl32_Web_Back_Handler_Static_A_Fallback$');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
it('returns index file for a directory', async () => {
|
|
45
|
-
addDir('/d');
|
|
46
|
-
addFile('/d/index.html');
|
|
47
|
-
|
|
48
|
-
const fb = await getFallback();
|
|
49
|
-
const result = await fb.apply('/d', ['index.html']);
|
|
50
|
-
assert.strictEqual(result, '/d/index.html');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('returns null when nothing found', async () => {
|
|
54
|
-
addDir('/x');
|
|
55
|
-
|
|
56
|
-
const fb = await getFallback();
|
|
57
|
-
const result = await fb.apply('/x', ['a.html']);
|
|
58
|
-
assert.strictEqual(result, null);
|
|
59
|
-
});
|
|
60
|
-
});
|