@objectstack/objectql 3.1.1 → 3.2.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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +9 -0
- package/dist/index.d.mts +60 -1
- package/dist/index.d.ts +60 -1
- package/package.json +4 -4
- package/src/protocol-data.test.ts +242 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/objectql@3.
|
|
2
|
+
> @objectstack/objectql@3.2.0 build /home/runner/work/spec/spec/packages/objectql
|
|
3
3
|
> tsup --config ../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m95.52 KB[39m
|
|
14
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m193.28 KB[39m
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in 194ms
|
|
16
13
|
[32mESM[39m [1mdist/index.mjs [22m[32m93.78 KB[39m
|
|
17
14
|
[32mESM[39m [1mdist/index.mjs.map [22m[32m191.98 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 179ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.js [22m[32m95.52 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m193.28 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 181ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 22038ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m77.32 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m77.32 KB[39m
|
package/CHANGELOG.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -462,6 +462,65 @@ declare class SchemaRegistry {
|
|
|
462
462
|
recordTypes?: string[] | undefined;
|
|
463
463
|
sharingModel?: "full" | "private" | "read" | "read_write" | undefined;
|
|
464
464
|
keyPrefix?: string | undefined;
|
|
465
|
+
actions?: {
|
|
466
|
+
name: string;
|
|
467
|
+
label: string | {
|
|
468
|
+
key: string;
|
|
469
|
+
defaultValue?: string | undefined;
|
|
470
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
471
|
+
};
|
|
472
|
+
type: "url" | "script" | "modal" | "flow" | "api";
|
|
473
|
+
refreshAfter: boolean;
|
|
474
|
+
objectName?: string | undefined;
|
|
475
|
+
icon?: string | undefined;
|
|
476
|
+
locations?: ("list_toolbar" | "list_item" | "record_header" | "record_more" | "record_related" | "global_nav")[] | undefined;
|
|
477
|
+
component?: "action:button" | "action:icon" | "action:menu" | "action:group" | undefined;
|
|
478
|
+
target?: string | undefined;
|
|
479
|
+
execute?: string | undefined;
|
|
480
|
+
params?: {
|
|
481
|
+
name: string;
|
|
482
|
+
label: string | {
|
|
483
|
+
key: string;
|
|
484
|
+
defaultValue?: string | undefined;
|
|
485
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
486
|
+
};
|
|
487
|
+
type: "number" | "boolean" | "date" | "lookup" | "file" | "url" | "json" | "text" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "code" | "color" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "tags" | "vector";
|
|
488
|
+
required: boolean;
|
|
489
|
+
options?: {
|
|
490
|
+
label: string | {
|
|
491
|
+
key: string;
|
|
492
|
+
defaultValue?: string | undefined;
|
|
493
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
494
|
+
};
|
|
495
|
+
value: string;
|
|
496
|
+
}[] | undefined;
|
|
497
|
+
}[] | undefined;
|
|
498
|
+
variant?: "link" | "primary" | "secondary" | "danger" | "ghost" | undefined;
|
|
499
|
+
confirmText?: string | {
|
|
500
|
+
key: string;
|
|
501
|
+
defaultValue?: string | undefined;
|
|
502
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
503
|
+
} | undefined;
|
|
504
|
+
successMessage?: string | {
|
|
505
|
+
key: string;
|
|
506
|
+
defaultValue?: string | undefined;
|
|
507
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
508
|
+
} | undefined;
|
|
509
|
+
visible?: string | undefined;
|
|
510
|
+
disabled?: string | boolean | undefined;
|
|
511
|
+
shortcut?: string | undefined;
|
|
512
|
+
bulkEnabled?: boolean | undefined;
|
|
513
|
+
timeout?: number | undefined;
|
|
514
|
+
aria?: {
|
|
515
|
+
ariaLabel?: string | {
|
|
516
|
+
key: string;
|
|
517
|
+
defaultValue?: string | undefined;
|
|
518
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
519
|
+
} | undefined;
|
|
520
|
+
ariaDescribedBy?: string | undefined;
|
|
521
|
+
role?: string | undefined;
|
|
522
|
+
} | undefined;
|
|
523
|
+
}[] | undefined;
|
|
465
524
|
} | {
|
|
466
525
|
name: string;
|
|
467
526
|
label: string | {
|
|
@@ -845,7 +904,7 @@ declare class SchemaRegistry {
|
|
|
845
904
|
objectstack: string;
|
|
846
905
|
} | undefined;
|
|
847
906
|
};
|
|
848
|
-
status: "
|
|
907
|
+
status: "disabled" | "error" | "installed" | "installing" | "upgrading" | "uninstalling";
|
|
849
908
|
enabled: boolean;
|
|
850
909
|
installedAt?: string | undefined;
|
|
851
910
|
updatedAt?: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -462,6 +462,65 @@ declare class SchemaRegistry {
|
|
|
462
462
|
recordTypes?: string[] | undefined;
|
|
463
463
|
sharingModel?: "full" | "private" | "read" | "read_write" | undefined;
|
|
464
464
|
keyPrefix?: string | undefined;
|
|
465
|
+
actions?: {
|
|
466
|
+
name: string;
|
|
467
|
+
label: string | {
|
|
468
|
+
key: string;
|
|
469
|
+
defaultValue?: string | undefined;
|
|
470
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
471
|
+
};
|
|
472
|
+
type: "url" | "script" | "modal" | "flow" | "api";
|
|
473
|
+
refreshAfter: boolean;
|
|
474
|
+
objectName?: string | undefined;
|
|
475
|
+
icon?: string | undefined;
|
|
476
|
+
locations?: ("list_toolbar" | "list_item" | "record_header" | "record_more" | "record_related" | "global_nav")[] | undefined;
|
|
477
|
+
component?: "action:button" | "action:icon" | "action:menu" | "action:group" | undefined;
|
|
478
|
+
target?: string | undefined;
|
|
479
|
+
execute?: string | undefined;
|
|
480
|
+
params?: {
|
|
481
|
+
name: string;
|
|
482
|
+
label: string | {
|
|
483
|
+
key: string;
|
|
484
|
+
defaultValue?: string | undefined;
|
|
485
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
486
|
+
};
|
|
487
|
+
type: "number" | "boolean" | "date" | "lookup" | "file" | "url" | "json" | "text" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "code" | "color" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "tags" | "vector";
|
|
488
|
+
required: boolean;
|
|
489
|
+
options?: {
|
|
490
|
+
label: string | {
|
|
491
|
+
key: string;
|
|
492
|
+
defaultValue?: string | undefined;
|
|
493
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
494
|
+
};
|
|
495
|
+
value: string;
|
|
496
|
+
}[] | undefined;
|
|
497
|
+
}[] | undefined;
|
|
498
|
+
variant?: "link" | "primary" | "secondary" | "danger" | "ghost" | undefined;
|
|
499
|
+
confirmText?: string | {
|
|
500
|
+
key: string;
|
|
501
|
+
defaultValue?: string | undefined;
|
|
502
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
503
|
+
} | undefined;
|
|
504
|
+
successMessage?: string | {
|
|
505
|
+
key: string;
|
|
506
|
+
defaultValue?: string | undefined;
|
|
507
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
508
|
+
} | undefined;
|
|
509
|
+
visible?: string | undefined;
|
|
510
|
+
disabled?: string | boolean | undefined;
|
|
511
|
+
shortcut?: string | undefined;
|
|
512
|
+
bulkEnabled?: boolean | undefined;
|
|
513
|
+
timeout?: number | undefined;
|
|
514
|
+
aria?: {
|
|
515
|
+
ariaLabel?: string | {
|
|
516
|
+
key: string;
|
|
517
|
+
defaultValue?: string | undefined;
|
|
518
|
+
params?: Record<string, string | number | boolean> | undefined;
|
|
519
|
+
} | undefined;
|
|
520
|
+
ariaDescribedBy?: string | undefined;
|
|
521
|
+
role?: string | undefined;
|
|
522
|
+
} | undefined;
|
|
523
|
+
}[] | undefined;
|
|
465
524
|
} | {
|
|
466
525
|
name: string;
|
|
467
526
|
label: string | {
|
|
@@ -845,7 +904,7 @@ declare class SchemaRegistry {
|
|
|
845
904
|
objectstack: string;
|
|
846
905
|
} | undefined;
|
|
847
906
|
};
|
|
848
|
-
status: "
|
|
907
|
+
status: "disabled" | "error" | "installed" | "installing" | "upgrading" | "uninstalling";
|
|
849
908
|
enabled: boolean;
|
|
850
909
|
installedAt?: string | undefined;
|
|
851
910
|
updatedAt?: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/objectql",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Isomorphic ObjectQL Engine for ObjectStack",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@objectstack/core": "3.
|
|
17
|
-
"@objectstack/spec": "3.
|
|
18
|
-
"@objectstack/types": "3.
|
|
16
|
+
"@objectstack/core": "3.2.0",
|
|
17
|
+
"@objectstack/spec": "3.2.0",
|
|
18
|
+
"@objectstack/types": "3.2.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"typescript": "^5.0.0",
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
+
import { ObjectStackProtocolImplementation } from './protocol.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Tests for the Protocol Implementation's data methods (findData, getData).
|
|
8
|
+
* Validates that expand/populate/select parameters are correctly normalized
|
|
9
|
+
* and forwarded to the underlying engine.
|
|
10
|
+
*/
|
|
11
|
+
describe('ObjectStackProtocolImplementation - Data Operations', () => {
|
|
12
|
+
let protocol: ObjectStackProtocolImplementation;
|
|
13
|
+
let mockEngine: any;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
mockEngine = {
|
|
17
|
+
find: vi.fn().mockResolvedValue([]),
|
|
18
|
+
findOne: vi.fn().mockResolvedValue(null),
|
|
19
|
+
};
|
|
20
|
+
protocol = new ObjectStackProtocolImplementation(mockEngine);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════
|
|
24
|
+
// findData — expand/populate normalization
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
describe('findData', () => {
|
|
28
|
+
it('should normalize expand string to populate array', async () => {
|
|
29
|
+
await protocol.findData({ object: 'order_item', query: { expand: 'order,product' } });
|
|
30
|
+
|
|
31
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
32
|
+
'order_item',
|
|
33
|
+
expect.objectContaining({
|
|
34
|
+
populate: ['order', 'product'],
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
// expand should be deleted from options
|
|
38
|
+
const callArgs = mockEngine.find.mock.calls[0][1];
|
|
39
|
+
expect(callArgs.expand).toBeUndefined();
|
|
40
|
+
expect(callArgs.$expand).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should normalize $expand (OData) to populate array', async () => {
|
|
44
|
+
await protocol.findData({ object: 'task', query: { $expand: 'assignee,project' } });
|
|
45
|
+
|
|
46
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
47
|
+
'task',
|
|
48
|
+
expect.objectContaining({
|
|
49
|
+
populate: ['assignee', 'project'],
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should pass populate array as-is if already an array', async () => {
|
|
55
|
+
await protocol.findData({ object: 'task', query: { populate: ['assignee'] } });
|
|
56
|
+
|
|
57
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
58
|
+
'task',
|
|
59
|
+
expect.objectContaining({
|
|
60
|
+
populate: ['assignee'],
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should normalize populate string to array', async () => {
|
|
66
|
+
await protocol.findData({ object: 'task', query: { populate: 'assignee,project' } });
|
|
67
|
+
|
|
68
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
69
|
+
'task',
|
|
70
|
+
expect.objectContaining({
|
|
71
|
+
populate: ['assignee', 'project'],
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should prefer explicit populate over expand', async () => {
|
|
77
|
+
await protocol.findData({
|
|
78
|
+
object: 'task',
|
|
79
|
+
query: { populate: ['assignee'], expand: 'project' },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// populate takes precedence; expand is not converted
|
|
83
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
84
|
+
'task',
|
|
85
|
+
expect.objectContaining({
|
|
86
|
+
populate: ['assignee'],
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should normalize expand array to populate array', async () => {
|
|
92
|
+
await protocol.findData({ object: 'task', query: { expand: ['owner', 'team'] } });
|
|
93
|
+
|
|
94
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
95
|
+
'task',
|
|
96
|
+
expect.objectContaining({
|
|
97
|
+
populate: ['owner', 'team'],
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should normalize select string to array', async () => {
|
|
103
|
+
await protocol.findData({ object: 'task', query: { select: 'name,status,assignee' } });
|
|
104
|
+
|
|
105
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
106
|
+
'task',
|
|
107
|
+
expect.objectContaining({
|
|
108
|
+
select: ['name', 'status', 'assignee'],
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should pass numeric pagination params correctly', async () => {
|
|
114
|
+
await protocol.findData({ object: 'task', query: { top: '10', skip: '20' } });
|
|
115
|
+
|
|
116
|
+
expect(mockEngine.find).toHaveBeenCalledWith(
|
|
117
|
+
'task',
|
|
118
|
+
expect.objectContaining({
|
|
119
|
+
top: 10,
|
|
120
|
+
skip: 20,
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should work with no query options', async () => {
|
|
126
|
+
await protocol.findData({ object: 'task' });
|
|
127
|
+
|
|
128
|
+
expect(mockEngine.find).toHaveBeenCalledWith('task', {});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should return records and standard response shape', async () => {
|
|
132
|
+
mockEngine.find.mockResolvedValue([{ _id: 't1', name: 'Task 1' }]);
|
|
133
|
+
|
|
134
|
+
const result = await protocol.findData({ object: 'task', query: {} });
|
|
135
|
+
|
|
136
|
+
expect(result).toEqual(
|
|
137
|
+
expect.objectContaining({
|
|
138
|
+
object: 'task',
|
|
139
|
+
records: [{ _id: 't1', name: 'Task 1' }],
|
|
140
|
+
total: 1,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ═══════════════════════════════════════════════════════════════
|
|
147
|
+
// getData — expand/select normalization
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════
|
|
149
|
+
|
|
150
|
+
describe('getData', () => {
|
|
151
|
+
it('should convert expand string to populate array', async () => {
|
|
152
|
+
mockEngine.findOne.mockResolvedValue({ _id: 'oi_1', name: 'Item 1' });
|
|
153
|
+
|
|
154
|
+
await protocol.getData({ object: 'order_item', id: 'oi_1', expand: 'order,product' });
|
|
155
|
+
|
|
156
|
+
expect(mockEngine.findOne).toHaveBeenCalledWith(
|
|
157
|
+
'order_item',
|
|
158
|
+
expect.objectContaining({
|
|
159
|
+
filter: { _id: 'oi_1' },
|
|
160
|
+
populate: ['order', 'product'],
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should convert expand array to populate array', async () => {
|
|
166
|
+
mockEngine.findOne.mockResolvedValue({ _id: 't1' });
|
|
167
|
+
|
|
168
|
+
await protocol.getData({ object: 'task', id: 't1', expand: ['assignee', 'project'] });
|
|
169
|
+
|
|
170
|
+
expect(mockEngine.findOne).toHaveBeenCalledWith(
|
|
171
|
+
'task',
|
|
172
|
+
expect.objectContaining({
|
|
173
|
+
populate: ['assignee', 'project'],
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should convert select string to array', async () => {
|
|
179
|
+
mockEngine.findOne.mockResolvedValue({ _id: 't1', name: 'Test' });
|
|
180
|
+
|
|
181
|
+
await protocol.getData({ object: 'task', id: 't1', select: 'name,status' });
|
|
182
|
+
|
|
183
|
+
expect(mockEngine.findOne).toHaveBeenCalledWith(
|
|
184
|
+
'task',
|
|
185
|
+
expect.objectContaining({
|
|
186
|
+
select: ['name', 'status'],
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should pass both expand and select together', async () => {
|
|
192
|
+
mockEngine.findOne.mockResolvedValue({ _id: 'oi_1' });
|
|
193
|
+
|
|
194
|
+
await protocol.getData({
|
|
195
|
+
object: 'order_item',
|
|
196
|
+
id: 'oi_1',
|
|
197
|
+
expand: 'order',
|
|
198
|
+
select: ['name', 'total'],
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(mockEngine.findOne).toHaveBeenCalledWith(
|
|
202
|
+
'order_item',
|
|
203
|
+
expect.objectContaining({
|
|
204
|
+
filter: { _id: 'oi_1' },
|
|
205
|
+
populate: ['order'],
|
|
206
|
+
select: ['name', 'total'],
|
|
207
|
+
}),
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should work without expand or select', async () => {
|
|
212
|
+
mockEngine.findOne.mockResolvedValue({ _id: 't1' });
|
|
213
|
+
|
|
214
|
+
await protocol.getData({ object: 'task', id: 't1' });
|
|
215
|
+
|
|
216
|
+
expect(mockEngine.findOne).toHaveBeenCalledWith(
|
|
217
|
+
'task',
|
|
218
|
+
{ filter: { _id: 't1' } },
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should return standard GetDataResponse shape', async () => {
|
|
223
|
+
mockEngine.findOne.mockResolvedValue({ _id: 'oi_1', name: 'Item 1' });
|
|
224
|
+
|
|
225
|
+
const result = await protocol.getData({ object: 'order_item', id: 'oi_1' });
|
|
226
|
+
|
|
227
|
+
expect(result).toEqual({
|
|
228
|
+
object: 'order_item',
|
|
229
|
+
id: 'oi_1',
|
|
230
|
+
record: { _id: 'oi_1', name: 'Item 1' },
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should throw when record not found', async () => {
|
|
235
|
+
mockEngine.findOne.mockResolvedValue(null);
|
|
236
|
+
|
|
237
|
+
await expect(
|
|
238
|
+
protocol.getData({ object: 'task', id: 'missing_id' })
|
|
239
|
+
).rejects.toThrow('not found');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|