koa-classic-server 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +44 -120
  2. package/index.cjs +200 -21
  3. package/package.json +10 -5
  4. package/.github/workflows/npm-publish.yml +0 -98
  5. package/CLAUDE.md +0 -101
  6. package/__tests__/benchmark-results-baseline-v1.2.0.txt +0 -354
  7. package/__tests__/benchmark-results-optimized-v2.0.0.txt +0 -354
  8. package/__tests__/benchmark-results-v3.0.0.txt +0 -372
  9. package/__tests__/benchmark.js +0 -239
  10. package/__tests__/caching-headers.test.js +0 -556
  11. package/__tests__/compression-fixtures/data.json +0 -1
  12. package/__tests__/compression-fixtures/large.txt +0 -1
  13. package/__tests__/compression-fixtures/small.txt +0 -1
  14. package/__tests__/compression.test.js +0 -284
  15. package/__tests__/customTest/README.md +0 -6
  16. package/__tests__/customTest/loadConfig.util.js +0 -41
  17. package/__tests__/customTest/serversToLoad.util.js +0 -93
  18. package/__tests__/demo-regex-index.js +0 -140
  19. package/__tests__/deprecation-warnings.test.js +0 -105
  20. package/__tests__/directory-sorting-links.test.js +0 -135
  21. package/__tests__/dt-unknown.test.js +0 -635
  22. package/__tests__/ejs.test.js +0 -299
  23. package/__tests__/head-method.test.js +0 -160
  24. package/__tests__/hidden-fixtures/.dot-dir/inside.txt +0 -1
  25. package/__tests__/hidden-fixtures/.env +0 -2
  26. package/__tests__/hidden-fixtures/.well-known/acme-challenge.txt +0 -1
  27. package/__tests__/hidden-fixtures/config/secrets/password.txt +0 -1
  28. package/__tests__/hidden-fixtures/data.key +0 -1
  29. package/__tests__/hidden-fixtures/file.secret +0 -1
  30. package/__tests__/hidden-fixtures/index.html +0 -1
  31. package/__tests__/hidden-fixtures/normal.txt +0 -1
  32. package/__tests__/hidden-fixtures/subdir/.env +0 -1
  33. package/__tests__/hidden-fixtures/subdir/regular.txt +0 -1
  34. package/__tests__/hidden-option.test.js +0 -407
  35. package/__tests__/hideExtension.test.js +0 -607
  36. package/__tests__/index-option.test.js +0 -449
  37. package/__tests__/index.test.js +0 -345
  38. package/__tests__/listing.test.js +0 -437
  39. package/__tests__/logger.test.js +0 -232
  40. package/__tests__/performance.test.js +0 -370
  41. package/__tests__/publicWwwTest/cartella/sottocartella/ciao.html +0 -11
  42. package/__tests__/publicWwwTest/cartella/sottocartella/provaEjs/testEjs.ejs +0 -11
  43. package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt +0 -1
  44. package/__tests__/publicWwwTest/cartella vuota con spazi nel nome /file con spazio nel nome .txt +0 -1
  45. package/__tests__/publicWwwTest/ejs-templates/complex.ejs +0 -56
  46. package/__tests__/publicWwwTest/ejs-templates/index.ejs +0 -30
  47. package/__tests__/publicWwwTest/ejs-templates/simple.ejs +0 -13
  48. package/__tests__/publicWwwTest/ejs-templates/with-conditional.ejs +0 -28
  49. package/__tests__/publicWwwTest/ejs-templates/with-escaping.ejs +0 -26
  50. package/__tests__/publicWwwTest/ejs-templates/with-loop.ejs +0 -16
  51. package/__tests__/publicWwwTest/hideext-test/about/index.html +0 -1
  52. package/__tests__/publicWwwTest/hideext-test/about.EJS +0 -1
  53. package/__tests__/publicWwwTest/hideext-test/about.ejs +0 -2
  54. package/__tests__/publicWwwTest/hideext-test/blog/articolo.ejs +0 -2
  55. package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina +0 -1
  56. package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina.ejs +0 -1
  57. package/__tests__/publicWwwTest/hideext-test/file.ejs.bak +0 -1
  58. package/__tests__/publicWwwTest/hideext-test/index.ejs +0 -2
  59. package/__tests__/publicWwwTest/hideext-test/photo.txt +0 -1
  60. package/__tests__/publicWwwTest/hideext-test/sezione/index.ejs +0 -1
  61. package/__tests__/publicWwwTest/hideext-test/style.css +0 -1
  62. package/__tests__/publicWwwTest/ile_vuoto.txt +0 -0
  63. package/__tests__/publicWwwTest/index.html +0 -11
  64. package/__tests__/publicWwwTest/prova file .txt +0 -2
  65. package/__tests__/publicWwwTest/semplicetxt.txt +0 -1
  66. package/__tests__/publicWwwTest/test-page.html +0 -1
  67. package/__tests__/publicWwwTest/test.txt +0 -1
  68. package/__tests__/range-fixtures/sample.txt +0 -1
  69. package/__tests__/range.test.js +0 -223
  70. package/__tests__/security-headers.test.js +0 -165
  71. package/__tests__/security.test.js +0 -322
  72. package/__tests__/server-cache-fixtures/large.txt +0 -1
  73. package/__tests__/server-cache-fixtures/small.txt +0 -1
  74. package/__tests__/server-cache.test.js +0 -594
  75. package/__tests__/setup-benchmark.js +0 -178
  76. package/__tests__/symlink.test.js +0 -441
  77. package/__tests__/template-timeout.test.js +0 -321
  78. package/__tests__/test-regex-quick.js +0 -158
  79. package/__tests__/useOriginalUrl.test.js +0 -213
  80. package/docs/ACTION_PLAN.md +0 -293
  81. package/docs/BENCHMARKS.md +0 -317
  82. package/docs/CHANGELOG.md +0 -906
  83. package/docs/CODE_REVIEW.md +0 -300
  84. package/docs/DEBUG_REPORT.md +0 -593
  85. package/docs/DOCUMENTATION.md +0 -1864
  86. package/docs/EXAMPLES_INDEX_OPTION.md +0 -395
  87. package/docs/FLOW_DIAGRAM.md +0 -954
  88. package/docs/INDEX_OPTION_PRIORITY.md +0 -527
  89. package/docs/OPTIMIZATION_HTTP_CACHING.md +0 -689
  90. package/docs/OPTIMIZATION_ROADMAP_for_V3.md +0 -864
  91. package/docs/PERFORMANCE_ANALYSIS.md +0 -839
  92. package/docs/PERFORMANCE_COMPARISON.md +0 -388
  93. package/docs/noteExports.md +0 -148
  94. package/docs/security_improvement_for_V3.md +0 -421
  95. package/docs/template-engine/TEMPLATE_ENGINE_GUIDE.md +0 -1734
  96. package/docs/template-engine/esempi-incrementali.js +0 -192
  97. package/docs/template-engine/examples/esempio1-nessun-dato.ejs +0 -12
  98. package/docs/template-engine/examples/esempio2-una-variabile.ejs +0 -11
  99. package/docs/template-engine/examples/esempio3-piu-variabili.ejs +0 -15
  100. package/docs/template-engine/examples/esempio4-condizionale.ejs +0 -18
  101. package/docs/template-engine/examples/esempio5-loop.ejs +0 -18
  102. package/docs/template-engine/examples/index-esempi.html +0 -181
  103. package/docs/template-engine/examples/index.html +0 -40
  104. package/docs/template-engine/examples/test.ejs +0 -64
  105. package/eslint.config.mjs +0 -17
  106. package/jest.config.js +0 -18
@@ -1,299 +0,0 @@
1
- const Koa = require('koa');
2
- const koaClassicServer = require('../index.cjs');
3
- const supertest = require('supertest');
4
- const path = require('path');
5
- const fs = require('fs');
6
- const ejs = require('ejs');
7
-
8
- describe('EJS Template Engine Integration Tests', () => {
9
- let app;
10
- let server;
11
- let request;
12
-
13
- beforeAll(() => {
14
- app = new Koa();
15
-
16
- const rootDir = path.join(__dirname, 'publicWwwTest');
17
-
18
- // Configure koaClassicServer with EJS template support
19
- app.use(
20
- koaClassicServer(rootDir, {
21
- method: ['GET'],
22
- dirListing: { enabled: true },
23
- template: {
24
- ext: ['ejs'],
25
- render: async (ctx, next, filePath) => {
26
- // Read the template file
27
- const templateContent = await fs.promises.readFile(filePath, 'utf-8');
28
-
29
- // Prepare data for different templates
30
- const data = getTemplateData(filePath);
31
-
32
- // Render with EJS
33
- const html = ejs.render(templateContent, data);
34
-
35
- ctx.type = 'text/html';
36
- ctx.body = html;
37
- }
38
- }
39
- })
40
- );
41
-
42
- server = app.listen();
43
- request = supertest(server);
44
- });
45
-
46
- afterAll(() => {
47
- if (server) {
48
- server.close();
49
- }
50
- });
51
-
52
- // Helper function to provide data for each template
53
- function getTemplateData(filePath) {
54
- const basename = path.basename(filePath, '.ejs');
55
-
56
- const dataMap = {
57
- 'simple': {
58
- title: 'Simple EJS Test',
59
- heading: 'Hello from EJS',
60
- message: 'This is a simple template test',
61
- timestamp: '2025-11-18'
62
- },
63
- 'with-loop': {
64
- items: [
65
- { name: 'Item 1', value: 100 },
66
- { name: 'Item 2', value: 200 },
67
- { name: 'Item 3', value: 300 }
68
- ]
69
- },
70
- 'with-conditional': {
71
- isLoggedIn: true,
72
- username: 'TestUser',
73
- role: 'admin',
74
- notifications: 5
75
- },
76
- 'with-escaping': {
77
- userInput: '<script>alert("XSS")</script>',
78
- htmlContent: '<strong>Bold text</strong>',
79
- safeText: 'Plain & safe text',
80
- allowedHtml: '<em>Emphasized text</em>'
81
- },
82
- 'complex': {
83
- pageTitle: 'E-Commerce Products',
84
- user: {
85
- name: 'Mario Rossi'
86
- },
87
- products: [
88
- {
89
- id: 1,
90
- name: 'Laptop',
91
- description: 'High-performance laptop',
92
- price: 999.99,
93
- discount: 10,
94
- inStock: true
95
- },
96
- {
97
- id: 2,
98
- name: 'Mouse',
99
- description: 'Wireless mouse',
100
- price: 29.99,
101
- discount: 0,
102
- inStock: true
103
- },
104
- {
105
- id: 3,
106
- name: 'Keyboard',
107
- description: 'Mechanical keyboard',
108
- price: 149.99,
109
- discount: 15,
110
- inStock: false
111
- }
112
- ]
113
- },
114
- 'testEjs': {
115
- // For the existing test file
116
- }
117
- };
118
-
119
- return dataMap[basename] || {};
120
- }
121
-
122
- describe('Simple Template - simple.ejs', () => {
123
- test('Should render simple template with variables', async () => {
124
- const response = await request.get('/ejs-templates/simple.ejs');
125
-
126
- expect(response.status).toBe(200);
127
- expect(response.type).toBe('text/html');
128
- expect(response.text).toContain('Simple EJS Test');
129
- expect(response.text).toContain('Hello from EJS');
130
- expect(response.text).toContain('This is a simple template test');
131
- expect(response.text).toContain('2025-11-18');
132
- });
133
- });
134
-
135
- describe('Loop Template - with-loop.ejs', () => {
136
- test('Should render template with forEach loop', async () => {
137
- const response = await request.get('/ejs-templates/with-loop.ejs');
138
-
139
- expect(response.status).toBe(200);
140
- expect(response.text).toContain('Lista di Items');
141
- expect(response.text).toContain('Item 1 - 100');
142
- expect(response.text).toContain('Item 2 - 200');
143
- expect(response.text).toContain('Item 3 - 300');
144
- expect(response.text).toContain('Totale items: 3');
145
- expect(response.text).toContain('data-index="0"');
146
- expect(response.text).toContain('data-index="1"');
147
- expect(response.text).toContain('data-index="2"');
148
- });
149
- });
150
-
151
- describe('Conditional Template - with-conditional.ejs', () => {
152
- test('Should render template with if/else conditionals (logged in)', async () => {
153
- const response = await request.get('/ejs-templates/with-conditional.ejs');
154
-
155
- expect(response.status).toBe(200);
156
- expect(response.text).toContain('Benvenuto, TestUser!');
157
- expect(response.text).toContain('Ruolo: admin');
158
- expect(response.text).toContain('Hai 5 nuove notifiche');
159
- // Should NOT contain guest panel
160
- expect(response.text).not.toContain('Benvenuto, ospite!');
161
- });
162
- });
163
-
164
- describe('Escaping Template - with-escaping.ejs', () => {
165
- test('Should properly escape HTML in <%= %> tags', async () => {
166
- const response = await request.get('/ejs-templates/with-escaping.ejs');
167
-
168
- expect(response.status).toBe(200);
169
-
170
- // HTML should be escaped (safe)
171
- // Note: EJS uses &#34; instead of &quot; for quotes
172
- expect(response.text).toContain('&lt;script&gt;alert(&#34;XSS&#34;)&lt;/script&gt;');
173
-
174
- // HTML should NOT be escaped (unsafe - unescaped output)
175
- expect(response.text).toContain('<strong>Bold text</strong>');
176
-
177
- // Safe text with & should be escaped
178
- expect(response.text).toContain('Plain &amp; safe text');
179
-
180
- // Allowed HTML should be rendered
181
- expect(response.text).toContain('<em>Emphasized text</em>');
182
- });
183
-
184
- test('Should protect against XSS attacks', async () => {
185
- const response = await request.get('/ejs-templates/with-escaping.ejs');
186
-
187
- // The escaped section should not contain executable script tags
188
- const escapedSection = response.text.match(/<div class="escaped">[\s\S]*?<\/div>/)[0];
189
- expect(escapedSection).not.toContain('<script>');
190
- expect(escapedSection).toContain('&lt;script&gt;');
191
- });
192
- });
193
-
194
- describe('Complex Template - complex.ejs', () => {
195
- test('Should render complex template with all features', async () => {
196
- const response = await request.get('/ejs-templates/complex.ejs');
197
-
198
- expect(response.status).toBe(200);
199
-
200
- // Page title
201
- expect(response.text).toContain('E-Commerce Products');
202
-
203
- // User greeting
204
- expect(response.text).toContain('Ciao, Mario Rossi!');
205
-
206
- // Products list
207
- expect(response.text).toContain('Laptop');
208
- expect(response.text).toContain('High-performance laptop');
209
- expect(response.text).toContain('€999.99');
210
-
211
- // Discount calculation
212
- expect(response.text).toContain('Sconto: 10%');
213
- expect(response.text).toContain('€899.99'); // 999.99 - 10%
214
-
215
- // Mouse without discount
216
- expect(response.text).toContain('Mouse');
217
- expect(response.text).toContain('€29.99');
218
-
219
- // Keyboard out of stock
220
- expect(response.text).toContain('Keyboard');
221
- expect(response.text).toContain('Non disponibile');
222
-
223
- // Total count
224
- expect(response.text).toContain('Totale prodotti: 3');
225
- });
226
-
227
- test('Should have correct product data attributes', async () => {
228
- const response = await request.get('/ejs-templates/complex.ejs');
229
-
230
- expect(response.text).toContain('data-id="1"');
231
- expect(response.text).toContain('data-id="2"');
232
- expect(response.text).toContain('data-id="3"');
233
- });
234
-
235
- test('Should show "Add to cart" button only for in-stock items', async () => {
236
- const response = await request.get('/ejs-templates/complex.ejs');
237
-
238
- // Count "Aggiungi al carrello" buttons - should be 2 (Laptop and Mouse)
239
- const buttonMatches = response.text.match(/Aggiungi al carrello/g);
240
- expect(buttonMatches).toHaveLength(2);
241
-
242
- // Count "Non disponibile" - should be 1 (Keyboard)
243
- const outOfStockMatches = response.text.match(/Non disponibile/g);
244
- expect(outOfStockMatches).toHaveLength(1);
245
- });
246
- });
247
-
248
- describe('Index Template - index.ejs', () => {
249
- test('Should render index page with links', async () => {
250
- const response = await request.get('/ejs-templates/index.ejs');
251
-
252
- expect(response.status).toBe(200);
253
- expect(response.text).toContain('Test Templates EJS');
254
- expect(response.text).toContain('simple.ejs');
255
- expect(response.text).toContain('with-loop.ejs');
256
- expect(response.text).toContain('with-conditional.ejs');
257
- expect(response.text).toContain('with-escaping.ejs');
258
- expect(response.text).toContain('complex.ejs');
259
- });
260
- });
261
-
262
- describe('Existing EJS file - testEjs.ejs', () => {
263
- test('Should render existing testEjs.ejs file', async () => {
264
- const response = await request.get('/cartella/sottocartella/provaEjs/testEjs.ejs');
265
-
266
- expect(response.status).toBe(200);
267
- expect(response.type).toBe('text/html');
268
- expect(response.text).toContain('<h1>hello world</h1>');
269
- });
270
- });
271
-
272
- describe('Error Handling', () => {
273
- test('Should return 404 for non-existent .ejs file', async () => {
274
- const response = await request.get('/ejs-templates/non-existent.ejs');
275
-
276
- expect(response.status).toBe(404);
277
- });
278
- });
279
-
280
- describe('Performance', () => {
281
- test('Should render complex template in reasonable time', async () => {
282
- const start = Date.now();
283
- const response = await request.get('/ejs-templates/complex.ejs');
284
- const duration = Date.now() - start;
285
-
286
- expect(response.status).toBe(200);
287
- expect(duration).toBeLessThan(100); // Should render in less than 100ms
288
- });
289
-
290
- test('Should render simple template very quickly', async () => {
291
- const start = Date.now();
292
- const response = await request.get('/ejs-templates/simple.ejs');
293
- const duration = Date.now() - start;
294
-
295
- expect(response.status).toBe(200);
296
- expect(duration).toBeLessThan(50); // Should render in less than 50ms
297
- });
298
- });
299
- });
@@ -1,160 +0,0 @@
1
- const Koa = require('koa');
2
- const koaClassicServer = require('../index.cjs');
3
- const supertest = require('supertest');
4
- const path = require('path');
5
- const fs = require('fs');
6
- const ejs = require('ejs');
7
-
8
- // Regression tests for the HEAD-method HTTP-conformance bug.
9
- //
10
- // RFC 9110 §9.3.2: a HEAD response must be identical to the GET response for the
11
- // same resource, minus the message body — same status code, same headers
12
- // (notably Content-Type and Content-Length).
13
- //
14
- // Before the fix, a route served by the template engine returned 200 on GET but
15
- // 404 on HEAD whenever the operator's render function did not itself handle HEAD
16
- // (e.g. it returned early on non-GET requests). The static-file branch already
17
- // handled HEAD correctly, and directory listings happened to work because Koa
18
- // strips the string body for HEAD — only the template branch was broken.
19
-
20
- const ROOT = path.join(__dirname, 'publicWwwTest');
21
-
22
- // Minimal data for the templates exercised here. ejs-templates/index.ejs needs no
23
- // variables; simple.ejs needs these four. Anything else renders with no locals.
24
- function templateData(filePath) {
25
- if (path.basename(filePath, '.ejs') === 'simple') {
26
- return {
27
- title: 'Simple EJS Test',
28
- heading: 'Hello from EJS',
29
- message: 'This is a simple template test',
30
- timestamp: '2025-11-18',
31
- };
32
- }
33
- return {};
34
- }
35
-
36
- // A method-AWARE render: it only produces a body on GET. This is a common
37
- // real-world pattern (operators guard render work behind a GET check) and is
38
- // exactly what exposed the bug — on HEAD it never set ctx.body, so the status
39
- // stayed at Koa's default 404. This render is what makes the regression real:
40
- // with a naive render the test would pass even without the fix, because Koa
41
- // strips the body of a 200 GET response for HEAD on its own.
42
- const methodAwareRender = async (ctx, next, filePath) => {
43
- if (ctx.method !== 'GET') return;
44
- const tpl = await fs.promises.readFile(filePath, 'utf-8');
45
- ctx.type = 'text/html';
46
- ctx.body = ejs.render(tpl, templateData(filePath));
47
- };
48
-
49
- // A method-AGNOSTIC render: sets the body regardless of method. Proves the fix
50
- // does not regress renders that already worked.
51
- const naiveRender = async (ctx, next, filePath) => {
52
- const tpl = await fs.promises.readFile(filePath, 'utf-8');
53
- ctx.type = 'text/html';
54
- ctx.body = ejs.render(tpl, templateData(filePath));
55
- };
56
-
57
- function buildServer(render) {
58
- const app = new Koa();
59
- app.use(
60
- koaClassicServer(ROOT, {
61
- method: ['GET', 'HEAD'],
62
- index: ['index.html', 'index.ejs'],
63
- dirListing: { enabled: true },
64
- template: { ext: ['ejs'], render },
65
- })
66
- );
67
- return app.listen();
68
- }
69
-
70
- // Asserts the HEAD response for `urlPath` matches the corresponding GET in status,
71
- // Content-Type and Content-Length, and that HEAD carries no body.
72
- async function expectHeadMatchesGet(request, urlPath, expectedStatus) {
73
- const get = await request.get(urlPath);
74
- const head = await request.head(urlPath);
75
-
76
- expect(get.status).toBe(expectedStatus);
77
- expect(head.status).toBe(expectedStatus);
78
-
79
- // Same headers as GET (RFC 9110 §9.3.2)
80
- expect(head.headers['content-type']).toBe(get.headers['content-type']);
81
- expect(head.headers['content-length']).toBe(get.headers['content-length']);
82
- // Content-Length must actually be populated for a body-bearing response
83
- expect(head.headers['content-length']).toBeDefined();
84
-
85
- // HEAD must not send a body (supertest surfaces an empty object for HEAD)
86
- expect(head.text).toBeFalsy();
87
- expect(head.body).toEqual({});
88
-
89
- return { get, head };
90
- }
91
-
92
- describe('HEAD method — template engine routes (method-aware render)', () => {
93
- let server;
94
- let request;
95
- beforeAll(() => { server = buildServer(methodAwareRender); request = supertest(server); });
96
- afterAll(() => server.close());
97
-
98
- // The headline bug: a directory whose index is a template (index.ejs as the
99
- // index of /ejs-templates/). GET 200, HEAD must also be 200 — not 404.
100
- test('HEAD on a directory whose index is a template → 200, matches GET', async () => {
101
- const { get } = await expectHeadMatchesGet(request, '/ejs-templates/', 200);
102
- // sanity: GET really did render the template
103
- expect(get.text).toContain('Test Templates EJS');
104
- expect(get.headers['content-type']).toMatch(/text\/html/);
105
- });
106
-
107
- test('HEAD on a directly-requested template file → 200, matches GET', async () => {
108
- const { get } = await expectHeadMatchesGet(request, '/ejs-templates/simple.ejs', 200);
109
- expect(get.text).toContain('Hello from EJS');
110
- });
111
-
112
- test('HEAD on a static file → 200, matches GET', async () => {
113
- const { head } = await expectHeadMatchesGet(request, '/test.txt', 200);
114
- // static branch advertises range support
115
- expect(head.headers['accept-ranges']).toBe('bytes');
116
- });
117
-
118
- test('HEAD on a listable directory (no index) → 200, matches GET', async () => {
119
- const { get } = await expectHeadMatchesGet(request, '/cartella/', 200);
120
- expect(get.headers['content-type']).toMatch(/text\/html/);
121
- });
122
-
123
- test('HEAD on a non-existent template path → 404, matches GET', async () => {
124
- const get = await request.get('/ejs-templates/non-existent.ejs');
125
- const head = await request.head('/ejs-templates/non-existent.ejs');
126
- expect(get.status).toBe(404);
127
- expect(head.status).toBe(404);
128
- expect(head.headers['content-type']).toBe(get.headers['content-type']);
129
- expect(head.text).toBeFalsy();
130
- });
131
-
132
- test('HEAD on a non-existent static path → 404, matches GET', async () => {
133
- const get = await request.get('/does-not-exist.txt');
134
- const head = await request.head('/does-not-exist.txt');
135
- expect(get.status).toBe(404);
136
- expect(head.status).toBe(404);
137
- expect(head.text).toBeFalsy();
138
- });
139
- });
140
-
141
- describe('HEAD method — template engine routes (method-agnostic render, no regression)', () => {
142
- let server;
143
- let request;
144
- beforeAll(() => { server = buildServer(naiveRender); request = supertest(server); });
145
- afterAll(() => server.close());
146
-
147
- test('HEAD on template index still matches GET → 200', async () => {
148
- await expectHeadMatchesGet(request, '/ejs-templates/', 200);
149
- });
150
-
151
- test('HEAD on direct template still matches GET → 200', async () => {
152
- await expectHeadMatchesGet(request, '/ejs-templates/simple.ejs', 200);
153
- });
154
-
155
- test('HEAD on non-existent template → 404', async () => {
156
- const head = await request.head('/ejs-templates/non-existent.ejs');
157
- expect(head.status).toBe(404);
158
- expect(head.text).toBeFalsy();
159
- });
160
- });
@@ -1 +0,0 @@
1
- dot dir file
@@ -1,2 +0,0 @@
1
- SECRET_KEY=mysecretvalue
2
- DB_PASSWORD=hidden123
@@ -1 +0,0 @@
1
- key data
@@ -1 +0,0 @@
1
- secret data
@@ -1 +0,0 @@
1
- regular
@@ -1 +0,0 @@
1
- normal
@@ -1 +0,0 @@
1
- NESTED_SECRET=nestedvalue
@@ -1 +0,0 @@
1
- regular in subdir