@smartive/graphql-magic 23.6.1-next.1 → 23.6.1-next.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
- ## [23.6.1-next.1](https://github.com/smartive/graphql-magic/compare/v23.6.0...v23.6.1-next.1) (2026-02-24)
1
+ ## [23.6.1-next.2](https://github.com/smartive/graphql-magic/compare/v23.6.1-next.1...v23.6.1-next.2) (2026-02-24)
2
2
 
3
3
  ### Bug Fixes
4
4
 
5
- * exclude expression from DB types and all generateAs from upserts ([adfa7fc](https://github.com/smartive/graphql-magic/commit/adfa7fc10b2fd84ae951840d1dc553d50f78bc3e))
5
+ * Correct mock module name in unit tests for generateAs functionality ([bcf21db](https://github.com/smartive/graphql-magic/commit/bcf21dbcd083340cb07bf10080f6390f4b370bc2))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartive/graphql-magic",
3
- "version": "23.6.1-next.1",
3
+ "version": "23.6.1-next.2",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,238 @@
1
+ import { Kind } from 'graphql';
2
+ import { generateDBModels } from '../../src/db/generate';
3
+ import {
4
+ isGenerateAsField,
5
+ isStoredInDatabase,
6
+ } from '../../src/models/utils';
7
+ import { ModelDefinitions, Models } from '../../src/models';
8
+ import type { EntityField } from '../../src/models/models';
9
+ import { generateDefinitions } from '../../src/schema/generate';
10
+
11
+ jest.mock('code-block-writer', () => {
12
+ const Writer = class {
13
+ private _out = '';
14
+
15
+ write(s: string) {
16
+ this._out += s;
17
+ return this;
18
+ }
19
+
20
+ blankLine() {
21
+ this._out += '\n';
22
+ return this;
23
+ }
24
+
25
+ newLine() {
26
+ this._out += '\n';
27
+ return this;
28
+ }
29
+
30
+ inlineBlock(fn: () => void) {
31
+ this._out += ' {\n';
32
+ fn();
33
+ this._out += '}';
34
+ return this;
35
+ }
36
+
37
+ toString() {
38
+ return this._out;
39
+ }
40
+ };
41
+ return { __esModule: true, default: { default: Writer } };
42
+ });
43
+
44
+ describe('generateAs helpers', () => {
45
+ describe('isGenerateAsField', () => {
46
+ it('returns false when field.generateAs is undefined', () => {
47
+ const field: EntityField = { name: 'price', type: 'Float' };
48
+ expect(isGenerateAsField(field)).toBe(false);
49
+ });
50
+
51
+ it('returns true when field has generateAs with type expression', () => {
52
+ const field: EntityField = {
53
+ name: 'total',
54
+ type: 'Float',
55
+ generateAs: { expression: 'price * quantity', type: 'expression' },
56
+ };
57
+ expect(isGenerateAsField(field)).toBe(true);
58
+ });
59
+
60
+ it('returns true when field has generateAs with type stored', () => {
61
+ const field: EntityField = {
62
+ name: 'total',
63
+ type: 'Float',
64
+ generateAs: { expression: 'price * quantity', type: 'stored' },
65
+ };
66
+ expect(isGenerateAsField(field)).toBe(true);
67
+ });
68
+
69
+ it('returns true when field has generateAs with type virtual', () => {
70
+ const field: EntityField = {
71
+ name: 'total',
72
+ type: 'Float',
73
+ generateAs: { expression: 'price * quantity', type: 'virtual' },
74
+ };
75
+ expect(isGenerateAsField(field)).toBe(true);
76
+ });
77
+ });
78
+
79
+ describe('isStoredInDatabase', () => {
80
+ it('returns true when field has no generateAs (normal column)', () => {
81
+ const field: EntityField = { name: 'price', type: 'Float' };
82
+ expect(isStoredInDatabase(field)).toBe(true);
83
+ });
84
+
85
+ it('returns false when field has generateAs.type expression', () => {
86
+ const field: EntityField = {
87
+ name: 'total',
88
+ type: 'Float',
89
+ generateAs: { expression: 'price * quantity', type: 'expression' },
90
+ };
91
+ expect(isStoredInDatabase(field)).toBe(false);
92
+ });
93
+
94
+ it('returns true when field has generateAs.type stored', () => {
95
+ const field: EntityField = {
96
+ name: 'total',
97
+ type: 'Float',
98
+ generateAs: { expression: 'price * quantity', type: 'stored' },
99
+ };
100
+ expect(isStoredInDatabase(field)).toBe(true);
101
+ });
102
+
103
+ it('returns true when field has generateAs.type virtual', () => {
104
+ const field: EntityField = {
105
+ name: 'total',
106
+ type: 'Float',
107
+ generateAs: { expression: 'price * quantity', type: 'virtual' },
108
+ };
109
+ expect(isStoredInDatabase(field)).toBe(true);
110
+ });
111
+ });
112
+ });
113
+
114
+ describe('generateDBModels', () => {
115
+ const productModelDefinitions: ModelDefinitions = [
116
+ {
117
+ kind: 'entity',
118
+ name: 'Product',
119
+ fields: [
120
+ { name: 'price', type: 'Float' },
121
+ { name: 'quantity', type: 'Int' },
122
+ {
123
+ name: 'totalExpression',
124
+ type: 'Float',
125
+ generateAs: { expression: 'price * quantity', type: 'expression' },
126
+ },
127
+ {
128
+ name: 'totalStored',
129
+ type: 'Float',
130
+ generateAs: { expression: 'price * quantity', type: 'stored' },
131
+ },
132
+ ],
133
+ },
134
+ ];
135
+
136
+ it('entity type includes normal and stored columns but not expression-only fields', () => {
137
+ const models = new Models(productModelDefinitions);
138
+ const output = generateDBModels(models, 'luxon');
139
+
140
+ expect(output).toContain("'price'");
141
+ expect(output).toContain("'quantity'");
142
+ expect(output).toContain("'totalStored'");
143
+ expect(output).not.toContain("'totalExpression'");
144
+ });
145
+
146
+ it('Initializer includes only user-settable fields (excludes all generateAs)', () => {
147
+ const models = new Models(productModelDefinitions);
148
+ const output = generateDBModels(models, 'luxon');
149
+
150
+ const start = output.indexOf('export type ProductInitializer');
151
+ const end = output.indexOf('export type', start + 1);
152
+ const block = end === -1 ? output.slice(start) : output.slice(start, end);
153
+ expect(block).toContain("'price'");
154
+ expect(block).toContain("'quantity'");
155
+ expect(block).not.toContain("'totalExpression'");
156
+ expect(block).not.toContain("'totalStored'");
157
+ });
158
+
159
+ it('Mutator includes only user-settable fields (excludes all generateAs)', () => {
160
+ const models = new Models(productModelDefinitions);
161
+ const output = generateDBModels(models, 'luxon');
162
+
163
+ const start = output.indexOf('export type ProductMutator');
164
+ const end = output.indexOf('export type', start + 1);
165
+ const block = end === -1 ? output.slice(start) : output.slice(start, end);
166
+ expect(block).toContain("'price'");
167
+ expect(block).toContain("'quantity'");
168
+ expect(block).not.toContain("'totalExpression'");
169
+ expect(block).not.toContain("'totalStored'");
170
+ });
171
+
172
+ it('Seed type excludes all generateAs fields', () => {
173
+ const models = new Models(productModelDefinitions);
174
+ const output = generateDBModels(models, 'luxon');
175
+
176
+ const start = output.indexOf('export type ProductSeed');
177
+ const end = output.indexOf('export type', start + 1);
178
+ const block = end === -1 ? output.slice(start) : output.slice(start, end);
179
+ expect(block).toContain("'price'");
180
+ expect(block).toContain("'quantity'");
181
+ expect(block).not.toContain("'totalExpression'");
182
+ expect(block).not.toContain("'totalStored'");
183
+ });
184
+ });
185
+
186
+ describe('generateDefinitions Create/Update inputs', () => {
187
+ const itemModelDefinitions: ModelDefinitions = [
188
+ { kind: 'entity', name: 'User', fields: [{ name: 'id', type: 'ID' }] },
189
+ {
190
+ kind: 'entity',
191
+ name: 'Item',
192
+ creatable: true,
193
+ updatable: true,
194
+ fields: [
195
+ { name: 'name', type: 'String', creatable: true, updatable: true },
196
+ {
197
+ name: 'computed',
198
+ type: 'Int',
199
+ creatable: true,
200
+ updatable: true,
201
+ generateAs: { expression: '1', type: 'expression' },
202
+ },
203
+ ],
204
+ },
205
+ ];
206
+
207
+ it('Create input does not include generateAs fields', () => {
208
+ const models = new Models(itemModelDefinitions);
209
+ const definitions = generateDefinitions(models);
210
+
211
+ const createInput = definitions.find(
212
+ (d) => d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && d.name?.value === 'CreateItem',
213
+ );
214
+ expect(createInput).toBeDefined();
215
+ expect(createInput!.kind).toBe(Kind.INPUT_OBJECT_TYPE_DEFINITION);
216
+
217
+ const fields = (createInput as { fields?: readonly { name: { value: string } }[] }).fields ?? [];
218
+ const fieldNames = fields.map((f) => f.name.value);
219
+ expect(fieldNames).toContain('name');
220
+ expect(fieldNames).not.toContain('computed');
221
+ });
222
+
223
+ it('Update input does not include generateAs fields', () => {
224
+ const models = new Models(itemModelDefinitions);
225
+ const definitions = generateDefinitions(models);
226
+
227
+ const updateInput = definitions.find(
228
+ (d) => d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && d.name?.value === 'UpdateItem',
229
+ );
230
+ expect(updateInput).toBeDefined();
231
+ expect(updateInput!.kind).toBe(Kind.INPUT_OBJECT_TYPE_DEFINITION);
232
+
233
+ const fields = (updateInput as { fields?: readonly { name: { value: string } }[] }).fields ?? [];
234
+ const fieldNames = fields.map((f) => f.name.value);
235
+ expect(fieldNames).toContain('name');
236
+ expect(fieldNames).not.toContain('computed');
237
+ });
238
+ });