@proteinjs/db 1.0.25 → 1.0.27

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 (43) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/generated/index.js +1 -1
  3. package/dist/generated/index.js.map +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/src/Columns.d.ts +12 -12
  9. package/dist/src/Columns.d.ts.map +1 -1
  10. package/dist/src/Columns.js +29 -18
  11. package/dist/src/Columns.js.map +1 -1
  12. package/dist/src/Db.d.ts +1 -0
  13. package/dist/src/Db.d.ts.map +1 -1
  14. package/dist/src/Db.js.map +1 -1
  15. package/dist/src/Record.d.ts.map +1 -1
  16. package/dist/src/Record.js +3 -0
  17. package/dist/src/Record.js.map +1 -1
  18. package/dist/src/StatementConfigFactory.d.ts.map +1 -1
  19. package/dist/src/StatementConfigFactory.js +1 -0
  20. package/dist/src/StatementConfigFactory.js.map +1 -1
  21. package/dist/src/Table.d.ts +2 -2
  22. package/dist/src/Table.d.ts.map +1 -1
  23. package/dist/src/schema/SchemaMetadata.js +5 -22
  24. package/dist/src/schema/SchemaMetadata.js.map +1 -1
  25. package/dist/test/ColumnTypesTests.d.ts +53 -0
  26. package/dist/test/ColumnTypesTests.d.ts.map +1 -0
  27. package/dist/test/ColumnTypesTests.js +209 -0
  28. package/dist/test/ColumnTypesTests.js.map +1 -0
  29. package/dist/test/CrudTests.d.ts +10 -0
  30. package/dist/test/CrudTests.d.ts.map +1 -1
  31. package/dist/test/CrudTests.js +302 -9
  32. package/dist/test/CrudTests.js.map +1 -1
  33. package/generated/index.ts +1 -1
  34. package/index.ts +1 -0
  35. package/package.json +3 -3
  36. package/src/Columns.ts +45 -32
  37. package/src/Db.ts +4 -1
  38. package/src/Record.ts +3 -0
  39. package/src/StatementConfigFactory.ts +1 -0
  40. package/src/Table.ts +2 -2
  41. package/src/schema/SchemaMetadata.ts +5 -5
  42. package/test/ColumnTypesTests.ts +153 -0
  43. package/test/CrudTests.ts +222 -10
@@ -0,0 +1,153 @@
1
+ import { Db, DbDriver } from '../src/Db';
2
+ import { Moment } from 'moment';
3
+ import { ReferenceArray } from '../src/reference/ReferenceArray';
4
+ import { Reference } from '../src/reference/Reference';
5
+ import {
6
+ IntegerColumn,
7
+ StringColumn,
8
+ FloatColumn,
9
+ DecimalColumn,
10
+ BooleanColumn,
11
+ DateColumn,
12
+ DateTimeColumn,
13
+ BinaryColumn,
14
+ UuidColumn,
15
+ PasswordColumn,
16
+ ObjectColumn,
17
+ ArrayColumn,
18
+ ReferenceArrayColumn,
19
+ ReferenceColumn,
20
+ } from '../src/Columns';
21
+ import { withRecordColumns, Record } from '../src/Record';
22
+ import { Table } from '../src/Table';
23
+
24
+ export interface TestRecord extends Record {
25
+ integerColumn?: number | null;
26
+ stringColumn?: string | null;
27
+ floatColumn?: number | null;
28
+ decimalColumn?: number | null;
29
+ booleanColumn?: boolean | null;
30
+ dateColumn?: Date | null;
31
+ dateTimeColumn?: Moment | null;
32
+ binaryColumn?: number | null;
33
+ uuidColumn?: string | null;
34
+ passwordColumn?: string | null;
35
+ objectColumn?: any | null;
36
+ arrayColumn?: any[] | null;
37
+ referenceArrayColumn?: ReferenceArray<any> | null;
38
+ referenceColumn?: Reference<any> | null;
39
+ }
40
+
41
+ export class TestTable extends Table<TestRecord> {
42
+ public name = 'db_test_table';
43
+ public columns = withRecordColumns<TestRecord>({
44
+ integerColumn: new IntegerColumn('integer_column'),
45
+ stringColumn: new StringColumn('string_column', {}, 255),
46
+ floatColumn: new FloatColumn('float_column'),
47
+ decimalColumn: new DecimalColumn('decimal_column'),
48
+ booleanColumn: new BooleanColumn('boolean_column'),
49
+ dateColumn: new DateColumn('date_column'),
50
+ dateTimeColumn: new DateTimeColumn('date_time_column'),
51
+ binaryColumn: new BinaryColumn('binary_column'),
52
+ uuidColumn: new UuidColumn('uuid_column'),
53
+ passwordColumn: new PasswordColumn('password_column'),
54
+ objectColumn: new ObjectColumn('object_column'),
55
+ arrayColumn: new ArrayColumn('array_column'),
56
+ referenceArrayColumn: new ReferenceArrayColumn('reference_array_column', 'reference_table', false),
57
+ referenceColumn: new ReferenceColumn('reference_column', 'reference_table', false),
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Used for testing purposes only.
63
+ * */
64
+ export const getColumnTypeTestTable = (tableName: string) => {
65
+ const testTable = new TestTable();
66
+ if (testTable.name === tableName) {
67
+ return new TestTable();
68
+ }
69
+ throw new Error(`Cannot find test table: ${tableName}`);
70
+ };
71
+
72
+ export const columnTypeTests = (driver: DbDriver, dropTable: (table: Table<any>) => Promise<void>) => {
73
+ return () => {
74
+ const db = new Db(driver, getColumnTypeTestTable);
75
+
76
+ beforeAll(async () => {
77
+ if (driver.start) {
78
+ await driver.start();
79
+ }
80
+
81
+ await driver.getTableManager().loadTable(new TestTable());
82
+ });
83
+
84
+ afterAll(async () => {
85
+ await dropTable(new TestTable());
86
+ });
87
+
88
+ test('Insert record with all null values', async () => {
89
+ const testRecord: Omit<TestRecord, keyof Record> = {
90
+ integerColumn: null,
91
+ stringColumn: null,
92
+ floatColumn: null,
93
+ decimalColumn: null,
94
+ booleanColumn: null,
95
+ dateColumn: null,
96
+ dateTimeColumn: null,
97
+ binaryColumn: null,
98
+ uuidColumn: null,
99
+ passwordColumn: null,
100
+ objectColumn: null,
101
+ arrayColumn: null,
102
+ referenceArrayColumn: null,
103
+ referenceColumn: null,
104
+ };
105
+
106
+ const testTable: Table<TestRecord> = new TestTable();
107
+ const insertedRecord = await db.insert(testTable, testRecord);
108
+ const fetchedRecord = await db.get(testTable, { id: insertedRecord.id });
109
+
110
+ expect(fetchedRecord).toBeTruthy();
111
+ expect(fetchedRecord.integerColumn).toBeNull();
112
+ expect(fetchedRecord.stringColumn).toBeNull();
113
+ expect(fetchedRecord.floatColumn).toBeNull();
114
+ expect(fetchedRecord.decimalColumn).toBeNull();
115
+ expect(fetchedRecord.booleanColumn).toBeNull();
116
+ expect(fetchedRecord.dateColumn).toBeNull();
117
+ expect(fetchedRecord.dateTimeColumn).toBeNull();
118
+ expect(fetchedRecord.binaryColumn).toBeNull();
119
+ expect(fetchedRecord.uuidColumn).toBeNull();
120
+ expect(fetchedRecord.passwordColumn).toBeNull();
121
+ expect(fetchedRecord.objectColumn).toBeNull();
122
+ expect(fetchedRecord.arrayColumn).toBeNull();
123
+ expect(fetchedRecord.referenceArrayColumn).toBeNull();
124
+ expect(fetchedRecord.referenceColumn).toBeNull();
125
+
126
+ // Clean up
127
+ await db.delete(testTable, { id: fetchedRecord.id });
128
+ });
129
+
130
+ test('Insert record with all undefined values', async () => {
131
+ const testRecord: Omit<TestRecord, keyof Record> = {
132
+ integerColumn: undefined,
133
+ stringColumn: undefined,
134
+ floatColumn: undefined,
135
+ decimalColumn: undefined,
136
+ booleanColumn: undefined,
137
+ dateColumn: undefined,
138
+ dateTimeColumn: undefined,
139
+ binaryColumn: undefined,
140
+ uuidColumn: undefined,
141
+ passwordColumn: undefined,
142
+ objectColumn: undefined,
143
+ arrayColumn: undefined,
144
+ referenceArrayColumn: undefined,
145
+ referenceColumn: undefined,
146
+ };
147
+
148
+ const testTable: Table<TestRecord> = new TestTable();
149
+
150
+ await expect(db.insert(testTable, testRecord)).rejects.toThrow();
151
+ });
152
+ };
153
+ };
package/test/CrudTests.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import { QueryBuilder } from '@proteinjs/db-query';
2
2
  import { DbDriver, Db } from '../src/Db';
3
3
  import { withRecordColumns, Record } from '../src/Record';
4
- import { StringColumn } from '../src/Columns';
4
+ import { BooleanColumn, DateColumn, StringColumn } from '../src/Columns';
5
5
  import { Table } from '../src/Table';
6
6
 
7
7
  export interface Employee extends Record {
8
8
  name: string;
9
9
  department?: string;
10
+ jobTitle?: string | null;
11
+ isRemote?: boolean;
12
+ startDate?: Date;
10
13
  object?: string;
11
14
  }
12
15
 
@@ -15,21 +18,28 @@ export class EmployeeTable extends Table<Employee> {
15
18
  columns = withRecordColumns<Employee>({
16
19
  name: new StringColumn('name'),
17
20
  department: new StringColumn('department'),
21
+ isRemote: new BooleanColumn('is_remote'),
22
+ jobTitle: new StringColumn('job_title'),
23
+ startDate: new DateColumn('start_date'),
18
24
  object: new StringColumn('object'),
19
25
  });
20
26
  }
21
27
 
28
+ /**
29
+ * Used for testing purposes only.
30
+ * */
31
+ export const getTestTable = (tableName: string) => {
32
+ const employeeTable = new EmployeeTable();
33
+ if (employeeTable.name == tableName) {
34
+ return new EmployeeTable();
35
+ }
36
+
37
+ throw new Error(`Cannot find test table: ${tableName}`);
38
+ };
39
+
22
40
  export const crudTests = (driver: DbDriver, dropTable: (table: Table<any>) => Promise<void>) => {
23
41
  return () => {
24
- const getTable = (tableName: string) => {
25
- const employeeTable = new EmployeeTable();
26
- if (employeeTable.name == tableName) {
27
- return employeeTable;
28
- }
29
-
30
- throw new Error('Cannot find test table');
31
- };
32
- const db = new Db(driver, getTable);
42
+ const db = new Db(driver, getTestTable);
33
43
 
34
44
  beforeAll(async () => {
35
45
  if (driver.start) {
@@ -126,5 +136,207 @@ export const crudTests = (driver: DbDriver, dropTable: (table: Table<any>) => Pr
126
136
  });
127
137
  await db.delete(emplyeeTable, qb);
128
138
  });
139
+
140
+ test('Insert with null values', async () => {
141
+ const testEmployee: Omit<Employee, keyof Record> = {
142
+ name: 'Veronica',
143
+ department: 'Cake Factory',
144
+ jobTitle: null,
145
+ isRemote: false,
146
+ };
147
+ const emplyeeTable: Table<Employee> = new EmployeeTable();
148
+ const insertedEmployee = await db.insert(emplyeeTable, testEmployee);
149
+ const fetchedEmployee = await db.get(emplyeeTable, { id: insertedEmployee.id });
150
+
151
+ expect(fetchedEmployee).toBeTruthy();
152
+ expect(fetchedEmployee.jobTitle).toBeNull();
153
+
154
+ await db.delete(emplyeeTable, { id: fetchedEmployee.id });
155
+ });
156
+
157
+ test('Update with null values', async () => {
158
+ const testEmployee: Omit<Employee, keyof Record> = {
159
+ name: 'Cassidy',
160
+ jobTitle: 'Cowboy',
161
+ isRemote: false,
162
+ };
163
+ const emplyeeTable: Table<Employee> = new EmployeeTable();
164
+ const insertedEmployee = await db.insert(emplyeeTable, testEmployee);
165
+ const fetchedEmployee = await db.get(emplyeeTable, { id: insertedEmployee.id });
166
+ expect(fetchedEmployee).toBeTruthy();
167
+
168
+ const nullUpdateCount = await db.update(emplyeeTable, { jobTitle: null }, { id: insertedEmployee.id });
169
+ expect(nullUpdateCount).toBe(1);
170
+ await db.delete(emplyeeTable, { id: fetchedEmployee.id });
171
+ });
172
+
173
+ test('Query with null values', async () => {
174
+ const testEmployee: Omit<Employee, keyof Record> = {
175
+ name: 'Cassidy',
176
+ jobTitle: null,
177
+ isRemote: false,
178
+ };
179
+ const emplyeeTable: Table<Employee> = new EmployeeTable();
180
+ const insertedEmployee = await db.insert(emplyeeTable, testEmployee);
181
+
182
+ const qb = new QueryBuilder<Employee>(emplyeeTable.name)
183
+ .condition({
184
+ field: 'jobTitle',
185
+ operator: 'IS NULL',
186
+ })
187
+ .condition({ field: 'id', operator: '=', value: insertedEmployee.id });
188
+ const rows = await db.query(emplyeeTable, qb);
189
+ expect(rows.length).toEqual(1);
190
+ await db.delete(emplyeeTable, { id: insertedEmployee.id });
191
+ });
192
+
193
+ test('Query with IN and NOT IN operators, including null values', async () => {
194
+ const testEmployee1: Omit<Employee, keyof Record> = { name: 'Veronica', jobTitle: 'Engineer' };
195
+ const testEmployee2: Omit<Employee, keyof Record> = { name: 'Zenyatta', jobTitle: null };
196
+ const testEmployee3: Omit<Employee, keyof Record> = { name: 'Cassidy', jobTitle: 'Cowboy' };
197
+ const emplyeeTable: Table<Employee> = new EmployeeTable();
198
+ const insertedEmployee1 = await db.insert(emplyeeTable, testEmployee1);
199
+ const insertedEmployee2 = await db.insert(emplyeeTable, testEmployee2);
200
+ const insertedEmployee3 = await db.insert(emplyeeTable, testEmployee3);
201
+
202
+ const inQuery = new QueryBuilder<Employee>(emplyeeTable.name).condition({
203
+ field: 'jobTitle',
204
+ operator: 'IN',
205
+ value: ['Engineer', null],
206
+ });
207
+ const inResults = await db.query(emplyeeTable, inQuery);
208
+ expect(inResults.length).toBe(1);
209
+ expect(inResults.some((emp) => emp.jobTitle === 'Engineer')).toBe(true);
210
+
211
+ const notInQuery = new QueryBuilder<Employee>(emplyeeTable.name).condition({
212
+ field: 'jobTitle',
213
+ operator: 'NOT IN',
214
+ value: ['Engineer', null],
215
+ });
216
+ const notInResults = await db.query(emplyeeTable, notInQuery);
217
+ expect(notInResults.length).toBe(0);
218
+
219
+ // Clean up
220
+ const qb = new QueryBuilder<Employee>(emplyeeTable.name).condition({
221
+ field: 'id',
222
+ operator: 'IN',
223
+ value: [insertedEmployee1.id, insertedEmployee2.id, insertedEmployee3.id],
224
+ });
225
+ await db.delete(emplyeeTable, qb);
226
+ });
227
+
228
+ test('Query with BETWEEN operator', async () => {
229
+ const testEmployee1: Omit<Employee, keyof Record> = {
230
+ name: 'Veronica',
231
+ jobTitle: 'Engineer',
232
+ startDate: new Date('1997-10-24T00:00:00Z'),
233
+ };
234
+ const testEmployee2: Omit<Employee, keyof Record> = {
235
+ name: 'Kiriko',
236
+ jobTitle: null,
237
+ startDate: new Date('2022-10-04T00:00:00Z'),
238
+ };
239
+ const testEmployee3: Omit<Employee, keyof Record> = {
240
+ name: 'Cassidy',
241
+ jobTitle: 'Cowboy',
242
+ startDate: new Date('2016-05-24T00:00:00Z'),
243
+ };
244
+ const employeeTable: Table<Employee> = new EmployeeTable();
245
+
246
+ const insertedEmployee1 = await db.insert(employeeTable, testEmployee1);
247
+ const insertedEmployee2 = await db.insert(employeeTable, testEmployee2);
248
+ const insertedEmployee3 = await db.insert(employeeTable, testEmployee3);
249
+
250
+ const betweenQuery = new QueryBuilder<Employee>(employeeTable.name).condition({
251
+ field: 'startDate',
252
+ operator: 'BETWEEN',
253
+ value: ['2000-06-05T00:00:00Z', '2024-06-05T00:00:00Z'],
254
+ });
255
+ const betweenResults = await db.query(employeeTable, betweenQuery);
256
+ expect(betweenResults.length).toBe(2);
257
+ expect(betweenResults.some((emp) => emp.name === 'Kiriko')).toBe(true);
258
+ expect(betweenResults.some((emp) => emp.name === 'Cassidy')).toBe(true);
259
+
260
+ // Clean up
261
+ const qb = new QueryBuilder<Employee>(employeeTable.name).condition({
262
+ field: 'id',
263
+ operator: 'IN',
264
+ value: [insertedEmployee1.id, insertedEmployee2.id, insertedEmployee3.id],
265
+ });
266
+ await db.delete(employeeTable, qb);
267
+ });
268
+
269
+ test('Query with sort', async () => {
270
+ const testEmployee1: Omit<Employee, keyof Record> = {
271
+ name: 'Veronica',
272
+ jobTitle: 'Engineer',
273
+ };
274
+ const testEmployee2: Omit<Employee, keyof Record> = {
275
+ name: 'Kiriko',
276
+ jobTitle: null,
277
+ };
278
+ const testEmployee3: Omit<Employee, keyof Record> = {
279
+ name: 'Cassidy',
280
+ jobTitle: 'Cowboy',
281
+ };
282
+ const emplyeeTable: Table<Employee> = new EmployeeTable();
283
+
284
+ const insertedEmployee1 = await db.insert(emplyeeTable, testEmployee1);
285
+ const insertedEmployee2 = await db.insert(emplyeeTable, testEmployee2);
286
+ const insertedEmployee3 = await db.insert(emplyeeTable, testEmployee3);
287
+
288
+ const sortQuery = new QueryBuilder<Employee>(emplyeeTable.name).sort([
289
+ { field: 'name', byValues: ['Cassidy', 'Veronica', 'Kiriko'] },
290
+ ]);
291
+ const sortResults = await db.query(emplyeeTable, sortQuery);
292
+
293
+ // Assertions
294
+ expect(sortResults.length).toBe(3);
295
+ expect(sortResults[0].name).toBe('Cassidy');
296
+ expect(sortResults[1].name).toBe('Veronica');
297
+ expect(sortResults[2].name).toBe('Kiriko');
298
+
299
+ // Clean up
300
+ const qb = new QueryBuilder<Employee>(emplyeeTable.name).condition({
301
+ field: 'id',
302
+ operator: 'IN',
303
+ value: [insertedEmployee1.id, insertedEmployee2.id, insertedEmployee3.id],
304
+ });
305
+ await db.delete(emplyeeTable, qb);
306
+ });
307
+
308
+ test('CRUD operations with undefined values', async () => {
309
+ const testEmployee1: Omit<Employee, keyof Record> = { name: 'Veronica', jobTitle: 'Software Engineer' };
310
+ const testEmployee2: Omit<Employee, keyof Record> = { name: 'Brent', jobTitle: undefined };
311
+ const emplyeeTable: Table<Employee> = new EmployeeTable();
312
+
313
+ // Insert operation with undefined values
314
+ await expect(db.insert(emplyeeTable, testEmployee2)).rejects.toThrow();
315
+
316
+ // Insert valid employee
317
+ const insertedEmployee1 = await db.insert(emplyeeTable, testEmployee1);
318
+
319
+ // Attempt to build query with undefined value
320
+ expect(() => {
321
+ new QueryBuilder<Employee>(emplyeeTable.name).condition({ field: 'jobTitle', operator: '=', value: undefined });
322
+ }).toThrow();
323
+
324
+ // Attempt to query
325
+ await expect(db.query(emplyeeTable, { id: insertedEmployee1, jobTitle: undefined })).rejects.toThrow();
326
+
327
+ // Update operation with undefined values
328
+ await expect(db.update(emplyeeTable, { id: insertedEmployee1.id, jobTitle: undefined })).rejects.toThrow();
329
+
330
+ // Attempt to delete
331
+ await expect(db.delete(emplyeeTable, { id: insertedEmployee1, jobTitle: undefined })).rejects.toThrow();
332
+
333
+ // Clean up
334
+ const deleteValidQuery = new QueryBuilder<Employee>(emplyeeTable.name).condition({
335
+ field: 'id',
336
+ operator: 'IN',
337
+ value: [insertedEmployee1.id],
338
+ });
339
+ await db.delete(emplyeeTable, deleteValidQuery);
340
+ });
129
341
  };
130
342
  };