@declaro/data 2.0.0-beta.126 → 2.0.0-beta.128
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/dist/browser/index.js +11 -11
- package/dist/browser/index.js.map +6 -6
- package/dist/node/index.cjs +217 -92
- package/dist/node/index.cjs.map +6 -6
- package/dist/node/index.js +209 -84
- package/dist/node/index.js.map +6 -6
- package/dist/ts/application/model-controller.d.ts +15 -5
- package/dist/ts/application/model-controller.d.ts.map +1 -1
- package/dist/ts/application/read-only-model-controller.d.ts +5 -1
- package/dist/ts/application/read-only-model-controller.d.ts.map +1 -1
- package/dist/ts/domain/services/model-service.d.ts +27 -0
- package/dist/ts/domain/services/model-service.d.ts.map +1 -1
- package/dist/ts/domain/services/read-only-model-service.d.ts +8 -0
- package/dist/ts/domain/services/read-only-model-service.d.ts.map +1 -1
- package/dist/ts/shared/utils/schema-inheritance.test.d.ts +2 -0
- package/dist/ts/shared/utils/schema-inheritance.test.d.ts.map +1 -0
- package/dist/ts/shared/utils/test/animal-schema.d.ts +57 -0
- package/dist/ts/shared/utils/test/animal-schema.d.ts.map +1 -0
- package/dist/ts/shared/utils/test/animal-trait-schema.d.ts +55 -0
- package/dist/ts/shared/utils/test/animal-trait-schema.d.ts.map +1 -0
- package/dist/ts/shared/utils/test/elephant-schema.d.ts +30 -0
- package/dist/ts/shared/utils/test/elephant-schema.d.ts.map +1 -0
- package/dist/ts/shared/utils/test/elephant-trait-schema.d.ts +26 -0
- package/dist/ts/shared/utils/test/elephant-trait-schema.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/application/model-controller.ts +110 -59
- package/src/application/read-only-model-controller.ts +43 -25
- package/src/domain/services/model-service.test.ts +460 -0
- package/src/domain/services/model-service.ts +165 -67
- package/src/domain/services/read-only-model-service.test.ts +230 -0
- package/src/domain/services/read-only-model-service.ts +65 -40
- package/src/shared/utils/schema-inheritance.test.ts +295 -0
- package/src/shared/utils/test/animal-schema.ts +46 -0
- package/src/shared/utils/test/animal-trait-schema.ts +45 -0
- package/src/shared/utils/test/elephant-schema.ts +58 -0
- package/src/shared/utils/test/elephant-trait-schema.ts +53 -0
- package/dist/ts/test/mock/repositories/mock-memory-repository.custom-lookup.test.d.ts +0 -1
- package/dist/ts/test/mock/repositories/mock-memory-repository.custom-lookup.test.d.ts.map +0 -1
- package/src/test/mock/repositories/mock-memory-repository.custom-lookup.test.ts +0 -0
|
@@ -934,4 +934,464 @@ describe('ModelService', () => {
|
|
|
934
934
|
})
|
|
935
935
|
})
|
|
936
936
|
})
|
|
937
|
+
|
|
938
|
+
describe('detailsToInput', () => {
|
|
939
|
+
it('should convert a detail object to an input by picking only input fields', async () => {
|
|
940
|
+
const detail = { id: 42, title: 'Test Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
941
|
+
const input = await service.detailsToInput(detail)
|
|
942
|
+
|
|
943
|
+
expect(input.title).toBe('Test Book')
|
|
944
|
+
expect(input.author).toBe('Author Name')
|
|
945
|
+
expect(input.publishedDate).toEqual(new Date('2023-06-15'))
|
|
946
|
+
})
|
|
947
|
+
|
|
948
|
+
it('should exclude fields not present in the input model', async () => {
|
|
949
|
+
const detailWithExtras = {
|
|
950
|
+
id: 42,
|
|
951
|
+
title: 'Test Book',
|
|
952
|
+
author: 'Author Name',
|
|
953
|
+
publishedDate: new Date('2023-06-15'),
|
|
954
|
+
extraField: 'should be excluded',
|
|
955
|
+
anotherExtra: 123,
|
|
956
|
+
}
|
|
957
|
+
const input = await service.detailsToInput(detailWithExtras)
|
|
958
|
+
|
|
959
|
+
expect(input.title).toBe('Test Book')
|
|
960
|
+
expect(input.author).toBe('Author Name')
|
|
961
|
+
expect((input as any).extraField).toBeUndefined()
|
|
962
|
+
expect((input as any).anotherExtra).toBeUndefined()
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
it('should coerce values through the input schema', async () => {
|
|
966
|
+
// publishedDate uses z.coerce.date(), so a string should be coerced to a Date
|
|
967
|
+
const detailWithStringDate = {
|
|
968
|
+
id: 42,
|
|
969
|
+
title: 'Test Book',
|
|
970
|
+
author: 'Author Name',
|
|
971
|
+
publishedDate: '2023-06-15T00:00:00.000Z',
|
|
972
|
+
}
|
|
973
|
+
const input = await service.detailsToInput(detailWithStringDate)
|
|
974
|
+
|
|
975
|
+
expect(input.publishedDate).toBeInstanceOf(Date)
|
|
976
|
+
expect(input.publishedDate).toEqual(new Date('2023-06-15T00:00:00.000Z'))
|
|
977
|
+
})
|
|
978
|
+
|
|
979
|
+
it('should include the primary key field when present in the source', async () => {
|
|
980
|
+
const detail = { id: 42, title: 'Test Book', author: 'Author Name', publishedDate: new Date() }
|
|
981
|
+
const input = await service.detailsToInput(detail)
|
|
982
|
+
|
|
983
|
+
// id is optional in the input model but should be included if present
|
|
984
|
+
expect(input.id).toBe(42)
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
it('should work with partial source data that satisfies required input fields', async () => {
|
|
988
|
+
const minimalDetail = {
|
|
989
|
+
title: 'Minimal Book',
|
|
990
|
+
author: 'Some Author',
|
|
991
|
+
publishedDate: new Date(),
|
|
992
|
+
}
|
|
993
|
+
const input = await service.detailsToInput(minimalDetail)
|
|
994
|
+
|
|
995
|
+
expect(input.title).toBe('Minimal Book')
|
|
996
|
+
expect(input.author).toBe('Some Author')
|
|
997
|
+
expect(input.id).toBeUndefined()
|
|
998
|
+
})
|
|
999
|
+
|
|
1000
|
+
it('should produce a valid input that can be used with create', async () => {
|
|
1001
|
+
const detail = { id: 42, title: 'Test Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
1002
|
+
const input = await service.detailsToInput(detail)
|
|
1003
|
+
|
|
1004
|
+
// Remove the primary key and create a new record
|
|
1005
|
+
delete (input as any).id
|
|
1006
|
+
const created = await service.create(input)
|
|
1007
|
+
|
|
1008
|
+
expect(created.title).toBe('Test Book')
|
|
1009
|
+
expect(created.author).toBe('Author Name')
|
|
1010
|
+
expect(created.id).toBeDefined()
|
|
1011
|
+
expect(created.id).not.toBe(42)
|
|
1012
|
+
})
|
|
1013
|
+
})
|
|
1014
|
+
|
|
1015
|
+
describe('duplicate', () => {
|
|
1016
|
+
it('should create a duplicate of an existing record with a new primary key', async () => {
|
|
1017
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
1018
|
+
await repository.create(original)
|
|
1019
|
+
|
|
1020
|
+
const duplicate = await service.duplicate({ id: 42 })
|
|
1021
|
+
|
|
1022
|
+
expect(duplicate.title).toBe('Original Book')
|
|
1023
|
+
expect(duplicate.author).toBe('Author Name')
|
|
1024
|
+
expect(duplicate.publishedDate).toEqual(new Date('2023-06-15'))
|
|
1025
|
+
expect(duplicate.id).toBeDefined()
|
|
1026
|
+
expect(duplicate.id).not.toBe(42)
|
|
1027
|
+
})
|
|
1028
|
+
|
|
1029
|
+
it('should apply overrides to the duplicated record', async () => {
|
|
1030
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
1031
|
+
await repository.create(original)
|
|
1032
|
+
|
|
1033
|
+
const duplicate = await service.duplicate({ id: 42 }, { title: 'Duplicated Book' })
|
|
1034
|
+
|
|
1035
|
+
expect(duplicate.title).toBe('Duplicated Book')
|
|
1036
|
+
expect(duplicate.author).toBe('Author Name')
|
|
1037
|
+
expect(duplicate.id).toBeDefined()
|
|
1038
|
+
expect(duplicate.id).not.toBe(42)
|
|
1039
|
+
})
|
|
1040
|
+
|
|
1041
|
+
it('should throw an error when trying to duplicate a non-existent record', async () => {
|
|
1042
|
+
await expect(service.duplicate({ id: 999 })).rejects.toThrow()
|
|
1043
|
+
})
|
|
1044
|
+
|
|
1045
|
+
it('should trigger create events for the duplicate', async () => {
|
|
1046
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date() }
|
|
1047
|
+
await repository.create(original)
|
|
1048
|
+
|
|
1049
|
+
beforeCreateSpy.mockClear()
|
|
1050
|
+
afterCreateSpy.mockClear()
|
|
1051
|
+
|
|
1052
|
+
await service.duplicate({ id: 42 })
|
|
1053
|
+
|
|
1054
|
+
expect(beforeCreateSpy).toHaveBeenCalledTimes(1)
|
|
1055
|
+
expect(afterCreateSpy).toHaveBeenCalledTimes(1)
|
|
1056
|
+
})
|
|
1057
|
+
|
|
1058
|
+
it('should not modify the original record', async () => {
|
|
1059
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
1060
|
+
await repository.create(original)
|
|
1061
|
+
|
|
1062
|
+
await service.duplicate({ id: 42 }, { title: 'Modified Title' })
|
|
1063
|
+
|
|
1064
|
+
const loaded = await repository.load({ id: 42 })
|
|
1065
|
+
expect(loaded?.title).toBe('Original Book')
|
|
1066
|
+
expect(loaded?.author).toBe('Author Name')
|
|
1067
|
+
})
|
|
1068
|
+
|
|
1069
|
+
it('should allow overriding multiple fields', async () => {
|
|
1070
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
1071
|
+
await repository.create(original)
|
|
1072
|
+
|
|
1073
|
+
const newDate = new Date('2024-01-01')
|
|
1074
|
+
const duplicate = await service.duplicate({ id: 42 }, {
|
|
1075
|
+
title: 'New Title',
|
|
1076
|
+
author: 'New Author',
|
|
1077
|
+
publishedDate: newDate,
|
|
1078
|
+
})
|
|
1079
|
+
|
|
1080
|
+
expect(duplicate.title).toBe('New Title')
|
|
1081
|
+
expect(duplicate.author).toBe('New Author')
|
|
1082
|
+
expect(duplicate.publishedDate).toEqual(newDate)
|
|
1083
|
+
expect(duplicate.id).not.toBe(42)
|
|
1084
|
+
})
|
|
1085
|
+
|
|
1086
|
+
it('should create independent records that can be modified separately', async () => {
|
|
1087
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date('2023-06-15') }
|
|
1088
|
+
await repository.create(original)
|
|
1089
|
+
|
|
1090
|
+
const duplicate = await service.duplicate({ id: 42 })
|
|
1091
|
+
|
|
1092
|
+
// Update the duplicate
|
|
1093
|
+
await service.update({ id: duplicate.id }, { title: 'Updated Duplicate', author: 'Updated Author', publishedDate: new Date() })
|
|
1094
|
+
|
|
1095
|
+
// Verify original is unchanged
|
|
1096
|
+
const loadedOriginal = await repository.load({ id: 42 })
|
|
1097
|
+
expect(loadedOriginal?.title).toBe('Original Book')
|
|
1098
|
+
|
|
1099
|
+
// Verify duplicate was updated
|
|
1100
|
+
const loadedDuplicate = await repository.load({ id: duplicate.id })
|
|
1101
|
+
expect(loadedDuplicate?.title).toBe('Updated Duplicate')
|
|
1102
|
+
})
|
|
1103
|
+
|
|
1104
|
+
it('should respect doNotDispatchEvents option', async () => {
|
|
1105
|
+
const original = { id: 42, title: 'Original Book', author: 'Author Name', publishedDate: new Date() }
|
|
1106
|
+
await repository.create(original)
|
|
1107
|
+
|
|
1108
|
+
beforeCreateSpy.mockClear()
|
|
1109
|
+
afterCreateSpy.mockClear()
|
|
1110
|
+
|
|
1111
|
+
await service.duplicate({ id: 42 }, undefined, { doNotDispatchEvents: true })
|
|
1112
|
+
|
|
1113
|
+
expect(beforeCreateSpy).not.toHaveBeenCalled()
|
|
1114
|
+
expect(afterCreateSpy).not.toHaveBeenCalled()
|
|
1115
|
+
})
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
describe('doNotDispatchEvents option', () => {
|
|
1119
|
+
const beforeLoadSpy = mock(() => {})
|
|
1120
|
+
const afterLoadSpy = mock(() => {})
|
|
1121
|
+
const beforeLoadManySpy = mock(() => {})
|
|
1122
|
+
const afterLoadManySpy = mock(() => {})
|
|
1123
|
+
|
|
1124
|
+
beforeEach(() => {
|
|
1125
|
+
repository = new MockMemoryRepository({ schema: mockSchema })
|
|
1126
|
+
emitter = new EventManager()
|
|
1127
|
+
|
|
1128
|
+
beforeLoadSpy.mockClear()
|
|
1129
|
+
afterLoadSpy.mockClear()
|
|
1130
|
+
beforeLoadManySpy.mockClear()
|
|
1131
|
+
afterLoadManySpy.mockClear()
|
|
1132
|
+
beforeCreateSpy.mockClear()
|
|
1133
|
+
afterCreateSpy.mockClear()
|
|
1134
|
+
beforeUpdateSpy.mockClear()
|
|
1135
|
+
afterUpdateSpy.mockClear()
|
|
1136
|
+
|
|
1137
|
+
emitter.on('books::book.beforeLoad', beforeLoadSpy)
|
|
1138
|
+
emitter.on('books::book.afterLoad', afterLoadSpy)
|
|
1139
|
+
emitter.on('books::book.beforeLoadMany', beforeLoadManySpy)
|
|
1140
|
+
emitter.on('books::book.afterLoadMany', afterLoadManySpy)
|
|
1141
|
+
emitter.on('books::book.beforeCreate', beforeCreateSpy)
|
|
1142
|
+
emitter.on('books::book.afterCreate', afterCreateSpy)
|
|
1143
|
+
emitter.on('books::book.beforeUpdate', beforeUpdateSpy)
|
|
1144
|
+
emitter.on('books::book.afterUpdate', afterUpdateSpy)
|
|
1145
|
+
|
|
1146
|
+
service = new ModelService({ repository, emitter, schema: mockSchema, namespace })
|
|
1147
|
+
})
|
|
1148
|
+
|
|
1149
|
+
describe('upsert', () => {
|
|
1150
|
+
it('should not dispatch any events when doNotDispatchEvents is true', async () => {
|
|
1151
|
+
const input = {
|
|
1152
|
+
id: 1,
|
|
1153
|
+
title: 'Existing Book',
|
|
1154
|
+
author: 'Author',
|
|
1155
|
+
publishedDate: new Date(),
|
|
1156
|
+
}
|
|
1157
|
+
await repository.create(input)
|
|
1158
|
+
|
|
1159
|
+
const updatedInput = {
|
|
1160
|
+
id: 1,
|
|
1161
|
+
title: 'Updated Book',
|
|
1162
|
+
author: 'Updated Author',
|
|
1163
|
+
publishedDate: new Date(),
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
await service.upsert(updatedInput, { doNotDispatchEvents: true })
|
|
1167
|
+
|
|
1168
|
+
// Load events should not be dispatched due to propagation
|
|
1169
|
+
expect(beforeLoadSpy).not.toHaveBeenCalled()
|
|
1170
|
+
expect(afterLoadSpy).not.toHaveBeenCalled()
|
|
1171
|
+
|
|
1172
|
+
// Update events should not be dispatched either
|
|
1173
|
+
expect(beforeUpdateSpy).not.toHaveBeenCalled()
|
|
1174
|
+
expect(afterUpdateSpy).not.toHaveBeenCalled()
|
|
1175
|
+
})
|
|
1176
|
+
|
|
1177
|
+
it('should not dispatch load events even when doNotDispatchEvents is false', async () => {
|
|
1178
|
+
const input = {
|
|
1179
|
+
id: 2,
|
|
1180
|
+
title: 'Existing Book',
|
|
1181
|
+
author: 'Author',
|
|
1182
|
+
publishedDate: new Date(),
|
|
1183
|
+
}
|
|
1184
|
+
await repository.create(input)
|
|
1185
|
+
|
|
1186
|
+
const updatedInput = {
|
|
1187
|
+
id: 2,
|
|
1188
|
+
title: 'Updated Book',
|
|
1189
|
+
author: 'Updated Author',
|
|
1190
|
+
publishedDate: new Date(),
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
await service.upsert(updatedInput, { doNotDispatchEvents: false })
|
|
1194
|
+
|
|
1195
|
+
// Load events should NOT be dispatched (forced internally)
|
|
1196
|
+
expect(beforeLoadSpy).not.toHaveBeenCalled()
|
|
1197
|
+
expect(afterLoadSpy).not.toHaveBeenCalled()
|
|
1198
|
+
|
|
1199
|
+
// Update events should be dispatched
|
|
1200
|
+
expect(beforeUpdateSpy).toHaveBeenCalledTimes(1)
|
|
1201
|
+
expect(afterUpdateSpy).toHaveBeenCalledTimes(1)
|
|
1202
|
+
})
|
|
1203
|
+
|
|
1204
|
+
it('should not dispatch load events even when doNotDispatchEvents is not specified', async () => {
|
|
1205
|
+
const input = {
|
|
1206
|
+
id: 3,
|
|
1207
|
+
title: 'Existing Book',
|
|
1208
|
+
author: 'Author',
|
|
1209
|
+
publishedDate: new Date(),
|
|
1210
|
+
}
|
|
1211
|
+
await repository.create(input)
|
|
1212
|
+
|
|
1213
|
+
const updatedInput = {
|
|
1214
|
+
id: 3,
|
|
1215
|
+
title: 'Updated Book',
|
|
1216
|
+
author: 'Updated Author',
|
|
1217
|
+
publishedDate: new Date(),
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
await service.upsert(updatedInput)
|
|
1221
|
+
|
|
1222
|
+
// Load events should NOT be dispatched (forced internally)
|
|
1223
|
+
expect(beforeLoadSpy).not.toHaveBeenCalled()
|
|
1224
|
+
expect(afterLoadSpy).not.toHaveBeenCalled()
|
|
1225
|
+
|
|
1226
|
+
// Update events should be dispatched
|
|
1227
|
+
expect(beforeUpdateSpy).toHaveBeenCalledTimes(1)
|
|
1228
|
+
expect(afterUpdateSpy).toHaveBeenCalledTimes(1)
|
|
1229
|
+
})
|
|
1230
|
+
})
|
|
1231
|
+
|
|
1232
|
+
describe('bulkUpsert', () => {
|
|
1233
|
+
it('should not dispatch any events when doNotDispatchEvents is true', async () => {
|
|
1234
|
+
const input1 = {
|
|
1235
|
+
id: 1,
|
|
1236
|
+
title: 'Existing Book 1',
|
|
1237
|
+
author: 'Author 1',
|
|
1238
|
+
publishedDate: new Date(),
|
|
1239
|
+
}
|
|
1240
|
+
const input2 = {
|
|
1241
|
+
id: 2,
|
|
1242
|
+
title: 'Existing Book 2',
|
|
1243
|
+
author: 'Author 2',
|
|
1244
|
+
publishedDate: new Date(),
|
|
1245
|
+
}
|
|
1246
|
+
await repository.create(input1)
|
|
1247
|
+
await repository.create(input2)
|
|
1248
|
+
|
|
1249
|
+
const updatedInputs = [
|
|
1250
|
+
{
|
|
1251
|
+
id: 1,
|
|
1252
|
+
title: 'Updated Book 1',
|
|
1253
|
+
author: 'Updated Author 1',
|
|
1254
|
+
publishedDate: new Date(),
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
id: 2,
|
|
1258
|
+
title: 'Updated Book 2',
|
|
1259
|
+
author: 'Updated Author 2',
|
|
1260
|
+
publishedDate: new Date(),
|
|
1261
|
+
},
|
|
1262
|
+
]
|
|
1263
|
+
|
|
1264
|
+
await service.bulkUpsert(updatedInputs, { doNotDispatchEvents: true })
|
|
1265
|
+
|
|
1266
|
+
// LoadMany events should not be dispatched due to propagation
|
|
1267
|
+
expect(beforeLoadManySpy).not.toHaveBeenCalled()
|
|
1268
|
+
expect(afterLoadManySpy).not.toHaveBeenCalled()
|
|
1269
|
+
|
|
1270
|
+
// Update events should not be dispatched either
|
|
1271
|
+
expect(beforeUpdateSpy).not.toHaveBeenCalled()
|
|
1272
|
+
expect(afterUpdateSpy).not.toHaveBeenCalled()
|
|
1273
|
+
})
|
|
1274
|
+
|
|
1275
|
+
it('should not dispatch loadMany events even when doNotDispatchEvents is false', async () => {
|
|
1276
|
+
const input1 = {
|
|
1277
|
+
id: 3,
|
|
1278
|
+
title: 'Existing Book 3',
|
|
1279
|
+
author: 'Author 3',
|
|
1280
|
+
publishedDate: new Date(),
|
|
1281
|
+
}
|
|
1282
|
+
const input2 = {
|
|
1283
|
+
id: 4,
|
|
1284
|
+
title: 'Existing Book 4',
|
|
1285
|
+
author: 'Author 4',
|
|
1286
|
+
publishedDate: new Date(),
|
|
1287
|
+
}
|
|
1288
|
+
await repository.create(input1)
|
|
1289
|
+
await repository.create(input2)
|
|
1290
|
+
|
|
1291
|
+
const updatedInputs = [
|
|
1292
|
+
{
|
|
1293
|
+
id: 3,
|
|
1294
|
+
title: 'Updated Book 3',
|
|
1295
|
+
author: 'Updated Author 3',
|
|
1296
|
+
publishedDate: new Date(),
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
id: 4,
|
|
1300
|
+
title: 'Updated Book 4',
|
|
1301
|
+
author: 'Updated Author 4',
|
|
1302
|
+
publishedDate: new Date(),
|
|
1303
|
+
},
|
|
1304
|
+
]
|
|
1305
|
+
|
|
1306
|
+
await service.bulkUpsert(updatedInputs, { doNotDispatchEvents: false })
|
|
1307
|
+
|
|
1308
|
+
// LoadMany events should NOT be dispatched (forced internally)
|
|
1309
|
+
expect(beforeLoadManySpy).not.toHaveBeenCalled()
|
|
1310
|
+
expect(afterLoadManySpy).not.toHaveBeenCalled()
|
|
1311
|
+
|
|
1312
|
+
// Update events should be dispatched (2 updates)
|
|
1313
|
+
expect(beforeUpdateSpy).toHaveBeenCalledTimes(2)
|
|
1314
|
+
expect(afterUpdateSpy).toHaveBeenCalledTimes(2)
|
|
1315
|
+
})
|
|
1316
|
+
|
|
1317
|
+
it('should not dispatch loadMany events even when doNotDispatchEvents is not specified', async () => {
|
|
1318
|
+
const input1 = {
|
|
1319
|
+
id: 5,
|
|
1320
|
+
title: 'Existing Book 5',
|
|
1321
|
+
author: 'Author 5',
|
|
1322
|
+
publishedDate: new Date(),
|
|
1323
|
+
}
|
|
1324
|
+
const input2 = {
|
|
1325
|
+
id: 6,
|
|
1326
|
+
title: 'Existing Book 6',
|
|
1327
|
+
author: 'Author 6',
|
|
1328
|
+
publishedDate: new Date(),
|
|
1329
|
+
}
|
|
1330
|
+
await repository.create(input1)
|
|
1331
|
+
await repository.create(input2)
|
|
1332
|
+
|
|
1333
|
+
const updatedInputs = [
|
|
1334
|
+
{
|
|
1335
|
+
id: 5,
|
|
1336
|
+
title: 'Updated Book 5',
|
|
1337
|
+
author: 'Updated Author 5',
|
|
1338
|
+
publishedDate: new Date(),
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
id: 6,
|
|
1342
|
+
title: 'Updated Book 6',
|
|
1343
|
+
author: 'Updated Author 6',
|
|
1344
|
+
publishedDate: new Date(),
|
|
1345
|
+
},
|
|
1346
|
+
]
|
|
1347
|
+
|
|
1348
|
+
await service.bulkUpsert(updatedInputs)
|
|
1349
|
+
|
|
1350
|
+
// LoadMany events should NOT be dispatched (forced internally)
|
|
1351
|
+
expect(beforeLoadManySpy).not.toHaveBeenCalled()
|
|
1352
|
+
expect(afterLoadManySpy).not.toHaveBeenCalled()
|
|
1353
|
+
|
|
1354
|
+
// Update events should be dispatched (2 updates)
|
|
1355
|
+
expect(beforeUpdateSpy).toHaveBeenCalledTimes(2)
|
|
1356
|
+
expect(afterUpdateSpy).toHaveBeenCalledTimes(2)
|
|
1357
|
+
})
|
|
1358
|
+
|
|
1359
|
+
it('should not dispatch any events with doNotDispatchEvents for mixed create and update operations', async () => {
|
|
1360
|
+
const existingInput = {
|
|
1361
|
+
id: 7,
|
|
1362
|
+
title: 'Existing Book',
|
|
1363
|
+
author: 'Author',
|
|
1364
|
+
publishedDate: new Date(),
|
|
1365
|
+
}
|
|
1366
|
+
await repository.create(existingInput)
|
|
1367
|
+
|
|
1368
|
+
const inputs = [
|
|
1369
|
+
{
|
|
1370
|
+
id: 7,
|
|
1371
|
+
title: 'Updated Book',
|
|
1372
|
+
author: 'Updated Author',
|
|
1373
|
+
publishedDate: new Date(),
|
|
1374
|
+
},
|
|
1375
|
+
{
|
|
1376
|
+
id: 8,
|
|
1377
|
+
title: 'New Book',
|
|
1378
|
+
author: 'New Author',
|
|
1379
|
+
publishedDate: new Date(),
|
|
1380
|
+
},
|
|
1381
|
+
]
|
|
1382
|
+
|
|
1383
|
+
await service.bulkUpsert(inputs, { doNotDispatchEvents: true })
|
|
1384
|
+
|
|
1385
|
+
// LoadMany events should not be dispatched
|
|
1386
|
+
expect(beforeLoadManySpy).not.toHaveBeenCalled()
|
|
1387
|
+
expect(afterLoadManySpy).not.toHaveBeenCalled()
|
|
1388
|
+
|
|
1389
|
+
// Neither create nor update events should be dispatched
|
|
1390
|
+
expect(beforeCreateSpy).not.toHaveBeenCalled()
|
|
1391
|
+
expect(afterCreateSpy).not.toHaveBeenCalled()
|
|
1392
|
+
expect(beforeUpdateSpy).not.toHaveBeenCalled()
|
|
1393
|
+
expect(afterUpdateSpy).not.toHaveBeenCalled()
|
|
1394
|
+
})
|
|
1395
|
+
})
|
|
1396
|
+
})
|
|
937
1397
|
})
|