jsgui3-server 0.0.138 → 0.0.140
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/AGENTS.md +87 -0
- package/README.md +12 -0
- package/docs/GUIDE_TO_AGENTIC_WORKFLOWS_BY_GROK.md +19 -0
- package/docs/advanced-usage-examples.md +1360 -0
- package/docs/agent-development-guide.md +386 -0
- package/docs/api-reference.md +916 -0
- package/docs/broken-functionality-tracker.md +285 -0
- package/docs/bundling-system-deep-dive.md +525 -0
- package/docs/cli-reference.md +393 -0
- package/docs/comprehensive-documentation.md +1403 -0
- package/docs/configuration-reference.md +808 -0
- package/docs/controls-development.md +859 -0
- package/docs/documentation-review/CURRENT_REVIEW.md +95 -0
- package/docs/function-publishers-json-apis.md +847 -0
- package/docs/getting-started-with-json.md +518 -0
- package/docs/minification-compression-sourcemaps-status.md +482 -0
- package/docs/minification-compression-sourcemaps-test-results.md +205 -0
- package/docs/publishers-guide.md +313 -0
- package/docs/resources-guide.md +615 -0
- package/docs/serve-helpers.md +406 -0
- package/docs/simple-server-api-design.md +13 -0
- package/docs/system-architecture.md +275 -0
- package/docs/troubleshooting.md +698 -0
- package/examples/json/README.md +115 -0
- package/examples/json/basic-api/README.md +345 -0
- package/examples/json/basic-api/server.js +199 -0
- package/examples/json/simple-api/README.md +125 -0
- package/examples/json/simple-api/diagnostic-report.json +73 -0
- package/examples/json/simple-api/diagnostic-test.js +433 -0
- package/examples/json/simple-api/server-debug.md +58 -0
- package/examples/json/simple-api/server.js +91 -0
- package/examples/json/simple-api/test.js +215 -0
- package/http/responders/static/Static_Route_HTTP_Responder.js +1 -2
- package/package.json +19 -8
- package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +65 -12
- package/publishers/helpers/preparers/static/bundle/Static_Routes_Responses_Webpage_Bundle_Preparer.js +6 -1
- package/publishers/http-function-publisher.js +59 -38
- package/publishers/http-webpage-publisher.js +48 -1
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +38 -146
- package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +54 -5
- package/resources/processors/bundlers/js/esbuild/Core_JS_Single_File_Minifying_Bundler_Using_ESBuild.js +36 -4
- package/serve-factory.js +36 -9
- package/server.js +10 -4
- package/test-report.json +0 -0
- package/tests/README.md +250 -0
- package/tests/assigners.test.js +316 -0
- package/tests/bundlers.test.js +329 -0
- package/tests/configuration-validation.test.js +530 -0
- package/tests/content-analysis.test.js +641 -0
- package/tests/end-to-end.test.js +496 -0
- package/tests/error-handling.test.js +746 -0
- package/tests/performance.test.js +653 -0
- package/tests/publishers.test.js +395 -0
- package/tests/temp_invalid.js +7 -0
- package/tests/temp_invalid_utf8.js +1 -0
- package/tests/temp_malformed.js +10 -0
- package/tests/test-runner.js +261 -0
package/tests/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# JSGUI3 Minification, Compression & Sourcemaps Test Suite
|
|
2
|
+
|
|
3
|
+
This comprehensive test suite validates the minification, compression, and sourcemap features implemented in JSGUI3 Server.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The test suite covers all aspects of the Phase 1 implementation:
|
|
8
|
+
|
|
9
|
+
- **Component Isolation Tests**: Test each bundler, assigner, and publisher in isolation
|
|
10
|
+
- **Configuration Validation Tests**: Test all configuration options and validation
|
|
11
|
+
- **End-to-End Integration Tests**: Run full server and test HTML/CSS/JS serving
|
|
12
|
+
- **Content Analysis Tests**: Examine served content for proper minification, compression, and sourcemaps
|
|
13
|
+
- **Performance Tests**: Test compression ratios and response sizes
|
|
14
|
+
- **Error Handling Tests**: Test invalid configurations and edge cases
|
|
15
|
+
|
|
16
|
+
## Test Structure
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
tests/
|
|
20
|
+
├── bundlers.test.js # Component isolation tests for bundlers
|
|
21
|
+
├── assigners.test.js # Component isolation tests for assigners
|
|
22
|
+
├── publishers.test.js # Component isolation tests for publishers
|
|
23
|
+
├── configuration-validation.test.js # Configuration validation tests
|
|
24
|
+
├── end-to-end.test.js # Full integration tests
|
|
25
|
+
├── content-analysis.test.js # Content analysis and verification
|
|
26
|
+
├── performance.test.js # Performance benchmarks
|
|
27
|
+
├── error-handling.test.js # Error handling and edge cases
|
|
28
|
+
├── test-runner.js # Custom test runner with reporting
|
|
29
|
+
└── README.md # This file
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Running Tests
|
|
33
|
+
|
|
34
|
+
### Run All Tests
|
|
35
|
+
```bash
|
|
36
|
+
npm test
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Run Specific Test Suite
|
|
40
|
+
```bash
|
|
41
|
+
# Using the custom test runner
|
|
42
|
+
node tests/test-runner.js --test=bundlers.test.js
|
|
43
|
+
|
|
44
|
+
# Using mocha directly
|
|
45
|
+
npx mocha tests/bundlers.test.js
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Run Tests with Options
|
|
49
|
+
```bash
|
|
50
|
+
# Debug mode (enables sourcemaps)
|
|
51
|
+
node tests/test-runner.js --debug
|
|
52
|
+
|
|
53
|
+
# Verbose output
|
|
54
|
+
node tests/test-runner.js --verbose
|
|
55
|
+
|
|
56
|
+
# Specific test with debug
|
|
57
|
+
node tests/test-runner.js --test=end-to-end.test.js --debug
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Test Coverage
|
|
61
|
+
|
|
62
|
+
### 1. Component Isolation Tests (`bundlers.test.js`, `assigners.test.js`, `publishers.test.js`)
|
|
63
|
+
|
|
64
|
+
Test each component independently:
|
|
65
|
+
|
|
66
|
+
- **Bundlers**: ESBuild integration, minification levels, sourcemap generation
|
|
67
|
+
- **Assigners**: Compression algorithms, statistics tracking, threshold handling
|
|
68
|
+
- **Publishers**: Configuration validation, component integration
|
|
69
|
+
|
|
70
|
+
### 2. Configuration Validation Tests (`configuration-validation.test.js`)
|
|
71
|
+
|
|
72
|
+
Validate all configuration options:
|
|
73
|
+
|
|
74
|
+
- Minification levels (`conservative`, `normal`, `aggressive`)
|
|
75
|
+
- Compression algorithms (`gzip`, `br`) and settings
|
|
76
|
+
- Sourcemap formats (`inline`, `external`) and conditions
|
|
77
|
+
- Threshold and exclusion rules
|
|
78
|
+
- Type validation and error messages
|
|
79
|
+
|
|
80
|
+
### 3. End-to-End Integration Tests (`end-to-end.test.js`)
|
|
81
|
+
|
|
82
|
+
Full server integration testing:
|
|
83
|
+
|
|
84
|
+
- HTTP server startup with various configurations
|
|
85
|
+
- Content negotiation (gzip/br/identity)
|
|
86
|
+
- Minification and compression pipeline
|
|
87
|
+
- Concurrent request handling
|
|
88
|
+
- Error scenarios (port conflicts, invalid configs)
|
|
89
|
+
|
|
90
|
+
### 4. Content Analysis Tests (`content-analysis.test.js`)
|
|
91
|
+
|
|
92
|
+
Verify content integrity and correctness:
|
|
93
|
+
|
|
94
|
+
- Minification effectiveness and ratios
|
|
95
|
+
- Compression integrity (decompression verification)
|
|
96
|
+
- Sourcemap generation and validation
|
|
97
|
+
- CSS extraction and preservation
|
|
98
|
+
- Bundle content structure
|
|
99
|
+
|
|
100
|
+
### 5. Performance Tests (`performance.test.js`)
|
|
101
|
+
|
|
102
|
+
Benchmark and analyze performance:
|
|
103
|
+
|
|
104
|
+
- Bundling speed across different file sizes
|
|
105
|
+
- Compression performance (gzip vs brotli)
|
|
106
|
+
- Memory usage analysis
|
|
107
|
+
- Server response times
|
|
108
|
+
- Concurrent operation handling
|
|
109
|
+
|
|
110
|
+
### 6. Error Handling Tests (`error-handling.test.js`)
|
|
111
|
+
|
|
112
|
+
Comprehensive error scenario testing:
|
|
113
|
+
|
|
114
|
+
- Invalid JavaScript syntax
|
|
115
|
+
- File system errors (permissions, missing files)
|
|
116
|
+
- Configuration validation errors
|
|
117
|
+
- Network and HTTP errors
|
|
118
|
+
- Memory and performance limits
|
|
119
|
+
- Encoding issues
|
|
120
|
+
|
|
121
|
+
## Configuration Examples
|
|
122
|
+
|
|
123
|
+
### Basic Minification
|
|
124
|
+
```javascript
|
|
125
|
+
Server.serve({
|
|
126
|
+
ctrl: MyControl,
|
|
127
|
+
debug: false, // Enables minification
|
|
128
|
+
bundler: {
|
|
129
|
+
minify: {
|
|
130
|
+
enabled: true,
|
|
131
|
+
level: 'normal'
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Full Optimization Pipeline
|
|
138
|
+
```javascript
|
|
139
|
+
Server.serve({
|
|
140
|
+
ctrl: MyControl,
|
|
141
|
+
debug: false,
|
|
142
|
+
bundler: {
|
|
143
|
+
minify: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
level: 'aggressive',
|
|
146
|
+
options: {
|
|
147
|
+
drop_console: true
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
sourcemaps: {
|
|
151
|
+
enabled: true,
|
|
152
|
+
format: 'inline'
|
|
153
|
+
},
|
|
154
|
+
compression: {
|
|
155
|
+
enabled: true,
|
|
156
|
+
algorithms: ['gzip', 'br'],
|
|
157
|
+
threshold: 1024
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Test Results and Reporting
|
|
164
|
+
|
|
165
|
+
The test runner generates:
|
|
166
|
+
|
|
167
|
+
- **Console Output**: Real-time test progress and results
|
|
168
|
+
- **JSON Report**: Detailed results saved to `test-report.json`
|
|
169
|
+
- **Summary Statistics**: Pass/fail counts, success rates, timing
|
|
170
|
+
|
|
171
|
+
### Sample Output
|
|
172
|
+
```
|
|
173
|
+
🚀 Starting JSGUI3 Minification, Compression & Sourcemaps Test Suite
|
|
174
|
+
|
|
175
|
+
================================================================================
|
|
176
|
+
📋 Running bundlers.test.js...
|
|
177
|
+
✅ bundlers.test.js passed
|
|
178
|
+
📋 Running assigners.test.js...
|
|
179
|
+
✅ assigners.test.js passed
|
|
180
|
+
...
|
|
181
|
+
================================================================================
|
|
182
|
+
📊 TEST SUMMARY
|
|
183
|
+
================================================================================
|
|
184
|
+
Total Test Suites: 8
|
|
185
|
+
✅ Passed: 8
|
|
186
|
+
❌ Failed: 0
|
|
187
|
+
⚠️ Skipped: 0
|
|
188
|
+
⏱️ Duration: 45.23s
|
|
189
|
+
📈 Success Rate: 100.0%
|
|
190
|
+
|
|
191
|
+
🎉 All tests passed! The minification, compression, and sourcemap features are working correctly.
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Environment Requirements
|
|
195
|
+
|
|
196
|
+
- Node.js >= 15.0.0
|
|
197
|
+
- Mocha test framework (included in package.json)
|
|
198
|
+
- ESBuild (included in package.json)
|
|
199
|
+
- zlib (built-in Node.js module)
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
### Common Issues
|
|
204
|
+
|
|
205
|
+
1. **Port conflicts**: Tests use specific ports (3001-3005). Ensure these are available.
|
|
206
|
+
|
|
207
|
+
2. **Memory issues**: Large content tests may require increased Node.js memory:
|
|
208
|
+
```bash
|
|
209
|
+
node --max-old-space-size=4096 tests/test-runner.js
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
3. **File permissions**: Tests create temporary files. Ensure write permissions in the tests directory.
|
|
213
|
+
|
|
214
|
+
4. **Slow tests**: Performance tests may take longer on resource-constrained systems.
|
|
215
|
+
|
|
216
|
+
### Debug Mode
|
|
217
|
+
|
|
218
|
+
Enable debug mode for additional logging:
|
|
219
|
+
```bash
|
|
220
|
+
export JSGUI_DEBUG=1
|
|
221
|
+
node tests/test-runner.js
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Contributing
|
|
225
|
+
|
|
226
|
+
When adding new tests:
|
|
227
|
+
|
|
228
|
+
1. Follow the existing naming convention: `*.test.js`
|
|
229
|
+
2. Add the new test file to `test-runner.js`
|
|
230
|
+
3. Update this README with new test descriptions
|
|
231
|
+
4. Ensure tests are isolated and don't interfere with each other
|
|
232
|
+
5. Include both positive and negative test cases
|
|
233
|
+
|
|
234
|
+
## Integration with CI/CD
|
|
235
|
+
|
|
236
|
+
The test suite is designed to work with continuous integration:
|
|
237
|
+
|
|
238
|
+
```yaml
|
|
239
|
+
# Example GitHub Actions
|
|
240
|
+
- name: Run Test Suite
|
|
241
|
+
run: npm test
|
|
242
|
+
|
|
243
|
+
- name: Upload Test Results
|
|
244
|
+
uses: actions/upload-artifact@v2
|
|
245
|
+
with:
|
|
246
|
+
name: test-results
|
|
247
|
+
path: test-report.json
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
This ensures the minification, compression, and sourcemap features maintain their functionality across code changes.
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const { describe, it, beforeEach, afterEach } = require('mocha');
|
|
3
|
+
|
|
4
|
+
// Import assigner classes
|
|
5
|
+
const Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner = require('../publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner');
|
|
6
|
+
|
|
7
|
+
describe('Assigner Component Isolation Tests', function() {
|
|
8
|
+
this.timeout(10000); // Increase timeout for compression operations
|
|
9
|
+
|
|
10
|
+
let mockBundleItems;
|
|
11
|
+
|
|
12
|
+
beforeEach(function() {
|
|
13
|
+
// Create mock bundle items for testing
|
|
14
|
+
mockBundleItems = [
|
|
15
|
+
{
|
|
16
|
+
type: 'HTML',
|
|
17
|
+
extension: 'html',
|
|
18
|
+
text: '<!DOCTYPE html><html><head><title>Test</title></head><body><h1>Hello World</h1></body></html>',
|
|
19
|
+
response_buffers: {}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'JavaScript',
|
|
23
|
+
extension: 'js',
|
|
24
|
+
text: 'function test(){console.log("test");return"result";}test();',
|
|
25
|
+
response_buffers: {}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'CSS',
|
|
29
|
+
extension: 'css',
|
|
30
|
+
text: '.test-class{color:red;font-size:14px;}.another-class{background:blue;}',
|
|
31
|
+
response_buffers: {}
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Initialize identity buffers (simulate what uncompressed assigner would do)
|
|
36
|
+
mockBundleItems.forEach(item => {
|
|
37
|
+
item.response_buffers.identity = Buffer.from(item.text, 'utf8');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner', function() {
|
|
42
|
+
it('should compress content with default gzip settings', async function() {
|
|
43
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
|
|
44
|
+
|
|
45
|
+
await assigner.assign(mockBundleItems);
|
|
46
|
+
|
|
47
|
+
// Verify gzip compression was applied
|
|
48
|
+
mockBundleItems.forEach(item => {
|
|
49
|
+
assert(item.response_buffers.gzip, `Item ${item.type} should have gzip buffer`);
|
|
50
|
+
assert(Buffer.isBuffer(item.response_buffers.gzip), 'Gzip buffer should be a Buffer');
|
|
51
|
+
|
|
52
|
+
// Compressed content should be smaller than original (for non-trivial content)
|
|
53
|
+
if (item.response_buffers.identity.length > 100) {
|
|
54
|
+
assert(item.response_buffers.gzip.length <= item.response_buffers.identity.length,
|
|
55
|
+
`Gzip compressed ${item.type} should be smaller or equal size`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Verify compression statistics
|
|
60
|
+
assert.strictEqual(assigner.compression_stats.total_items, 3, 'Should track 3 total items');
|
|
61
|
+
assert.strictEqual(assigner.compression_stats.gzip_compressed, 3, 'Should have compressed 3 items with gzip');
|
|
62
|
+
assert(assigner.compression_stats.gzip_savings >= 0, 'Gzip savings should be non-negative');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should compress content with default brotli settings', async function() {
|
|
66
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
|
|
67
|
+
|
|
68
|
+
await assigner.assign(mockBundleItems);
|
|
69
|
+
|
|
70
|
+
// Verify brotli compression was applied
|
|
71
|
+
mockBundleItems.forEach(item => {
|
|
72
|
+
assert(item.response_buffers.br, `Item ${item.type} should have brotli buffer`);
|
|
73
|
+
assert(Buffer.isBuffer(item.response_buffers.br), 'Brotli buffer should be a Buffer');
|
|
74
|
+
|
|
75
|
+
// Compressed content should be smaller than original (for non-trivial content)
|
|
76
|
+
if (item.response_buffers.identity.length > 100) {
|
|
77
|
+
assert(item.response_buffers.br.length <= item.response_buffers.identity.length,
|
|
78
|
+
`Brotli compressed ${item.type} should be smaller or equal size`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Verify compression statistics
|
|
83
|
+
assert.strictEqual(assigner.compression_stats.brotli_compressed, 3, 'Should have compressed 3 items with brotli');
|
|
84
|
+
assert(assigner.compression_stats.brotli_savings >= 0, 'Brotli savings should be non-negative');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should respect compression configuration - gzip only', async function() {
|
|
88
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
89
|
+
compression: {
|
|
90
|
+
enabled: true,
|
|
91
|
+
algorithms: ['gzip']
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await assigner.assign(mockBundleItems);
|
|
96
|
+
|
|
97
|
+
// Should have gzip but not brotli
|
|
98
|
+
mockBundleItems.forEach(item => {
|
|
99
|
+
assert(item.response_buffers.gzip, `Item ${item.type} should have gzip buffer`);
|
|
100
|
+
assert(!item.response_buffers.br, `Item ${item.type} should not have brotli buffer`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
assert.strictEqual(assigner.compression_stats.gzip_compressed, 3);
|
|
104
|
+
assert.strictEqual(assigner.compression_stats.brotli_compressed, 0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should respect compression configuration - brotli only', async function() {
|
|
108
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
109
|
+
compression: {
|
|
110
|
+
enabled: true,
|
|
111
|
+
algorithms: ['br']
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await assigner.assign(mockBundleItems);
|
|
116
|
+
|
|
117
|
+
// Should have brotli but not gzip
|
|
118
|
+
mockBundleItems.forEach(item => {
|
|
119
|
+
assert(!item.response_buffers.gzip, `Item ${item.type} should not have gzip buffer`);
|
|
120
|
+
assert(item.response_buffers.br, `Item ${item.type} should have brotli buffer`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
assert.strictEqual(assigner.compression_stats.gzip_compressed, 0);
|
|
124
|
+
assert.strictEqual(assigner.compression_stats.brotli_compressed, 3);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should respect compression configuration - custom gzip level', async function() {
|
|
128
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
129
|
+
compression: {
|
|
130
|
+
enabled: true,
|
|
131
|
+
algorithms: ['gzip'],
|
|
132
|
+
gzip: { level: 1 } // Fastest compression
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await assigner.assign(mockBundleItems);
|
|
137
|
+
|
|
138
|
+
// Should have gzip with custom level
|
|
139
|
+
mockBundleItems.forEach(item => {
|
|
140
|
+
assert(item.response_buffers.gzip, `Item ${item.type} should have gzip buffer`);
|
|
141
|
+
assert(!item.response_buffers.br, `Item ${item.type} should not have brotli buffer`);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should respect compression configuration - custom brotli quality', async function() {
|
|
146
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
147
|
+
compression: {
|
|
148
|
+
enabled: true,
|
|
149
|
+
algorithms: ['br'],
|
|
150
|
+
brotli: { quality: 1 } // Lowest quality (fastest)
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
await assigner.assign(mockBundleItems);
|
|
155
|
+
|
|
156
|
+
// Should have brotli with custom quality
|
|
157
|
+
mockBundleItems.forEach(item => {
|
|
158
|
+
assert(!item.response_buffers.gzip, `Item ${item.type} should not have gzip buffer`);
|
|
159
|
+
assert(item.response_buffers.br, `Item ${item.type} should have brotli buffer`);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should skip compression when disabled', async function() {
|
|
164
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
165
|
+
compression: {
|
|
166
|
+
enabled: false
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await assigner.assign(mockBundleItems);
|
|
171
|
+
|
|
172
|
+
// Should not add any compressed buffers
|
|
173
|
+
mockBundleItems.forEach(item => {
|
|
174
|
+
assert(!item.response_buffers.gzip, `Item ${item.type} should not have gzip buffer when disabled`);
|
|
175
|
+
assert(!item.response_buffers.br, `Item ${item.type} should not have brotli buffer when disabled`);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Statistics should be zero
|
|
179
|
+
assert.strictEqual(assigner.compression_stats.total_items, 0);
|
|
180
|
+
assert.strictEqual(assigner.compression_stats.gzip_compressed, 0);
|
|
181
|
+
assert.strictEqual(assigner.compression_stats.brotli_compressed, 0);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should respect compression threshold', async function() {
|
|
185
|
+
// Create a very small item that should be below threshold
|
|
186
|
+
const smallItem = {
|
|
187
|
+
type: 'JavaScript',
|
|
188
|
+
extension: 'js',
|
|
189
|
+
text: 'x=1', // Very small content
|
|
190
|
+
response_buffers: {
|
|
191
|
+
identity: Buffer.from('x=1', 'utf8')
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
196
|
+
compression: {
|
|
197
|
+
enabled: true,
|
|
198
|
+
threshold: 1000 // High threshold
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await assigner.assign([smallItem]);
|
|
203
|
+
|
|
204
|
+
// Small item should be skipped
|
|
205
|
+
assert(!smallItem.response_buffers.gzip, 'Small item should not be gzip compressed');
|
|
206
|
+
assert(!smallItem.response_buffers.br, 'Small item should not be brotli compressed');
|
|
207
|
+
|
|
208
|
+
// Statistics should reflect skipping
|
|
209
|
+
assert.strictEqual(assigner.compression_stats.total_items, 1);
|
|
210
|
+
assert.strictEqual(assigner.compression_stats.gzip_compressed, 0);
|
|
211
|
+
assert.strictEqual(assigner.compression_stats.brotli_compressed, 0);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should handle items without text property', async function() {
|
|
215
|
+
const itemWithoutText = {
|
|
216
|
+
type: 'Image',
|
|
217
|
+
extension: 'png',
|
|
218
|
+
response_buffers: {
|
|
219
|
+
identity: Buffer.from('fake image data', 'utf8')
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
|
|
224
|
+
|
|
225
|
+
await assigner.assign([itemWithoutText]);
|
|
226
|
+
|
|
227
|
+
// Should not crash and should not add compression
|
|
228
|
+
assert(!itemWithoutText.response_buffers.gzip);
|
|
229
|
+
assert(!itemWithoutText.response_buffers.br);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should handle empty bundle array', async function() {
|
|
233
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
|
|
234
|
+
|
|
235
|
+
await assigner.assign([]);
|
|
236
|
+
|
|
237
|
+
// Should not crash
|
|
238
|
+
assert.strictEqual(assigner.compression_stats.total_items, 0);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should calculate compression ratios correctly', async function() {
|
|
242
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
243
|
+
compression: {
|
|
244
|
+
enabled: true,
|
|
245
|
+
algorithms: ['gzip']
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Use a larger text that will definitely compress
|
|
250
|
+
const largeText = 'console.log("'.repeat(1000) + '");';
|
|
251
|
+
const largeItem = {
|
|
252
|
+
type: 'JavaScript',
|
|
253
|
+
extension: 'js',
|
|
254
|
+
text: largeText,
|
|
255
|
+
response_buffers: {
|
|
256
|
+
identity: Buffer.from(largeText, 'utf8')
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
await assigner.assign([largeItem]);
|
|
261
|
+
|
|
262
|
+
// Verify compression savings calculation
|
|
263
|
+
const originalSize = largeItem.response_buffers.identity.length;
|
|
264
|
+
const compressedSize = largeItem.response_buffers.gzip.length;
|
|
265
|
+
const expectedSavings = originalSize - compressedSize;
|
|
266
|
+
|
|
267
|
+
assert.strictEqual(assigner.compression_stats.gzip_savings, expectedSavings);
|
|
268
|
+
assert(expectedSavings > 0, 'Should have compression savings for repetitive content');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should handle invalid compression configuration gracefully', async function() {
|
|
272
|
+
// Test with invalid algorithm
|
|
273
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner({
|
|
274
|
+
compression: {
|
|
275
|
+
enabled: true,
|
|
276
|
+
algorithms: ['invalid_algorithm']
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
await assigner.assign(mockBundleItems);
|
|
281
|
+
|
|
282
|
+
// Should not compress anything due to invalid algorithm
|
|
283
|
+
mockBundleItems.forEach(item => {
|
|
284
|
+
assert(!item.response_buffers.gzip, 'Should not compress with invalid algorithm');
|
|
285
|
+
assert(!item.response_buffers.br, 'Should not compress with invalid algorithm');
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('Compression Statistics', function() {
|
|
291
|
+
it('should track compression statistics accurately', async function() {
|
|
292
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
|
|
293
|
+
|
|
294
|
+
await assigner.assign(mockBundleItems);
|
|
295
|
+
|
|
296
|
+
// Verify all statistics are tracked
|
|
297
|
+
assert.strictEqual(assigner.compression_stats.total_items, 3);
|
|
298
|
+
assert.strictEqual(assigner.compression_stats.gzip_compressed, 3);
|
|
299
|
+
assert.strictEqual(assigner.compression_stats.brotli_compressed, 3);
|
|
300
|
+
assert(typeof assigner.compression_stats.gzip_savings === 'number');
|
|
301
|
+
assert(typeof assigner.compression_stats.brotli_savings === 'number');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should reset statistics between assign calls', async function() {
|
|
305
|
+
const assigner = new Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner();
|
|
306
|
+
|
|
307
|
+
// First assign
|
|
308
|
+
await assigner.assign(mockBundleItems.slice(0, 1));
|
|
309
|
+
assert.strictEqual(assigner.compression_stats.total_items, 1);
|
|
310
|
+
|
|
311
|
+
// Second assign should add to statistics, not reset
|
|
312
|
+
await assigner.assign(mockBundleItems.slice(1, 2));
|
|
313
|
+
assert.strictEqual(assigner.compression_stats.total_items, 2);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|