masterrecord 0.2.34 ā 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.
- package/.claude/settings.local.json +25 -1
- package/Entity/entityModel.js +6 -0
- package/Entity/entityTrackerModel.js +20 -3
- package/Entity/fieldTransformer.js +266 -0
- package/Migrations/migrationMySQLQuery.js +145 -1
- package/Migrations/migrationPostgresQuery.js +402 -0
- package/Migrations/migrationSQLiteQuery.js +145 -1
- package/Migrations/schema.js +131 -28
- package/QueryLanguage/queryMethods.js +193 -15
- package/QueryLanguage/queryParameters.js +136 -0
- package/QueryLanguage/queryScript.js +14 -5
- package/SQLLiteEngine.js +309 -19
- package/context.js +57 -12
- package/docs/INCLUDES_CLARIFICATION.md +202 -0
- package/docs/METHODS_REFERENCE.md +184 -0
- package/docs/MIGRATIONS_GUIDE.md +699 -0
- package/docs/POSTGRESQL_SETUP.md +415 -0
- package/examples/jsonArrayTransformer.js +215 -0
- package/mySQLEngine.js +249 -17
- package/package.json +6 -6
- package/postgresEngine.js +434 -491
- package/postgresSyncConnect.js +209 -0
- package/readme.md +1121 -265
- package/test/anyCommaStringTest.js +237 -0
- package/test/anyMethodTest.js +176 -0
- package/test/findByIdTest.js +227 -0
- package/test/includesFeatureTest.js +183 -0
- package/test/includesTransformTest.js +110 -0
- package/test/newMethodTest.js +330 -0
- package/test/newMethodUnitTest.js +320 -0
- package/test/parameterizedPlaceholderTest.js +159 -0
- package/test/postgresEngineTest.js +463 -0
- package/test/postgresIntegrationTest.js +381 -0
- package/test/securityTest.js +268 -0
- package/test/singleDollarPlaceholderTest.js +238 -0
- package/test/tablePrefixTest.js +100 -0
- package/test/transformerTest.js +287 -0
- package/test/verifyFindById.js +169 -0
- package/test/verifyNewMethod.js +191 -0
- package/test/whereChainingTest.js +88 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: .includes() Feature
|
|
3
|
+
*
|
|
4
|
+
* This test demonstrates the new .includes() syntax that allows
|
|
5
|
+
* natural JavaScript array operations in queries.
|
|
6
|
+
*
|
|
7
|
+
* Example: const ids = [1, 2, 3];
|
|
8
|
+
* context.User.where(u => $$.includes(u.id), ids).toList()
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Create in-memory test models
|
|
12
|
+
class User {
|
|
13
|
+
constructor(){
|
|
14
|
+
this.id = { type: "integer", primary: true, auto: true };
|
|
15
|
+
this.name = { type: "string" };
|
|
16
|
+
this.__name = "User";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class TestContext {
|
|
21
|
+
constructor(){
|
|
22
|
+
this.isSQLite = true;
|
|
23
|
+
this.isMySQL = false;
|
|
24
|
+
this.isPostgres = false;
|
|
25
|
+
|
|
26
|
+
// Mock database
|
|
27
|
+
this.db = {
|
|
28
|
+
prepare: (sql) => {
|
|
29
|
+
console.log("\nš Generated SQL:", sql);
|
|
30
|
+
return {
|
|
31
|
+
all: (...params) => {
|
|
32
|
+
console.log("š¦ Parameters:", params);
|
|
33
|
+
// Return mock data
|
|
34
|
+
return [
|
|
35
|
+
{ id: 1, name: "Alice" },
|
|
36
|
+
{ id: 2, name: "Bob" },
|
|
37
|
+
{ id: 5, name: "Charlie" }
|
|
38
|
+
];
|
|
39
|
+
},
|
|
40
|
+
get: (...params) => {
|
|
41
|
+
console.log("š¦ Parameters:", params);
|
|
42
|
+
return { id: 1, name: "Alice" };
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Track entities
|
|
49
|
+
this.trackedEntities = [];
|
|
50
|
+
this.__track = (entity) => {
|
|
51
|
+
this.trackedEntities.push(entity);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Mock SQL engine
|
|
55
|
+
this._SQLEngine = {
|
|
56
|
+
all: (script, entity, context) => {
|
|
57
|
+
console.log("\nš Query Script:", JSON.stringify(script, null, 2));
|
|
58
|
+
return [];
|
|
59
|
+
},
|
|
60
|
+
get: (script, entity, context) => {
|
|
61
|
+
console.log("\nš Query Script:", JSON.stringify(script, null, 2));
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
dbset(model, name){
|
|
68
|
+
const queryMethods = require('../QueryLanguage/queryMethods');
|
|
69
|
+
const entity = new model();
|
|
70
|
+
return new queryMethods(entity, this);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Run tests
|
|
75
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
76
|
+
console.log("ā .includes() Feature Test Suite ā");
|
|
77
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const context = new TestContext();
|
|
81
|
+
const userSet = context.dbset(User, "User");
|
|
82
|
+
|
|
83
|
+
// Test 1: Basic .includes() with integer array
|
|
84
|
+
console.log("š Test 1: Basic .includes() with integer array");
|
|
85
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
86
|
+
const userIds = [1, 2, 5];
|
|
87
|
+
console.log("Input: const userIds = [1, 2, 5];");
|
|
88
|
+
console.log("Query: context.User.where(u => $$.includes(u.id), userIds).toList()");
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const result1 = userSet.where(u => $$.includes(u.id), userIds).toList();
|
|
92
|
+
console.log("ā
Test 1 PASSED - No errors thrown");
|
|
93
|
+
} catch(err) {
|
|
94
|
+
console.log("ā Test 1 FAILED:", err.message);
|
|
95
|
+
console.log(err.stack);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Test 2: .includes() with string array
|
|
99
|
+
console.log("\n\nš Test 2: .includes() with string array");
|
|
100
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
101
|
+
const names = ["Alice", "Bob", "Charlie"];
|
|
102
|
+
console.log("Input: const names = ['Alice', 'Bob', 'Charlie'];");
|
|
103
|
+
console.log("Query: context.User.where(u => $$.includes(u.name), names).toList()");
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const result2 = userSet.where(u => $$.includes(u.name), names).toList();
|
|
107
|
+
console.log("ā
Test 2 PASSED - No errors thrown");
|
|
108
|
+
} catch(err) {
|
|
109
|
+
console.log("ā Test 2 FAILED:", err.message);
|
|
110
|
+
console.log(err.stack);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Test 3: Combined .includes() with other conditions
|
|
114
|
+
console.log("\n\nš Test 3: Combined .includes() with AND condition");
|
|
115
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
116
|
+
const ids = [1, 2, 3];
|
|
117
|
+
console.log("Input: const ids = [1, 2, 3];");
|
|
118
|
+
console.log("Query: context.User.where(u => $$.includes(u.id), ids)");
|
|
119
|
+
console.log(" .and(u => u.name != $$, 'Admin')");
|
|
120
|
+
console.log(" .toList()");
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const result3 = userSet
|
|
124
|
+
.where(u => $$.includes(u.id), ids)
|
|
125
|
+
.and(u => u.name != $$, 'Admin')
|
|
126
|
+
.toList();
|
|
127
|
+
console.log("ā
Test 3 PASSED - No errors thrown");
|
|
128
|
+
} catch(err) {
|
|
129
|
+
console.log("ā Test 3 FAILED:", err.message);
|
|
130
|
+
console.log(err.stack);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Test 4: Empty array (should fail with proper error)
|
|
134
|
+
console.log("\n\nš Test 4: Empty array (should throw error)");
|
|
135
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
136
|
+
const emptyArray = [];
|
|
137
|
+
console.log("Input: const emptyArray = [];");
|
|
138
|
+
console.log("Query: context.User.where(u => $$.includes(u.id), emptyArray).toList()");
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const result4 = userSet.where(u => $$.includes(u.id), emptyArray).toList();
|
|
142
|
+
console.log("ā Test 4 FAILED - Should have thrown error for empty array");
|
|
143
|
+
} catch(err) {
|
|
144
|
+
if(err.message.includes('empty array')){
|
|
145
|
+
console.log("ā
Test 4 PASSED - Correctly rejected empty array");
|
|
146
|
+
console.log(" Error message:", err.message);
|
|
147
|
+
} else {
|
|
148
|
+
console.log("ā ļø Test 4 PARTIAL - Threw error but wrong message:", err.message);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Test 5: Security test - array with special characters
|
|
153
|
+
console.log("\n\nš Test 5: Security - Array with special SQL characters");
|
|
154
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
155
|
+
const maliciousNames = ["Alice'; DROP TABLE User;--", "Bob", "Charlie"];
|
|
156
|
+
console.log("Input: const maliciousNames = [\"Alice'; DROP TABLE User;--\", \"Bob\", \"Charlie\"];");
|
|
157
|
+
console.log("Query: context.User.where(u => $$.includes(u.name), maliciousNames).toList()");
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const result5 = userSet.where(u => $$.includes(u.name), maliciousNames).toList();
|
|
161
|
+
console.log("ā
Test 5 PASSED - Parameters properly escaped (no SQL injection)");
|
|
162
|
+
} catch(err) {
|
|
163
|
+
console.log("ā Test 5 FAILED:", err.message);
|
|
164
|
+
console.log(err.stack);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Summary
|
|
168
|
+
console.log("\n\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
169
|
+
console.log("ā Test Summary ā");
|
|
170
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
171
|
+
console.log("\n⨠.includes() feature implementation complete!");
|
|
172
|
+
console.log("\nš Usage Guide:");
|
|
173
|
+
console.log(" Instead of: context.User.where(u => u.id.any($$), '1,2,3')");
|
|
174
|
+
console.log(" You can now: context.User.where(u => $$.includes(u.id), [1, 2, 3])");
|
|
175
|
+
console.log("\nš Security: All array values are properly parameterized");
|
|
176
|
+
console.log(" preventing SQL injection attacks.");
|
|
177
|
+
|
|
178
|
+
} catch(err) {
|
|
179
|
+
console.log("\nā CRITICAL ERROR:", err.message);
|
|
180
|
+
console.log(err.stack);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
console.log("\n");
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit Test: .includes() Transformation Logic
|
|
3
|
+
*
|
|
4
|
+
* This test verifies the __transformIncludes method correctly transforms
|
|
5
|
+
* JavaScript .includes() syntax to MasterRecord's .any() syntax.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Simple mock of the transformation method
|
|
9
|
+
function __transformIncludes(str, args){
|
|
10
|
+
// Pattern: $$.includes(entity.field) or $$.includes(entity.field.nested)
|
|
11
|
+
const includesPattern = /\$\$\.includes\s*\(\s*([\w\d$_]+)\.([.\w\d_]+)\s*\)/g;
|
|
12
|
+
|
|
13
|
+
// Use replace with a function - when using a function, return value is used literally
|
|
14
|
+
const transformedStr = str.replace(includesPattern, (match, entity, field) => {
|
|
15
|
+
// Transform to .any() syntax: entity.field.any($$)
|
|
16
|
+
return entity + '.' + field + '.any($$)';
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return { query: transformedStr, args: args };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Test cases
|
|
23
|
+
const tests = [
|
|
24
|
+
{
|
|
25
|
+
name: "Basic .includes() with simple field",
|
|
26
|
+
input: "u => $$.includes(u.id)",
|
|
27
|
+
expected: "u => u.id.any($$)",
|
|
28
|
+
args: [[1, 2, 3]]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: ".includes() with string field",
|
|
32
|
+
input: "u => $$.includes(u.name)",
|
|
33
|
+
expected: "u => u.name.any($$)",
|
|
34
|
+
args: [["Alice", "Bob"]]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: ".includes() with spaces",
|
|
38
|
+
input: "u => $$.includes( u.id )",
|
|
39
|
+
expected: "u => u.id.any($$)",
|
|
40
|
+
args: [[1, 2, 3]]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: ".includes() in complex query",
|
|
44
|
+
input: "u => $$.includes(u.id) && u.active == true",
|
|
45
|
+
expected: "u => u.id.any($$) && u.active == true",
|
|
46
|
+
args: [[1, 2, 3]]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Multiple .includes() in same query",
|
|
50
|
+
input: "u => $$.includes(u.id) && $$.includes(u.role_id)",
|
|
51
|
+
expected: "u => u.id.any($$) && u.role_id.any($$)",
|
|
52
|
+
args: [[1, 2, 3], [10, 20]]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "No .includes() - should not change",
|
|
56
|
+
input: "u => u.id == $$",
|
|
57
|
+
expected: "u => u.id == $$",
|
|
58
|
+
args: [5]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: ".includes() with nested field path",
|
|
62
|
+
input: "u => $$.includes(u.profile_id)",
|
|
63
|
+
expected: "u => u.profile_id.any($$)",
|
|
64
|
+
args: [[100, 200, 300]]
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
// Run tests
|
|
69
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
70
|
+
console.log("ā .includes() Transformation Unit Tests ā");
|
|
71
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
72
|
+
|
|
73
|
+
let passed = 0;
|
|
74
|
+
let failed = 0;
|
|
75
|
+
|
|
76
|
+
for(const test of tests){
|
|
77
|
+
console.log(`š ${test.name}`);
|
|
78
|
+
console.log(` Input: ${test.input}`);
|
|
79
|
+
console.log(` Expected: ${test.expected}`);
|
|
80
|
+
|
|
81
|
+
const result = __transformIncludes(test.input, test.args);
|
|
82
|
+
|
|
83
|
+
if(result.query === test.expected){
|
|
84
|
+
console.log(` ā
PASS - Got: ${result.query}\n`);
|
|
85
|
+
passed++;
|
|
86
|
+
} else {
|
|
87
|
+
console.log(` ā FAIL - Got: ${result.query}\n`);
|
|
88
|
+
failed++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Summary
|
|
93
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
94
|
+
console.log("ā Test Summary ā");
|
|
95
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
96
|
+
console.log(` Total: ${tests.length} tests`);
|
|
97
|
+
console.log(` ā
Passed: ${passed}`);
|
|
98
|
+
console.log(` ā Failed: ${failed}`);
|
|
99
|
+
console.log(` Success Rate: ${Math.round((passed/tests.length)*100)}%\n`);
|
|
100
|
+
|
|
101
|
+
if(failed === 0){
|
|
102
|
+
console.log("š All transformation tests passed!");
|
|
103
|
+
console.log("\nš The .includes() feature is ready to use:");
|
|
104
|
+
console.log(" const ids = [1, 2, 3];");
|
|
105
|
+
console.log(" context.User.where(u => $$.includes(u.id), ids).toList()");
|
|
106
|
+
process.exit(0);
|
|
107
|
+
} else {
|
|
108
|
+
console.log("ā ļø Some tests failed. Review the transformation logic.");
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: .new() Method for Creating Empty Entity Instances
|
|
3
|
+
*
|
|
4
|
+
* Verifies that all dbsets have access to .new() method
|
|
5
|
+
* which creates empty entity instances ready for property assignment.
|
|
6
|
+
*
|
|
7
|
+
* Bug Report: QaIntelligenceJob.new() was not a function
|
|
8
|
+
* Root Cause: .new() method was never implemented in queryMethods.js
|
|
9
|
+
* Fix: Added .new() method to create tracked entity instances
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const masterrecord = require('../MasterRecord.js');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
16
|
+
console.log("ā .new() Method Test Suite ā");
|
|
17
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
18
|
+
|
|
19
|
+
let passed = 0;
|
|
20
|
+
let failed = 0;
|
|
21
|
+
|
|
22
|
+
// Test 1: Define test entities
|
|
23
|
+
console.log("š Test 1: Define test entities");
|
|
24
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
25
|
+
|
|
26
|
+
class TestUser {
|
|
27
|
+
id(db) {
|
|
28
|
+
db.integer().primary().auto();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
name(db) {
|
|
32
|
+
db.string().notNullable();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
email(db) {
|
|
36
|
+
db.string().notNullable().unique();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
age(db) {
|
|
40
|
+
db.integer().nullable();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
created_at(db) {
|
|
44
|
+
db.string().notNullable();
|
|
45
|
+
db.get(function(value) {
|
|
46
|
+
return value || Date.now().toString();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class TestPost {
|
|
52
|
+
id(db) {
|
|
53
|
+
db.integer().primary().auto();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
title(db) {
|
|
57
|
+
db.string().notNullable();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
content(db) {
|
|
61
|
+
db.string().nullable();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
user_id(db) {
|
|
65
|
+
db.integer().notNullable();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(" ā TestUser entity defined");
|
|
70
|
+
console.log(" ā TestPost entity defined");
|
|
71
|
+
passed++;
|
|
72
|
+
|
|
73
|
+
// Test 2: Create context and register dbsets
|
|
74
|
+
console.log("\nš Test 2: Create context and register dbsets");
|
|
75
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
76
|
+
|
|
77
|
+
class TestContext extends masterrecord.context {
|
|
78
|
+
constructor() {
|
|
79
|
+
super();
|
|
80
|
+
// Use in-memory SQLite for testing
|
|
81
|
+
this.tablePrefix = "test_";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
onConfig(db) {
|
|
85
|
+
this.dbset(TestUser);
|
|
86
|
+
this.dbset(TestPost);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const ctx = new TestContext();
|
|
92
|
+
ctx.onConfig();
|
|
93
|
+
|
|
94
|
+
if(ctx.TestUser && ctx.TestPost) {
|
|
95
|
+
console.log(" ā Context created");
|
|
96
|
+
console.log(" ā TestUser dbset registered");
|
|
97
|
+
console.log(" ā TestPost dbset registered");
|
|
98
|
+
passed++;
|
|
99
|
+
} else {
|
|
100
|
+
console.log(" ā Dbsets not registered properly");
|
|
101
|
+
failed++;
|
|
102
|
+
}
|
|
103
|
+
} catch(err) {
|
|
104
|
+
console.log(` ā Error: ${err.message}`);
|
|
105
|
+
failed++;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Test 3: Verify .new() method exists on all dbsets
|
|
109
|
+
console.log("\nš Test 3: Verify .new() method exists");
|
|
110
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const ctx = new TestContext();
|
|
114
|
+
ctx.onConfig();
|
|
115
|
+
|
|
116
|
+
const hasNewOnUser = typeof ctx.TestUser.new === 'function';
|
|
117
|
+
const hasNewOnPost = typeof ctx.TestPost.new === 'function';
|
|
118
|
+
|
|
119
|
+
if(hasNewOnUser && hasNewOnPost) {
|
|
120
|
+
console.log(" ā TestUser.new is a function");
|
|
121
|
+
console.log(" ā TestPost.new is a function");
|
|
122
|
+
passed++;
|
|
123
|
+
} else {
|
|
124
|
+
console.log(` ā TestUser.new: ${typeof ctx.TestUser.new}`);
|
|
125
|
+
console.log(` ā TestPost.new: ${typeof ctx.TestPost.new}`);
|
|
126
|
+
failed++;
|
|
127
|
+
}
|
|
128
|
+
} catch(err) {
|
|
129
|
+
console.log(` ā Error: ${err.message}`);
|
|
130
|
+
failed++;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Test 4: Create entity instance with .new()
|
|
134
|
+
console.log("\nš Test 4: Create entity instance with .new()");
|
|
135
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const ctx = new TestContext();
|
|
139
|
+
ctx.onConfig();
|
|
140
|
+
|
|
141
|
+
const user = ctx.TestUser.new();
|
|
142
|
+
|
|
143
|
+
if(user && typeof user === 'object') {
|
|
144
|
+
console.log(" ā .new() returns an object");
|
|
145
|
+
|
|
146
|
+
// Check internal properties
|
|
147
|
+
if(user.__state === 'insert') {
|
|
148
|
+
console.log(" ā Entity has __state = 'insert'");
|
|
149
|
+
} else {
|
|
150
|
+
console.log(` ā Expected __state='insert', got '${user.__state}'`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if(user.__entity) {
|
|
154
|
+
console.log(" ā Entity has __entity reference");
|
|
155
|
+
} else {
|
|
156
|
+
console.log(" ā Entity missing __entity reference");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if(user.__context) {
|
|
160
|
+
console.log(" ā Entity has __context reference");
|
|
161
|
+
} else {
|
|
162
|
+
console.log(" ā Entity missing __context reference");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
passed++;
|
|
166
|
+
} else {
|
|
167
|
+
console.log(` ā .new() returned: ${typeof user}`);
|
|
168
|
+
failed++;
|
|
169
|
+
}
|
|
170
|
+
} catch(err) {
|
|
171
|
+
console.log(` ā Error: ${err.message}`);
|
|
172
|
+
failed++;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Test 5: Set properties on new entity
|
|
176
|
+
console.log("\nš Test 5: Set properties on new entity");
|
|
177
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const ctx = new TestContext();
|
|
181
|
+
ctx.onConfig();
|
|
182
|
+
|
|
183
|
+
const user = ctx.TestUser.new();
|
|
184
|
+
user.name = "John Doe";
|
|
185
|
+
user.email = "john@example.com";
|
|
186
|
+
user.age = 30;
|
|
187
|
+
|
|
188
|
+
if(user.name === "John Doe" && user.email === "john@example.com" && user.age === 30) {
|
|
189
|
+
console.log(" ā Properties set successfully");
|
|
190
|
+
console.log(` ā user.name = "${user.name}"`);
|
|
191
|
+
console.log(` ā user.email = "${user.email}"`);
|
|
192
|
+
console.log(` ā user.age = ${user.age}`);
|
|
193
|
+
passed++;
|
|
194
|
+
} else {
|
|
195
|
+
console.log(` ā Properties not set correctly`);
|
|
196
|
+
console.log(` ā user.name = "${user.name}"`);
|
|
197
|
+
console.log(` ā user.email = "${user.email}"`);
|
|
198
|
+
console.log(` ā user.age = ${user.age}`);
|
|
199
|
+
failed++;
|
|
200
|
+
}
|
|
201
|
+
} catch(err) {
|
|
202
|
+
console.log(` ā Error: ${err.message}`);
|
|
203
|
+
failed++;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Test 6: Verify entity is tracked
|
|
207
|
+
console.log("\nš Test 6: Verify entity is tracked in context");
|
|
208
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const ctx = new TestContext();
|
|
212
|
+
ctx.onConfig();
|
|
213
|
+
|
|
214
|
+
const user = ctx.TestUser.new();
|
|
215
|
+
user.name = "Jane Doe";
|
|
216
|
+
user.email = "jane@example.com";
|
|
217
|
+
|
|
218
|
+
// Check if entity is in tracked entities
|
|
219
|
+
const isTracked = ctx.__trackedEntities.some(e => e.__ID === user.__ID);
|
|
220
|
+
|
|
221
|
+
if(isTracked) {
|
|
222
|
+
console.log(" ā Entity is tracked in context");
|
|
223
|
+
console.log(` ā Tracked entities count: ${ctx.__trackedEntities.length}`);
|
|
224
|
+
passed++;
|
|
225
|
+
} else {
|
|
226
|
+
console.log(" ā Entity not found in tracked entities");
|
|
227
|
+
failed++;
|
|
228
|
+
}
|
|
229
|
+
} catch(err) {
|
|
230
|
+
console.log(` ā Error: ${err.message}`);
|
|
231
|
+
failed++;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Test 7: Verify dirty fields tracking
|
|
235
|
+
console.log("\nš Test 7: Verify dirty fields are tracked");
|
|
236
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
const ctx = new TestContext();
|
|
240
|
+
ctx.onConfig();
|
|
241
|
+
|
|
242
|
+
const user = ctx.TestUser.new();
|
|
243
|
+
user.name = "Test User";
|
|
244
|
+
user.email = "test@example.com";
|
|
245
|
+
|
|
246
|
+
const hasDirtyFields = user.__dirtyFields && user.__dirtyFields.length > 0;
|
|
247
|
+
const hasNameInDirty = user.__dirtyFields.includes('name');
|
|
248
|
+
const hasEmailInDirty = user.__dirtyFields.includes('email');
|
|
249
|
+
|
|
250
|
+
if(hasDirtyFields && hasNameInDirty && hasEmailInDirty) {
|
|
251
|
+
console.log(" ā Dirty fields tracked");
|
|
252
|
+
console.log(` ā Dirty fields: [${user.__dirtyFields.join(', ')}]`);
|
|
253
|
+
passed++;
|
|
254
|
+
} else {
|
|
255
|
+
console.log(` ā Dirty fields not tracked properly`);
|
|
256
|
+
console.log(` ā Dirty fields: [${user.__dirtyFields.join(', ')}]`);
|
|
257
|
+
failed++;
|
|
258
|
+
}
|
|
259
|
+
} catch(err) {
|
|
260
|
+
console.log(` ā Error: ${err.message}`);
|
|
261
|
+
failed++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Test 8: Multiple entities from same dbset
|
|
265
|
+
console.log("\nš Test 8: Create multiple entities from same dbset");
|
|
266
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const ctx = new TestContext();
|
|
270
|
+
ctx.onConfig();
|
|
271
|
+
|
|
272
|
+
const user1 = ctx.TestUser.new();
|
|
273
|
+
user1.name = "User 1";
|
|
274
|
+
user1.email = "user1@example.com";
|
|
275
|
+
|
|
276
|
+
const user2 = ctx.TestUser.new();
|
|
277
|
+
user2.name = "User 2";
|
|
278
|
+
user2.email = "user2@example.com";
|
|
279
|
+
|
|
280
|
+
const differentIDs = user1.__ID !== user2.__ID;
|
|
281
|
+
const bothTracked = ctx.__trackedEntities.length === 2;
|
|
282
|
+
|
|
283
|
+
if(differentIDs && bothTracked) {
|
|
284
|
+
console.log(" ā Multiple entities created");
|
|
285
|
+
console.log(" ā Entities have different IDs");
|
|
286
|
+
console.log(" ā Both entities tracked");
|
|
287
|
+
passed++;
|
|
288
|
+
} else {
|
|
289
|
+
console.log(` ā Multiple entity creation failed`);
|
|
290
|
+
console.log(` ā Different IDs: ${differentIDs}`);
|
|
291
|
+
console.log(` ā Both tracked: ${bothTracked}`);
|
|
292
|
+
failed++;
|
|
293
|
+
}
|
|
294
|
+
} catch(err) {
|
|
295
|
+
console.log(` ā Error: ${err.message}`);
|
|
296
|
+
failed++;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Summary
|
|
300
|
+
console.log("\n\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
301
|
+
console.log("ā Test Summary ā");
|
|
302
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
303
|
+
|
|
304
|
+
const total = passed + failed;
|
|
305
|
+
const successRate = Math.round((passed/total)*100);
|
|
306
|
+
|
|
307
|
+
console.log(`\n Total Tests: ${total}`);
|
|
308
|
+
console.log(` ā
Passed: ${passed}`);
|
|
309
|
+
console.log(` ā Failed: ${failed}`);
|
|
310
|
+
console.log(` Success Rate: ${successRate}%\n`);
|
|
311
|
+
|
|
312
|
+
if(failed === 0){
|
|
313
|
+
console.log("š All .new() tests passed!");
|
|
314
|
+
console.log("\n⨠Feature Implemented!");
|
|
315
|
+
console.log("\nš What was fixed:");
|
|
316
|
+
console.log(" - Added .new() method to queryMethods.js");
|
|
317
|
+
console.log(" - Creates empty entity instances with property setters");
|
|
318
|
+
console.log(" - Automatically tracks entities for INSERT operations");
|
|
319
|
+
console.log(" - Sets __state = 'insert' for new entities");
|
|
320
|
+
console.log(" - Tracks dirty fields as properties are set");
|
|
321
|
+
console.log("\n Usage example:");
|
|
322
|
+
console.log(" const job = context.QaIntelligenceJob.new();");
|
|
323
|
+
console.log(" job.annotation_id = 123;");
|
|
324
|
+
console.log(" job.job_type = 'auto_rewrite';");
|
|
325
|
+
console.log(" context.saveChanges(); // Inserts the job\n");
|
|
326
|
+
process.exit(0);
|
|
327
|
+
} else {
|
|
328
|
+
console.log("ā ļø Some tests failed. Review and fix issues.");
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|