@rebasepro/server-postgresql 0.6.0 → 0.7.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/package.json +24 -18
- package/src/PostgresBackendDriver.ts +65 -44
- package/src/PostgresBootstrapper.ts +49 -20
- package/src/auth/ensure-tables.ts +56 -1
- package/src/auth/services.ts +94 -1
- package/src/cli-errors.ts +162 -0
- package/src/cli-helpers.ts +183 -0
- package/src/cli.ts +198 -251
- package/src/data-transformer.ts +9 -1
- package/src/schema/auth-default-policies.ts +90 -0
- package/src/schema/auth-schema.ts +25 -2
- package/src/schema/doctor.ts +2 -4
- package/src/schema/generate-drizzle-schema-logic.ts +4 -3
- package/src/schema/generate-drizzle-schema.ts +3 -5
- package/src/schema/generate-postgres-ddl-logic.ts +524 -0
- package/src/schema/generate-postgres-ddl.ts +116 -0
- package/src/services/EntityPersistService.ts +18 -16
- package/src/services/entityService.ts +28 -3
- package/src/utils/pg-array-null-patch.ts +42 -0
- package/src/utils/pg-error-utils.ts +16 -0
- package/src/utils/table-classification.ts +16 -0
- package/src/websocket.ts +9 -0
- package/test/array-null-safety.test.ts +335 -0
- package/test/auth-default-policies.test.ts +89 -0
- package/test/cli-helpers-extended.test.ts +324 -0
- package/test/cli-helpers.test.ts +59 -0
- package/test/connection.test.ts +292 -0
- package/test/data-transformer.test.ts +53 -2
- package/test/databasePoolManager.test.ts +289 -0
- package/test/doctor-extended.test.ts +443 -0
- package/test/e2e/db-e2e.test.ts +293 -0
- package/test/e2e/pg-setup.ts +79 -0
- package/test/entity-persist-composite-keys.test.ts +451 -0
- package/test/generate-postgres-ddl-edge-cases.test.ts +716 -0
- package/test/generate-postgres-ddl.test.ts +300 -0
- package/test/mfa-service.test.ts +544 -0
- package/test/pg-array-null-patch.test.ts +65 -0
- package/test/pg-error-utils.test.ts +50 -1
- package/test/realtimeService-channels.test.ts +696 -0
- package/test/unmapped-tables-safety.test.ts +55 -342
- package/vite.config.ts +8 -6
- package/vitest.e2e.config.ts +10 -0
- package/build-errors.txt +0 -37
- package/dist/PostgresAdapter.d.ts +0 -6
- package/dist/PostgresBackendDriver.d.ts +0 -110
- package/dist/PostgresBootstrapper.d.ts +0 -46
- package/dist/auth/ensure-tables.d.ts +0 -10
- package/dist/auth/services.d.ts +0 -231
- package/dist/cli.d.ts +0 -1
- package/dist/collections/PostgresCollectionRegistry.d.ts +0 -47
- package/dist/connection.d.ts +0 -65
- package/dist/data-transformer.d.ts +0 -55
- package/dist/databasePoolManager.d.ts +0 -20
- package/dist/history/HistoryService.d.ts +0 -71
- package/dist/history/ensure-history-table.d.ts +0 -7
- package/dist/index.d.ts +0 -14
- package/dist/index.es.js +0 -10764
- package/dist/index.es.js.map +0 -1
- package/dist/index.umd.js +0 -11055
- package/dist/index.umd.js.map +0 -1
- package/dist/interfaces.d.ts +0 -18
- package/dist/schema/auth-schema.d.ts +0 -2149
- package/dist/schema/doctor-cli.d.ts +0 -2
- package/dist/schema/doctor.d.ts +0 -52
- package/dist/schema/generate-drizzle-schema-logic.d.ts +0 -2
- package/dist/schema/generate-drizzle-schema.d.ts +0 -1
- package/dist/schema/introspect-db-inference.d.ts +0 -5
- package/dist/schema/introspect-db-logic.d.ts +0 -118
- package/dist/schema/introspect-db.d.ts +0 -1
- package/dist/schema/test-schema.d.ts +0 -24
- package/dist/services/BranchService.d.ts +0 -47
- package/dist/services/EntityFetchService.d.ts +0 -214
- package/dist/services/EntityPersistService.d.ts +0 -40
- package/dist/services/RelationService.d.ts +0 -98
- package/dist/services/entity-helpers.d.ts +0 -38
- package/dist/services/entityService.d.ts +0 -110
- package/dist/services/index.d.ts +0 -4
- package/dist/services/realtimeService.d.ts +0 -220
- package/dist/types.d.ts +0 -3
- package/dist/utils/drizzle-conditions.d.ts +0 -138
- package/dist/utils/pg-error-utils.d.ts +0 -55
- package/dist/websocket.d.ts +0 -11
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StringProperty,
|
|
3
|
+
NumberProperty,
|
|
4
|
+
ArrayProperty,
|
|
5
|
+
MapProperty,
|
|
6
|
+
Property,
|
|
7
|
+
VectorProperty,
|
|
8
|
+
BinaryProperty,
|
|
9
|
+
} from "@rebasepro/types";
|
|
10
|
+
import { getExpectedColumnType } from "../src/schema/doctor";
|
|
11
|
+
|
|
12
|
+
describe("Doctor — getExpectedColumnType extended coverage", () => {
|
|
13
|
+
|
|
14
|
+
// ── String property edge cases ──────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
describe("string property", () => {
|
|
17
|
+
|
|
18
|
+
it("should return 'uuid' when isId is 'uuid'", () => {
|
|
19
|
+
const prop: StringProperty = {
|
|
20
|
+
type: "string",
|
|
21
|
+
isId: "uuid",
|
|
22
|
+
};
|
|
23
|
+
expect(getExpectedColumnType(prop)).toBe("uuid");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should return 'character varying' for basic string (no options)", () => {
|
|
27
|
+
const prop: StringProperty = { type: "string" };
|
|
28
|
+
expect(getExpectedColumnType(prop)).toBe("character varying");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should return 'text' when columnType is 'text'", () => {
|
|
32
|
+
const prop: StringProperty = {
|
|
33
|
+
type: "string",
|
|
34
|
+
columnType: "text",
|
|
35
|
+
};
|
|
36
|
+
expect(getExpectedColumnType(prop)).toBe("text");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should return 'character' when columnType is 'char'", () => {
|
|
40
|
+
const prop: StringProperty = {
|
|
41
|
+
type: "string",
|
|
42
|
+
columnType: "char",
|
|
43
|
+
};
|
|
44
|
+
expect(getExpectedColumnType(prop)).toBe("character");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should return 'uuid' when columnType is 'uuid'", () => {
|
|
48
|
+
const prop: StringProperty = {
|
|
49
|
+
type: "string",
|
|
50
|
+
columnType: "uuid",
|
|
51
|
+
};
|
|
52
|
+
// Current implementation does NOT check columnType === 'uuid' (only checks isId === 'uuid')
|
|
53
|
+
// So this documents the current behavior:
|
|
54
|
+
// columnType 'uuid' falls through to the default 'character varying'
|
|
55
|
+
const result = getExpectedColumnType(prop);
|
|
56
|
+
// This is the actual behavior — columnType 'uuid' is not handled by doctor
|
|
57
|
+
expect(result).toBe("character varying");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should return 'USER-DEFINED' for enum string", () => {
|
|
61
|
+
const prop: StringProperty = {
|
|
62
|
+
type: "string",
|
|
63
|
+
enum: { active: "Active", inactive: "Inactive" },
|
|
64
|
+
};
|
|
65
|
+
expect(getExpectedColumnType(prop)).toBe("USER-DEFINED");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should prioritize enum over columnType", () => {
|
|
69
|
+
const prop: StringProperty = {
|
|
70
|
+
type: "string",
|
|
71
|
+
enum: ["a", "b"],
|
|
72
|
+
columnType: "text",
|
|
73
|
+
};
|
|
74
|
+
// enum check comes first in the switch
|
|
75
|
+
expect(getExpectedColumnType(prop)).toBe("USER-DEFINED");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should prioritize enum over isId", () => {
|
|
79
|
+
const prop: StringProperty = {
|
|
80
|
+
type: "string",
|
|
81
|
+
enum: { x: "X" },
|
|
82
|
+
isId: "uuid",
|
|
83
|
+
};
|
|
84
|
+
expect(getExpectedColumnType(prop)).toBe("USER-DEFINED");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should return 'character varying' for string with multiline: true", () => {
|
|
88
|
+
// multiline is a UI hint, doctor doesn't check it for type mapping
|
|
89
|
+
const prop: StringProperty = {
|
|
90
|
+
type: "string",
|
|
91
|
+
multiline: true,
|
|
92
|
+
};
|
|
93
|
+
const result = getExpectedColumnType(prop);
|
|
94
|
+
// Current behavior: multiline doesn't influence the column type mapping
|
|
95
|
+
expect(result).toBe("character varying");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should return 'character varying' for string with markdown: true", () => {
|
|
99
|
+
// markdown is a UI hint, doctor doesn't check it for type mapping
|
|
100
|
+
const prop: StringProperty = {
|
|
101
|
+
type: "string",
|
|
102
|
+
markdown: true,
|
|
103
|
+
};
|
|
104
|
+
const result = getExpectedColumnType(prop);
|
|
105
|
+
// Current behavior: markdown doesn't influence the column type mapping
|
|
106
|
+
expect(result).toBe("character varying");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should return 'character varying' for string with isId: true (boolean)", () => {
|
|
110
|
+
const prop: StringProperty = {
|
|
111
|
+
type: "string",
|
|
112
|
+
isId: true,
|
|
113
|
+
};
|
|
114
|
+
// isId: true (not "uuid") doesn't match isId === "uuid"
|
|
115
|
+
const result = getExpectedColumnType(prop);
|
|
116
|
+
expect(result).toBe("character varying");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should return 'uuid' for string with isId: 'uuid' even with columnType 'text'", () => {
|
|
120
|
+
const prop: StringProperty = {
|
|
121
|
+
type: "string",
|
|
122
|
+
isId: "uuid",
|
|
123
|
+
columnType: "text",
|
|
124
|
+
};
|
|
125
|
+
// isId: "uuid" check comes before columnType check
|
|
126
|
+
expect(getExpectedColumnType(prop)).toBe("uuid");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should return 'character varying' for string with isId: 'cuid'", () => {
|
|
130
|
+
const prop: StringProperty = {
|
|
131
|
+
type: "string",
|
|
132
|
+
isId: "cuid",
|
|
133
|
+
};
|
|
134
|
+
// Only "uuid" isId triggers special handling
|
|
135
|
+
expect(getExpectedColumnType(prop)).toBe("character varying");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should return 'character varying' for string with isId: 'manual'", () => {
|
|
139
|
+
const prop: StringProperty = {
|
|
140
|
+
type: "string",
|
|
141
|
+
isId: "manual",
|
|
142
|
+
};
|
|
143
|
+
expect(getExpectedColumnType(prop)).toBe("character varying");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ── Number property edge cases ──────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
describe("number property", () => {
|
|
150
|
+
|
|
151
|
+
it("should return 'integer' when isId is 'increment'", () => {
|
|
152
|
+
const prop: NumberProperty = {
|
|
153
|
+
type: "number",
|
|
154
|
+
isId: "increment",
|
|
155
|
+
};
|
|
156
|
+
// isId is truthy so the last condition matches: ("isId" in np && np.isId) → integer
|
|
157
|
+
expect(getExpectedColumnType(prop)).toBe("integer");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should return 'integer' when isId is true (boolean)", () => {
|
|
161
|
+
const prop: NumberProperty = {
|
|
162
|
+
type: "number",
|
|
163
|
+
isId: true,
|
|
164
|
+
};
|
|
165
|
+
expect(getExpectedColumnType(prop)).toBe("integer");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should return 'integer' when isId is 'manual'", () => {
|
|
169
|
+
const prop: NumberProperty = {
|
|
170
|
+
type: "number",
|
|
171
|
+
isId: "manual",
|
|
172
|
+
};
|
|
173
|
+
// "manual" is truthy, so ("isId" in np && np.isId) → integer
|
|
174
|
+
expect(getExpectedColumnType(prop)).toBe("integer");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should return 'numeric' for plain number without any options", () => {
|
|
178
|
+
const prop: NumberProperty = { type: "number" };
|
|
179
|
+
expect(getExpectedColumnType(prop)).toBe("numeric");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should return 'integer' when validation.integer is true", () => {
|
|
183
|
+
const prop: NumberProperty = {
|
|
184
|
+
type: "number",
|
|
185
|
+
validation: { integer: true },
|
|
186
|
+
};
|
|
187
|
+
expect(getExpectedColumnType(prop)).toBe("integer");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should return 'integer' for serial columnType", () => {
|
|
191
|
+
const prop: NumberProperty = {
|
|
192
|
+
type: "number",
|
|
193
|
+
columnType: "serial",
|
|
194
|
+
};
|
|
195
|
+
// serial maps to integer in information_schema
|
|
196
|
+
expect(getExpectedColumnType(prop)).toBe("integer");
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("should return 'bigint' for bigserial columnType", () => {
|
|
200
|
+
const prop: NumberProperty = {
|
|
201
|
+
type: "number",
|
|
202
|
+
columnType: "bigserial",
|
|
203
|
+
};
|
|
204
|
+
expect(getExpectedColumnType(prop)).toBe("bigint");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should return 'bigint' for bigint columnType", () => {
|
|
208
|
+
const prop: NumberProperty = {
|
|
209
|
+
type: "number",
|
|
210
|
+
columnType: "bigint",
|
|
211
|
+
};
|
|
212
|
+
expect(getExpectedColumnType(prop)).toBe("bigint");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should return 'numeric' for numeric columnType", () => {
|
|
216
|
+
const prop: NumberProperty = {
|
|
217
|
+
type: "number",
|
|
218
|
+
columnType: "numeric",
|
|
219
|
+
};
|
|
220
|
+
expect(getExpectedColumnType(prop)).toBe("numeric");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should return 'real' for real columnType", () => {
|
|
224
|
+
const prop: NumberProperty = {
|
|
225
|
+
type: "number",
|
|
226
|
+
columnType: "real",
|
|
227
|
+
};
|
|
228
|
+
expect(getExpectedColumnType(prop)).toBe("real");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should return 'double precision' for double precision columnType", () => {
|
|
232
|
+
const prop: NumberProperty = {
|
|
233
|
+
type: "number",
|
|
234
|
+
columnType: "double precision",
|
|
235
|
+
};
|
|
236
|
+
expect(getExpectedColumnType(prop)).toBe("double precision");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should prioritize columnType over validation.integer", () => {
|
|
240
|
+
const prop: NumberProperty = {
|
|
241
|
+
type: "number",
|
|
242
|
+
columnType: "numeric",
|
|
243
|
+
validation: { integer: true },
|
|
244
|
+
};
|
|
245
|
+
// columnType checks come before validation checks
|
|
246
|
+
expect(getExpectedColumnType(prop)).toBe("numeric");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("should prioritize columnType 'real' over isId", () => {
|
|
250
|
+
const prop: NumberProperty = {
|
|
251
|
+
type: "number",
|
|
252
|
+
columnType: "real",
|
|
253
|
+
isId: true,
|
|
254
|
+
};
|
|
255
|
+
// columnType check for "real" comes before isId check
|
|
256
|
+
expect(getExpectedColumnType(prop)).toBe("real");
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// ── Boolean property ────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
describe("boolean property", () => {
|
|
263
|
+
it("should return 'boolean'", () => {
|
|
264
|
+
expect(getExpectedColumnType({ type: "boolean" })).toBe("boolean");
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// ── Date property edge cases ────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
describe("date property", () => {
|
|
271
|
+
it("should return 'timestamp with time zone' for default date", () => {
|
|
272
|
+
expect(getExpectedColumnType({ type: "date" })).toBe("timestamp with time zone");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should return 'date' for columnType 'date'", () => {
|
|
276
|
+
expect(getExpectedColumnType({ type: "date", columnType: "date" } as Property)).toBe("date");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should return 'time without time zone' for columnType 'time'", () => {
|
|
280
|
+
expect(getExpectedColumnType({ type: "date", columnType: "time" } as Property)).toBe("time without time zone");
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// ── Reference property ──────────────────────────────────────────────
|
|
285
|
+
|
|
286
|
+
describe("reference property", () => {
|
|
287
|
+
it("should return 'character varying' for reference type", () => {
|
|
288
|
+
const prop: Property = {
|
|
289
|
+
type: "reference",
|
|
290
|
+
path: "some_collection",
|
|
291
|
+
} as Property;
|
|
292
|
+
expect(getExpectedColumnType(prop)).toBe("character varying");
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ── Vector property ─────────────────────────────────────────────────
|
|
297
|
+
|
|
298
|
+
describe("vector property", () => {
|
|
299
|
+
it("should return 'USER-DEFINED' for vector type", () => {
|
|
300
|
+
const prop: VectorProperty = {
|
|
301
|
+
type: "vector",
|
|
302
|
+
dimensions: 768,
|
|
303
|
+
};
|
|
304
|
+
expect(getExpectedColumnType(prop)).toBe("USER-DEFINED");
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("should return 'USER-DEFINED' for vector with any dimensions", () => {
|
|
308
|
+
const prop: VectorProperty = {
|
|
309
|
+
type: "vector",
|
|
310
|
+
dimensions: 1536,
|
|
311
|
+
};
|
|
312
|
+
expect(getExpectedColumnType(prop)).toBe("USER-DEFINED");
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// ── Binary property ─────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
describe("binary property", () => {
|
|
319
|
+
it("should return 'bytea' for binary type", () => {
|
|
320
|
+
const prop: BinaryProperty = {
|
|
321
|
+
type: "binary",
|
|
322
|
+
};
|
|
323
|
+
expect(getExpectedColumnType(prop)).toBe("bytea");
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// ── Relation property ───────────────────────────────────────────────
|
|
328
|
+
|
|
329
|
+
describe("relation property", () => {
|
|
330
|
+
it("should return null for relation type", () => {
|
|
331
|
+
expect(getExpectedColumnType({ type: "relation" } as Property)).toBeNull();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ── Array property edge cases ───────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
describe("array property", () => {
|
|
338
|
+
it("should return 'jsonb' for array with no columnType and no of", () => {
|
|
339
|
+
const prop: ArrayProperty = { type: "array" } as ArrayProperty;
|
|
340
|
+
expect(getExpectedColumnType(prop)).toBe("jsonb");
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should return 'json' for array with columnType 'json'", () => {
|
|
344
|
+
const prop: ArrayProperty = {
|
|
345
|
+
type: "array",
|
|
346
|
+
columnType: "json",
|
|
347
|
+
} as ArrayProperty;
|
|
348
|
+
expect(getExpectedColumnType(prop)).toBe("json");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("should return 'jsonb' for array with columnType 'jsonb'", () => {
|
|
352
|
+
const prop: ArrayProperty = {
|
|
353
|
+
type: "array",
|
|
354
|
+
columnType: "jsonb",
|
|
355
|
+
} as ArrayProperty;
|
|
356
|
+
expect(getExpectedColumnType(prop)).toBe("jsonb");
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it("should return 'ARRAY' for array of strings", () => {
|
|
360
|
+
const prop: ArrayProperty = {
|
|
361
|
+
type: "array",
|
|
362
|
+
of: { type: "string" },
|
|
363
|
+
} as ArrayProperty;
|
|
364
|
+
expect(getExpectedColumnType(prop)).toBe("ARRAY");
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("should return 'ARRAY' for array of integers", () => {
|
|
368
|
+
const prop: ArrayProperty = {
|
|
369
|
+
type: "array",
|
|
370
|
+
of: { type: "number", validation: { integer: true } },
|
|
371
|
+
} as ArrayProperty;
|
|
372
|
+
expect(getExpectedColumnType(prop)).toBe("ARRAY");
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("should return 'ARRAY' for array of booleans", () => {
|
|
376
|
+
const prop: ArrayProperty = {
|
|
377
|
+
type: "array",
|
|
378
|
+
of: { type: "boolean" },
|
|
379
|
+
} as ArrayProperty;
|
|
380
|
+
expect(getExpectedColumnType(prop)).toBe("ARRAY");
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it("should return 'jsonb' for array of numbers (non-integer)", () => {
|
|
384
|
+
const prop: ArrayProperty = {
|
|
385
|
+
type: "array",
|
|
386
|
+
of: { type: "number" },
|
|
387
|
+
} as ArrayProperty;
|
|
388
|
+
// numeric[] → "ARRAY"
|
|
389
|
+
expect(getExpectedColumnType(prop)).toBe("ARRAY");
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("should return 'jsonb' for array of maps (complex type, no native array support)", () => {
|
|
393
|
+
const prop: ArrayProperty = {
|
|
394
|
+
type: "array",
|
|
395
|
+
of: { type: "map" },
|
|
396
|
+
} as ArrayProperty;
|
|
397
|
+
// map type doesn't match string/number/boolean → colType remains undefined → jsonb
|
|
398
|
+
expect(getExpectedColumnType(prop)).toBe("jsonb");
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("should return 'jsonb' for array with Array.isArray(of) being true", () => {
|
|
402
|
+
const prop: ArrayProperty = {
|
|
403
|
+
type: "array",
|
|
404
|
+
of: [{ type: "string" }],
|
|
405
|
+
} as ArrayProperty;
|
|
406
|
+
// Array.isArray(ap.of) → true → doesn't enter the single-of branch → jsonb
|
|
407
|
+
expect(getExpectedColumnType(prop)).toBe("jsonb");
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// ── Map property edge cases ─────────────────────────────────────────
|
|
412
|
+
|
|
413
|
+
describe("map property", () => {
|
|
414
|
+
it("should return 'jsonb' for default map", () => {
|
|
415
|
+
const prop: MapProperty = { type: "map" } as MapProperty;
|
|
416
|
+
expect(getExpectedColumnType(prop)).toBe("jsonb");
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("should return 'json' for map with columnType 'json'", () => {
|
|
420
|
+
const prop: MapProperty = {
|
|
421
|
+
type: "map",
|
|
422
|
+
columnType: "json",
|
|
423
|
+
};
|
|
424
|
+
expect(getExpectedColumnType(prop)).toBe("json");
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should return 'jsonb' for map with columnType 'jsonb'", () => {
|
|
428
|
+
const prop: MapProperty = {
|
|
429
|
+
type: "map",
|
|
430
|
+
columnType: "jsonb",
|
|
431
|
+
};
|
|
432
|
+
expect(getExpectedColumnType(prop)).toBe("jsonb");
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// ── Unknown / unsupported types ─────────────────────────────────────
|
|
437
|
+
|
|
438
|
+
describe("unknown types", () => {
|
|
439
|
+
it("should return null for geopoint type", () => {
|
|
440
|
+
expect(getExpectedColumnType({ type: "geopoint" } as Property)).toBeNull();
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
});
|