@e22m4u/js-repository 0.1.8 → 0.1.10
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/README.md +22 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/Adapter.html +1 -1
- package/docs/classes/AdapterLoader.html +1 -1
- package/docs/classes/AdapterRegistry.html +1 -1
- package/docs/classes/BelongsToResolver.html +1 -1
- package/docs/classes/DatasourceDefinitionValidator.html +1 -1
- package/docs/classes/DefinitionRegistry.html +1 -1
- package/docs/classes/EmptyValuesDefiner.html +18 -0
- package/docs/classes/FieldsClauseTool.html +1 -1
- package/docs/classes/HasManyResolver.html +1 -1
- package/docs/classes/HasOneResolver.html +1 -1
- package/docs/classes/IncludeClauseTool.html +1 -1
- package/docs/classes/InvalidArgumentError.html +1 -1
- package/docs/classes/InvalidOperatorValueError.html +1 -1
- package/docs/classes/ModelDataSanitizer.html +1 -1
- package/docs/classes/ModelDataTransformer.html +1 -1
- package/docs/classes/ModelDataValidator.html +1 -1
- package/docs/classes/ModelDefinitionUtils.html +1 -1
- package/docs/classes/ModelDefinitionValidator.html +1 -1
- package/docs/classes/NotImplementedError.html +1 -1
- package/docs/classes/OperatorClauseTool.html +1 -1
- package/docs/classes/OrderClauseTool.html +1 -1
- package/docs/classes/PrimaryKeysDefinitionValidator.html +1 -1
- package/docs/classes/PropertiesDefinitionValidator.html +1 -1
- package/docs/classes/PropertyTransformerRegistry.html +1 -1
- package/docs/classes/PropertyUniquenessValidator.html +16 -0
- package/docs/classes/PropertyValidatorRegistry.html +1 -1
- package/docs/classes/ReferencesManyResolver.html +1 -1
- package/docs/classes/RelationsDefinitionValidator.html +1 -1
- package/docs/classes/Repository.html +1 -1
- package/docs/classes/RepositoryRegistry.html +1 -1
- package/docs/classes/Schema.html +1 -1
- package/docs/classes/SliceClauseTool.html +1 -1
- package/docs/classes/WhereClauseTool.html +1 -1
- package/docs/enums/DataType.html +1 -1
- package/docs/enums/DecoratorTargetType.html +1 -1
- package/docs/enums/RelationType.html +1 -1
- package/docs/functions/capitalize.html +1 -1
- package/docs/functions/cloneDeep.html +1 -1
- package/docs/functions/excludeObjectKeys.html +1 -1
- package/docs/functions/getCtorName.html +1 -1
- package/docs/functions/getDecoratorTargetType.html +1 -1
- package/docs/functions/getValueByPath.html +1 -1
- package/docs/functions/isCtor.html +1 -1
- package/docs/functions/isDeepEqual.html +2 -0
- package/docs/functions/isPureObject.html +1 -1
- package/docs/functions/selectObjectKeys.html +1 -1
- package/docs/functions/singularize.html +1 -1
- package/docs/functions/stringToRegexp.html +1 -1
- package/docs/index.html +18 -3
- package/docs/interfaces/AndClause.html +1 -1
- package/docs/interfaces/Constructor.html +1 -1
- package/docs/interfaces/OrClause.html +1 -1
- package/docs/modules.html +6 -1
- package/docs/types/AnyObject.html +1 -1
- package/docs/types/BelongsToDefinition.html +1 -1
- package/docs/types/CountMethod.html +2 -0
- package/docs/types/DEFAULT_PRIMARY_KEY_PROPERTY_NAME.html +1 -1
- package/docs/types/DatasourceDefinition.html +1 -1
- package/docs/types/FieldsClause.html +1 -1
- package/docs/types/FilterClause.html +1 -1
- package/docs/types/Flatten.html +1 -1
- package/docs/types/FullPropertyDefinition.html +2 -2
- package/docs/types/HasManyDefinition.html +1 -1
- package/docs/types/HasOneDefinition.html +1 -1
- package/docs/types/Identity.html +1 -1
- package/docs/types/IncludeClause.html +1 -1
- package/docs/types/ItemFilterClause.html +1 -1
- package/docs/types/ModelData.html +1 -1
- package/docs/types/ModelDefinition.html +1 -1
- package/docs/types/ModelId.html +1 -1
- package/docs/types/NestedIncludeClause.html +1 -1
- package/docs/types/NormalizedFieldsClause.html +1 -1
- package/docs/types/NormalizedIncludeClause.html +1 -1
- package/docs/types/OperatorClause.html +1 -1
- package/docs/types/OptionalUnlessRequiredId.html +1 -1
- package/docs/types/OrderClause.html +1 -1
- package/docs/types/PartialBy.html +1 -1
- package/docs/types/PartialWithoutId.html +1 -1
- package/docs/types/PolyBelongsToDefinition.html +1 -1
- package/docs/types/PolyHasManyDefinitionWithTargetKeys.html +1 -1
- package/docs/types/PolyHasManyDefinitionWithTargetRelationName.html +1 -1
- package/docs/types/PolyHasOneDefinitionWithTargetKeys.html +1 -1
- package/docs/types/PolyHasOneDefinitionWithTargetRelationName.html +1 -1
- package/docs/types/PropertiesClause.html +1 -1
- package/docs/types/PropertyDefinition.html +1 -1
- package/docs/types/PropertyDefinitionMap.html +1 -1
- package/docs/types/PropertyTransformOptions.html +1 -1
- package/docs/types/PropertyTransformer.html +1 -1
- package/docs/types/PropertyTransformerContext.html +1 -1
- package/docs/types/PropertyUniqueness.html +2 -0
- package/docs/types/PropertyValidateOptions.html +1 -1
- package/docs/types/PropertyValidator.html +1 -1
- package/docs/types/PropertyValidatorContext.html +1 -1
- package/docs/types/ReferencesManyDefinition.html +1 -1
- package/docs/types/RelationDefinition.html +1 -1
- package/docs/types/RelationDefinitionMap.html +1 -1
- package/docs/types/WhereClause.html +1 -1
- package/docs/types/WithoutId.html +1 -1
- package/package.json +4 -4
- package/src/adapter/adapter.js +2 -0
- package/src/adapter/adapter.spec.js +8 -2
- package/src/adapter/decorator/index.d.ts +1 -0
- package/src/adapter/decorator/index.js +1 -0
- package/src/adapter/decorator/property-uniqueness-decorator.d.ts +14 -0
- package/src/adapter/decorator/property-uniqueness-decorator.js +76 -0
- package/src/adapter/decorator/property-uniqueness-decorator.spec.js +135 -0
- package/src/definition/definition-registry.spec.js +1 -0
- package/src/definition/model/properties/empty-values-definer.d.ts +23 -0
- package/src/definition/model/properties/empty-values-definer.js +66 -0
- package/src/definition/model/properties/empty-values-definer.spec.js +96 -0
- package/src/definition/model/properties/index.d.ts +3 -0
- package/src/definition/model/properties/index.js +3 -0
- package/src/definition/model/properties/properties-definition-validator.js +21 -0
- package/src/definition/model/properties/properties-definition-validator.spec.js +50 -5
- package/src/definition/model/properties/property-definition.d.ts +3 -0
- package/src/definition/model/properties/property-uniqueness-validator.d.ts +31 -0
- package/src/definition/model/properties/property-uniqueness-validator.js +141 -0
- package/src/definition/model/properties/property-uniqueness-validator.spec.js +987 -0
- package/src/definition/model/properties/property-uniqueness.d.ts +8 -0
- package/src/definition/model/properties/property-uniqueness.js +8 -0
- package/src/utils/index.d.ts +1 -0
- package/src/utils/index.js +1 -0
- package/src/utils/is-deep-equal.d.ts +10 -0
- package/src/utils/is-deep-equal.js +71 -0
- package/src/utils/is-deep-equal.spec.js +238 -0
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {DataType} from './data-type.js';
|
|
3
|
+
import {format} from '@e22m4u/js-format';
|
|
4
|
+
import {Schema} from '../../../schema.js';
|
|
5
|
+
import {PropertyUniqueness} from './property-uniqueness.js';
|
|
6
|
+
import {EmptyValuesDefiner} from './empty-values-definer.js';
|
|
7
|
+
import {PropertyUniquenessValidator} from './property-uniqueness-validator.js';
|
|
8
|
+
import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '../model-definition-utils.js';
|
|
9
|
+
|
|
10
|
+
describe('PropertyUniquenessValidator', function () {
|
|
11
|
+
describe('validate', function () {
|
|
12
|
+
it('requires the parameter "countMethod" to be a Function', async function () {
|
|
13
|
+
const schema = new Schema();
|
|
14
|
+
schema.defineModel({
|
|
15
|
+
name: 'model',
|
|
16
|
+
properties: {
|
|
17
|
+
foo: {
|
|
18
|
+
type: DataType.ANY,
|
|
19
|
+
unique: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
24
|
+
const throwable = v => S.validate(v, 'create', 'model', {});
|
|
25
|
+
const error = v =>
|
|
26
|
+
format(
|
|
27
|
+
'The parameter "countMethod" of the PropertyUniquenessValidator ' +
|
|
28
|
+
'must be a Function, but %s given.',
|
|
29
|
+
v,
|
|
30
|
+
);
|
|
31
|
+
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
|
|
32
|
+
await expect(throwable('')).to.be.rejectedWith(error('""'));
|
|
33
|
+
await expect(throwable(10)).to.be.rejectedWith(error('10'));
|
|
34
|
+
await expect(throwable(0)).to.be.rejectedWith(error('0'));
|
|
35
|
+
await expect(throwable(true)).to.be.rejectedWith(error('true'));
|
|
36
|
+
await expect(throwable(false)).to.be.rejectedWith(error('false'));
|
|
37
|
+
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
|
|
38
|
+
await expect(throwable(null)).to.be.rejectedWith(error('null'));
|
|
39
|
+
await expect(throwable(new Date())).to.be.rejectedWith(error('Date'));
|
|
40
|
+
await expect(throwable([1, 2, 3])).to.be.rejectedWith(error('Array'));
|
|
41
|
+
await expect(throwable([])).to.be.rejectedWith(error('Array'));
|
|
42
|
+
await expect(throwable({foo: 'bar'})).to.be.rejectedWith(error('Object'));
|
|
43
|
+
await expect(throwable({})).to.be.rejectedWith(error('Object'));
|
|
44
|
+
await throwable(() => 0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('requires the parameter "methodName" to be a non-empty String', async function () {
|
|
48
|
+
const schema = new Schema();
|
|
49
|
+
schema.defineModel({
|
|
50
|
+
name: 'model',
|
|
51
|
+
properties: {
|
|
52
|
+
foo: {
|
|
53
|
+
type: DataType.ANY,
|
|
54
|
+
unique: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
59
|
+
const throwable = v => S.validate(() => 0, v, 'model', {});
|
|
60
|
+
const error = v =>
|
|
61
|
+
format(
|
|
62
|
+
'The parameter "methodName" of the PropertyUniquenessValidator ' +
|
|
63
|
+
'must be a non-empty String, but %s given.',
|
|
64
|
+
v,
|
|
65
|
+
);
|
|
66
|
+
await expect(throwable('')).to.be.rejectedWith(error('""'));
|
|
67
|
+
await expect(throwable(10)).to.be.rejectedWith(error('10'));
|
|
68
|
+
await expect(throwable(0)).to.be.rejectedWith(error('0'));
|
|
69
|
+
await expect(throwable(true)).to.be.rejectedWith(error('true'));
|
|
70
|
+
await expect(throwable(false)).to.be.rejectedWith(error('false'));
|
|
71
|
+
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
|
|
72
|
+
await expect(throwable(null)).to.be.rejectedWith(error('null'));
|
|
73
|
+
await expect(throwable(new Date())).to.be.rejectedWith(error('Date'));
|
|
74
|
+
await expect(throwable([1, 2, 3])).to.be.rejectedWith(error('Array'));
|
|
75
|
+
await expect(throwable([])).to.be.rejectedWith(error('Array'));
|
|
76
|
+
await expect(throwable({foo: 'bar'})).to.be.rejectedWith(error('Object'));
|
|
77
|
+
await expect(throwable({})).to.be.rejectedWith(error('Object'));
|
|
78
|
+
await expect(throwable(() => 0)).to.be.rejectedWith(error('Function'));
|
|
79
|
+
await throwable('create');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('requires the parameter "modelName" to be a non-empty String', async function () {
|
|
83
|
+
const schema = new Schema();
|
|
84
|
+
schema.defineModel({
|
|
85
|
+
name: 'model',
|
|
86
|
+
properties: {
|
|
87
|
+
foo: {
|
|
88
|
+
type: DataType.ANY,
|
|
89
|
+
unique: true,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
94
|
+
const throwable = v => S.validate(() => 0, 'create', v, {});
|
|
95
|
+
const error = v =>
|
|
96
|
+
format(
|
|
97
|
+
'The parameter "modelName" of the PropertyUniquenessValidator ' +
|
|
98
|
+
'must be a non-empty String, but %s given.',
|
|
99
|
+
v,
|
|
100
|
+
);
|
|
101
|
+
await expect(throwable('')).to.be.rejectedWith(error('""'));
|
|
102
|
+
await expect(throwable(10)).to.be.rejectedWith(error('10'));
|
|
103
|
+
await expect(throwable(0)).to.be.rejectedWith(error('0'));
|
|
104
|
+
await expect(throwable(true)).to.be.rejectedWith(error('true'));
|
|
105
|
+
await expect(throwable(false)).to.be.rejectedWith(error('false'));
|
|
106
|
+
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
|
|
107
|
+
await expect(throwable(null)).to.be.rejectedWith(error('null'));
|
|
108
|
+
await expect(throwable(new Date())).to.be.rejectedWith(error('Date'));
|
|
109
|
+
await expect(throwable([1, 2, 3])).to.be.rejectedWith(error('Array'));
|
|
110
|
+
await expect(throwable([])).to.be.rejectedWith(error('Array'));
|
|
111
|
+
await expect(throwable({foo: 'bar'})).to.be.rejectedWith(error('Object'));
|
|
112
|
+
await expect(throwable({})).to.be.rejectedWith(error('Object'));
|
|
113
|
+
await expect(throwable(() => 0)).to.be.rejectedWith(error('Function'));
|
|
114
|
+
await throwable('model');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('requires the parameter "modelData" to be a pure Object', async function () {
|
|
118
|
+
const schema = new Schema();
|
|
119
|
+
schema.defineModel({
|
|
120
|
+
name: 'model',
|
|
121
|
+
properties: {
|
|
122
|
+
foo: {
|
|
123
|
+
type: DataType.ANY,
|
|
124
|
+
unique: true,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
129
|
+
const throwable = v => S.validate(() => 0, 'create', 'model', v);
|
|
130
|
+
const error = v =>
|
|
131
|
+
format(
|
|
132
|
+
'The data of the model "model" should be an Object, but %s given.',
|
|
133
|
+
v,
|
|
134
|
+
);
|
|
135
|
+
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
|
|
136
|
+
await expect(throwable('')).to.be.rejectedWith(error('""'));
|
|
137
|
+
await expect(throwable(10)).to.be.rejectedWith(error('10'));
|
|
138
|
+
await expect(throwable(0)).to.be.rejectedWith(error('0'));
|
|
139
|
+
await expect(throwable(true)).to.be.rejectedWith(error('true'));
|
|
140
|
+
await expect(throwable(false)).to.be.rejectedWith(error('false'));
|
|
141
|
+
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
|
|
142
|
+
await expect(throwable(null)).to.be.rejectedWith(error('null'));
|
|
143
|
+
await expect(throwable([1, 2, 3])).to.be.rejectedWith(error('Array'));
|
|
144
|
+
await expect(throwable([])).to.be.rejectedWith(error('Array'));
|
|
145
|
+
await expect(throwable(new Date())).to.be.rejectedWith(error('Date'));
|
|
146
|
+
await expect(throwable(() => 0)).to.be.rejectedWith(error('Function'));
|
|
147
|
+
await throwable({foo: 'bar'});
|
|
148
|
+
await throwable({});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('skips checking if the option "unique" is false or not defined', async function () {
|
|
152
|
+
const schema = new Schema();
|
|
153
|
+
schema.defineModel({
|
|
154
|
+
name: 'model',
|
|
155
|
+
properties: {
|
|
156
|
+
foo: DataType.ANY,
|
|
157
|
+
bar: {
|
|
158
|
+
type: DataType.ANY,
|
|
159
|
+
},
|
|
160
|
+
baz: {
|
|
161
|
+
type: DataType.ANY,
|
|
162
|
+
unique: false,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
167
|
+
const promise = S.validate(() => 1, 'create', 'model', {});
|
|
168
|
+
await expect(promise).not.to.be.rejected;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('throws an error for unsupported method', async function () {
|
|
172
|
+
const schema = new Schema();
|
|
173
|
+
schema.defineModel({
|
|
174
|
+
name: 'model',
|
|
175
|
+
properties: {
|
|
176
|
+
foo: {
|
|
177
|
+
type: DataType.ANY,
|
|
178
|
+
unique: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
183
|
+
const promise = S.validate(() => 1, 'unsupported', 'model', {});
|
|
184
|
+
await expect(promise).to.be.rejectedWith(
|
|
185
|
+
'The PropertyUniquenessValidator does not ' +
|
|
186
|
+
'support the adapter method "unsupported".',
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('skips uniqueness checking for an empty value for "sparse" mode', async function () {
|
|
191
|
+
const schema = new Schema();
|
|
192
|
+
schema.defineModel({
|
|
193
|
+
name: 'model',
|
|
194
|
+
properties: {
|
|
195
|
+
foo: {
|
|
196
|
+
type: DataType.STRING,
|
|
197
|
+
unique: true,
|
|
198
|
+
},
|
|
199
|
+
bar: {
|
|
200
|
+
type: DataType.STRING,
|
|
201
|
+
unique: PropertyUniqueness.SPARSE,
|
|
202
|
+
},
|
|
203
|
+
baz: {
|
|
204
|
+
type: DataType.STRING,
|
|
205
|
+
unique: true,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
210
|
+
let invoked = 0;
|
|
211
|
+
schema
|
|
212
|
+
.getService(EmptyValuesDefiner)
|
|
213
|
+
.setEmptyValuesOf(DataType.STRING, ['val2']);
|
|
214
|
+
const modelData = {
|
|
215
|
+
foo: 'val1',
|
|
216
|
+
bar: 'val2',
|
|
217
|
+
baz: 'val3',
|
|
218
|
+
};
|
|
219
|
+
const countMethod = where => {
|
|
220
|
+
invoked++;
|
|
221
|
+
if (invoked === 1) {
|
|
222
|
+
expect(where).to.be.eql({foo: 'val1'});
|
|
223
|
+
} else if (invoked === 2) {
|
|
224
|
+
expect(where).to.be.eql({baz: 'val3'});
|
|
225
|
+
}
|
|
226
|
+
return 0;
|
|
227
|
+
};
|
|
228
|
+
await S.validate(countMethod, 'create', 'model', modelData);
|
|
229
|
+
expect(invoked).to.be.eql(2);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('create', function () {
|
|
233
|
+
it('throws an error if the "countMethod" returns a positive number', async function () {
|
|
234
|
+
const schema = new Schema();
|
|
235
|
+
schema.defineModel({
|
|
236
|
+
name: 'model',
|
|
237
|
+
properties: {
|
|
238
|
+
foo: {
|
|
239
|
+
type: DataType.ANY,
|
|
240
|
+
unique: true,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
245
|
+
const promise = S.validate(() => 1, 'create', 'model', {foo: 'bar'});
|
|
246
|
+
await expect(promise).to.be.rejectedWith(
|
|
247
|
+
'An existing document of the model "model" already has ' +
|
|
248
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('passes validation if the "countMethod" returns zero', async function () {
|
|
253
|
+
const schema = new Schema();
|
|
254
|
+
schema.defineModel({
|
|
255
|
+
name: 'model',
|
|
256
|
+
properties: {
|
|
257
|
+
foo: {
|
|
258
|
+
type: DataType.ANY,
|
|
259
|
+
unique: true,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
264
|
+
await S.validate(() => 0, 'create', 'model', {foo: 'bar'});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('invokes the "countMethod" for each unique property of the model', async function () {
|
|
268
|
+
const schema = new Schema();
|
|
269
|
+
schema.defineModel({
|
|
270
|
+
name: 'model',
|
|
271
|
+
properties: {
|
|
272
|
+
foo: {
|
|
273
|
+
type: DataType.ANY,
|
|
274
|
+
unique: true,
|
|
275
|
+
},
|
|
276
|
+
bar: {
|
|
277
|
+
type: DataType.ANY,
|
|
278
|
+
unique: false,
|
|
279
|
+
},
|
|
280
|
+
baz: {
|
|
281
|
+
type: DataType.ANY,
|
|
282
|
+
unique: true,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
287
|
+
let invoked = 0;
|
|
288
|
+
const modelData = {foo: 'val1', bar: 'val2'};
|
|
289
|
+
const countMethod = where => {
|
|
290
|
+
if (invoked === 0) {
|
|
291
|
+
expect(where).to.be.eql({foo: 'val1'});
|
|
292
|
+
} else if (invoked === 1) {
|
|
293
|
+
expect(where).to.be.eql({baz: undefined});
|
|
294
|
+
}
|
|
295
|
+
invoked++;
|
|
296
|
+
return 0;
|
|
297
|
+
};
|
|
298
|
+
await S.validate(countMethod, 'create', 'model', modelData);
|
|
299
|
+
expect(invoked).to.be.eq(2);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe('replaceById', function () {
|
|
304
|
+
it('throws an error if the "countMethod" returns a positive number', async function () {
|
|
305
|
+
const schema = new Schema();
|
|
306
|
+
schema.defineModel({
|
|
307
|
+
name: 'model',
|
|
308
|
+
properties: {
|
|
309
|
+
foo: {
|
|
310
|
+
type: DataType.ANY,
|
|
311
|
+
unique: true,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
316
|
+
const promise = S.validate(
|
|
317
|
+
() => 1,
|
|
318
|
+
'replaceById',
|
|
319
|
+
'model',
|
|
320
|
+
{foo: 'bar'},
|
|
321
|
+
1,
|
|
322
|
+
);
|
|
323
|
+
await expect(promise).to.be.rejectedWith(
|
|
324
|
+
'An existing document of the model "model" already has ' +
|
|
325
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('passes validation if the "countMethod" returns zero', async function () {
|
|
330
|
+
const schema = new Schema();
|
|
331
|
+
schema.defineModel({
|
|
332
|
+
name: 'model',
|
|
333
|
+
properties: {
|
|
334
|
+
foo: {
|
|
335
|
+
type: DataType.ANY,
|
|
336
|
+
unique: true,
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
341
|
+
await S.validate(() => 0, 'replaceById', 'model', {foo: 'bar'}, 1);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('invokes the "countMethod" for each unique property of the model', async function () {
|
|
345
|
+
const schema = new Schema();
|
|
346
|
+
schema.defineModel({
|
|
347
|
+
name: 'model',
|
|
348
|
+
properties: {
|
|
349
|
+
foo: {
|
|
350
|
+
type: DataType.ANY,
|
|
351
|
+
unique: true,
|
|
352
|
+
},
|
|
353
|
+
bar: {
|
|
354
|
+
type: DataType.ANY,
|
|
355
|
+
unique: false,
|
|
356
|
+
},
|
|
357
|
+
baz: {
|
|
358
|
+
type: DataType.ANY,
|
|
359
|
+
unique: true,
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
364
|
+
let invoked = 0;
|
|
365
|
+
const idValue = 1;
|
|
366
|
+
const modelData = {foo: 'val1', bar: 'val2'};
|
|
367
|
+
const countMethod = where => {
|
|
368
|
+
if (invoked === 0) {
|
|
369
|
+
expect(where).to.be.eql({
|
|
370
|
+
[DEF_PK]: {neq: idValue},
|
|
371
|
+
foo: 'val1',
|
|
372
|
+
});
|
|
373
|
+
} else if (invoked === 1) {
|
|
374
|
+
expect(where).to.be.eql({
|
|
375
|
+
[DEF_PK]: {neq: idValue},
|
|
376
|
+
baz: undefined,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
invoked++;
|
|
380
|
+
return 0;
|
|
381
|
+
};
|
|
382
|
+
await S.validate(
|
|
383
|
+
countMethod,
|
|
384
|
+
'replaceById',
|
|
385
|
+
'model',
|
|
386
|
+
modelData,
|
|
387
|
+
idValue,
|
|
388
|
+
);
|
|
389
|
+
expect(invoked).to.be.eq(2);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('can use a custom primary key', async function () {
|
|
393
|
+
const schema = new Schema();
|
|
394
|
+
schema.defineModel({
|
|
395
|
+
name: 'model',
|
|
396
|
+
properties: {
|
|
397
|
+
myId: {
|
|
398
|
+
type: DataType.NUMBER,
|
|
399
|
+
primaryKey: true,
|
|
400
|
+
},
|
|
401
|
+
foo: {
|
|
402
|
+
type: DataType.ANY,
|
|
403
|
+
unique: true,
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
408
|
+
let invoked = 0;
|
|
409
|
+
const idValue = 1;
|
|
410
|
+
const modelData = {foo: 'bar'};
|
|
411
|
+
const countMethod = where => {
|
|
412
|
+
if (invoked === 0) {
|
|
413
|
+
expect(where).to.be.eql({
|
|
414
|
+
myId: {neq: idValue},
|
|
415
|
+
foo: 'bar',
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
invoked++;
|
|
419
|
+
return 0;
|
|
420
|
+
};
|
|
421
|
+
await S.validate(
|
|
422
|
+
countMethod,
|
|
423
|
+
'replaceById',
|
|
424
|
+
'model',
|
|
425
|
+
modelData,
|
|
426
|
+
idValue,
|
|
427
|
+
);
|
|
428
|
+
expect(invoked).to.be.eq(1);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
describe('replaceOrCreate', function () {
|
|
433
|
+
it('throws an error if the "countMethod" returns a positive number', async function () {
|
|
434
|
+
const schema = new Schema();
|
|
435
|
+
schema.defineModel({
|
|
436
|
+
name: 'model',
|
|
437
|
+
properties: {
|
|
438
|
+
foo: {
|
|
439
|
+
type: DataType.ANY,
|
|
440
|
+
unique: true,
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
445
|
+
const promise = S.validate(() => 1, 'replaceOrCreate', 'model', {
|
|
446
|
+
foo: 'bar',
|
|
447
|
+
});
|
|
448
|
+
await expect(promise).to.be.rejectedWith(
|
|
449
|
+
'An existing document of the model "model" already has ' +
|
|
450
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('passes validation if the "countMethod" returns zero', async function () {
|
|
455
|
+
const schema = new Schema();
|
|
456
|
+
schema.defineModel({
|
|
457
|
+
name: 'model',
|
|
458
|
+
properties: {
|
|
459
|
+
foo: {
|
|
460
|
+
type: DataType.ANY,
|
|
461
|
+
unique: true,
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
466
|
+
await S.validate(() => 0, 'replaceOrCreate', 'model', {foo: 'bar'});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('invokes the "countMethod" for each unique property of the model', async function () {
|
|
470
|
+
const schema = new Schema();
|
|
471
|
+
schema.defineModel({
|
|
472
|
+
name: 'model',
|
|
473
|
+
properties: {
|
|
474
|
+
foo: {
|
|
475
|
+
type: DataType.ANY,
|
|
476
|
+
unique: true,
|
|
477
|
+
},
|
|
478
|
+
bar: {
|
|
479
|
+
type: DataType.ANY,
|
|
480
|
+
unique: false,
|
|
481
|
+
},
|
|
482
|
+
baz: {
|
|
483
|
+
type: DataType.ANY,
|
|
484
|
+
unique: true,
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
489
|
+
let invoked = 0;
|
|
490
|
+
const modelData = {foo: 'val1', bar: 'val2'};
|
|
491
|
+
const countMethod = where => {
|
|
492
|
+
if (invoked === 0) {
|
|
493
|
+
expect(where).to.be.eql({foo: 'val1'});
|
|
494
|
+
} else if (invoked === 1) {
|
|
495
|
+
expect(where).to.be.eql({baz: undefined});
|
|
496
|
+
}
|
|
497
|
+
invoked++;
|
|
498
|
+
return 0;
|
|
499
|
+
};
|
|
500
|
+
await S.validate(countMethod, 'replaceOrCreate', 'model', modelData);
|
|
501
|
+
expect(invoked).to.be.eq(2);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
describe('in case that the given model has a document identifier', function () {
|
|
505
|
+
describe('a document of the given identifier does not exist', function () {
|
|
506
|
+
it('uses the default primary key to check existence of the given identifier', async function () {
|
|
507
|
+
const schema = new Schema();
|
|
508
|
+
schema.defineModel({
|
|
509
|
+
name: 'model',
|
|
510
|
+
properties: {
|
|
511
|
+
foo: {
|
|
512
|
+
type: DataType.ANY,
|
|
513
|
+
unique: true,
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
518
|
+
let invoked = 0;
|
|
519
|
+
const idValue = 1;
|
|
520
|
+
const modelData = {[DEF_PK]: idValue, foo: 'bar'};
|
|
521
|
+
const countMethod = where => {
|
|
522
|
+
invoked++;
|
|
523
|
+
if (invoked === 1) {
|
|
524
|
+
expect(where).to.be.eql({[DEF_PK]: idValue});
|
|
525
|
+
return 0;
|
|
526
|
+
} else if (invoked === 2) {
|
|
527
|
+
expect(where).to.be.eql({foo: 'bar'});
|
|
528
|
+
return 0;
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
await S.validate(
|
|
532
|
+
countMethod,
|
|
533
|
+
'replaceOrCreate',
|
|
534
|
+
'model',
|
|
535
|
+
modelData,
|
|
536
|
+
);
|
|
537
|
+
expect(invoked).to.be.eq(2);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('uses a custom primary key to check existence of the given identifier', async function () {
|
|
541
|
+
const schema = new Schema();
|
|
542
|
+
schema.defineModel({
|
|
543
|
+
name: 'model',
|
|
544
|
+
properties: {
|
|
545
|
+
myId: {
|
|
546
|
+
type: DataType.NUMBER,
|
|
547
|
+
primaryKey: true,
|
|
548
|
+
},
|
|
549
|
+
foo: {
|
|
550
|
+
type: DataType.ANY,
|
|
551
|
+
unique: true,
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
556
|
+
let invoked = 0;
|
|
557
|
+
const idValue = 1;
|
|
558
|
+
const modelData = {myId: idValue, foo: 'bar'};
|
|
559
|
+
const countMethod = where => {
|
|
560
|
+
invoked++;
|
|
561
|
+
if (invoked === 1) {
|
|
562
|
+
expect(where).to.be.eql({myId: idValue});
|
|
563
|
+
return 0;
|
|
564
|
+
} else if (invoked === 2) {
|
|
565
|
+
expect(where).to.be.eql({foo: 'bar'});
|
|
566
|
+
return 0;
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
await S.validate(
|
|
570
|
+
countMethod,
|
|
571
|
+
'replaceOrCreate',
|
|
572
|
+
'model',
|
|
573
|
+
modelData,
|
|
574
|
+
idValue,
|
|
575
|
+
);
|
|
576
|
+
expect(invoked).to.be.eq(2);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('checks the given identifier only once', async function () {
|
|
580
|
+
const schema = new Schema();
|
|
581
|
+
schema.defineModel({
|
|
582
|
+
name: 'model',
|
|
583
|
+
properties: {
|
|
584
|
+
foo: {
|
|
585
|
+
type: DataType.ANY,
|
|
586
|
+
unique: true,
|
|
587
|
+
},
|
|
588
|
+
bar: {
|
|
589
|
+
type: DataType.ANY,
|
|
590
|
+
unique: true,
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
});
|
|
594
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
595
|
+
let invoked = 0;
|
|
596
|
+
const idValue = 1;
|
|
597
|
+
const modelData = {[DEF_PK]: idValue, foo: 'val1', bar: 'val2'};
|
|
598
|
+
const countMethod = where => {
|
|
599
|
+
invoked++;
|
|
600
|
+
if (invoked === 1) {
|
|
601
|
+
expect(where).to.be.eql({[DEF_PK]: idValue});
|
|
602
|
+
return 0;
|
|
603
|
+
} else if (invoked === 2) {
|
|
604
|
+
expect(where).to.be.eql({foo: 'val1'});
|
|
605
|
+
return 0;
|
|
606
|
+
} else if (invoked === 3) {
|
|
607
|
+
expect(where).to.be.eql({bar: 'val2'});
|
|
608
|
+
return 0;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
await S.validate(
|
|
612
|
+
countMethod,
|
|
613
|
+
'replaceOrCreate',
|
|
614
|
+
'model',
|
|
615
|
+
modelData,
|
|
616
|
+
);
|
|
617
|
+
expect(invoked).to.be.eq(3);
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
describe('a document of the given identifier already exist', function () {
|
|
622
|
+
it('uses the default primary key to check existence of the given identifier', async function () {
|
|
623
|
+
const schema = new Schema();
|
|
624
|
+
schema.defineModel({
|
|
625
|
+
name: 'model',
|
|
626
|
+
properties: {
|
|
627
|
+
foo: {
|
|
628
|
+
type: DataType.ANY,
|
|
629
|
+
unique: true,
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
634
|
+
let invoked = 0;
|
|
635
|
+
const idValue = 1;
|
|
636
|
+
const modelData = {
|
|
637
|
+
[DEF_PK]: idValue,
|
|
638
|
+
foo: 'bar',
|
|
639
|
+
};
|
|
640
|
+
const countMethod = where => {
|
|
641
|
+
invoked++;
|
|
642
|
+
if (invoked === 1) {
|
|
643
|
+
expect(where).to.be.eql({
|
|
644
|
+
[DEF_PK]: idValue,
|
|
645
|
+
});
|
|
646
|
+
return 1;
|
|
647
|
+
} else if (invoked === 2) {
|
|
648
|
+
expect(where).to.be.eql({
|
|
649
|
+
[DEF_PK]: {neq: idValue},
|
|
650
|
+
foo: 'bar',
|
|
651
|
+
});
|
|
652
|
+
return 0;
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
await S.validate(
|
|
656
|
+
countMethod,
|
|
657
|
+
'replaceOrCreate',
|
|
658
|
+
'model',
|
|
659
|
+
modelData,
|
|
660
|
+
);
|
|
661
|
+
expect(invoked).to.be.eq(2);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('uses a custom primary key to check existence of the given identifier', async function () {
|
|
665
|
+
const schema = new Schema();
|
|
666
|
+
schema.defineModel({
|
|
667
|
+
name: 'model',
|
|
668
|
+
properties: {
|
|
669
|
+
myId: {
|
|
670
|
+
type: DataType.NUMBER,
|
|
671
|
+
primaryKey: true,
|
|
672
|
+
},
|
|
673
|
+
foo: {
|
|
674
|
+
type: DataType.ANY,
|
|
675
|
+
unique: true,
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
});
|
|
679
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
680
|
+
let invoked = 0;
|
|
681
|
+
const idValue = 1;
|
|
682
|
+
const modelData = {myId: idValue, foo: 'bar'};
|
|
683
|
+
const countMethod = where => {
|
|
684
|
+
invoked++;
|
|
685
|
+
if (invoked === 1) {
|
|
686
|
+
expect(where).to.be.eql({
|
|
687
|
+
myId: idValue,
|
|
688
|
+
});
|
|
689
|
+
return 1;
|
|
690
|
+
} else if (invoked === 2) {
|
|
691
|
+
expect(where).to.be.eql({
|
|
692
|
+
myId: {neq: idValue},
|
|
693
|
+
foo: 'bar',
|
|
694
|
+
});
|
|
695
|
+
return 0;
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
await S.validate(
|
|
699
|
+
countMethod,
|
|
700
|
+
'replaceOrCreate',
|
|
701
|
+
'model',
|
|
702
|
+
modelData,
|
|
703
|
+
idValue,
|
|
704
|
+
);
|
|
705
|
+
expect(invoked).to.be.eq(2);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('checks the given identifier only once', async function () {
|
|
709
|
+
const schema = new Schema();
|
|
710
|
+
schema.defineModel({
|
|
711
|
+
name: 'model',
|
|
712
|
+
properties: {
|
|
713
|
+
foo: {
|
|
714
|
+
type: DataType.ANY,
|
|
715
|
+
unique: true,
|
|
716
|
+
},
|
|
717
|
+
bar: {
|
|
718
|
+
type: DataType.ANY,
|
|
719
|
+
unique: true,
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
724
|
+
let invoked = 0;
|
|
725
|
+
const idValue = 1;
|
|
726
|
+
const modelData = {
|
|
727
|
+
[DEF_PK]: idValue,
|
|
728
|
+
foo: 'val1',
|
|
729
|
+
bar: 'val2',
|
|
730
|
+
};
|
|
731
|
+
const countMethod = where => {
|
|
732
|
+
invoked++;
|
|
733
|
+
if (invoked === 1) {
|
|
734
|
+
expect(where).to.be.eql({[DEF_PK]: idValue});
|
|
735
|
+
return 1;
|
|
736
|
+
} else if (invoked === 2) {
|
|
737
|
+
expect(where).to.be.eql({
|
|
738
|
+
[DEF_PK]: {neq: idValue},
|
|
739
|
+
foo: 'val1',
|
|
740
|
+
});
|
|
741
|
+
return 0;
|
|
742
|
+
} else if (invoked === 3) {
|
|
743
|
+
expect(where).to.be.eql({
|
|
744
|
+
[DEF_PK]: {neq: idValue},
|
|
745
|
+
bar: 'val2',
|
|
746
|
+
});
|
|
747
|
+
return 0;
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
await S.validate(
|
|
751
|
+
countMethod,
|
|
752
|
+
'replaceOrCreate',
|
|
753
|
+
'model',
|
|
754
|
+
modelData,
|
|
755
|
+
);
|
|
756
|
+
expect(invoked).to.be.eq(3);
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
describe('patch', function () {
|
|
763
|
+
it('throws an error if the "countMethod" returns a positive number', async function () {
|
|
764
|
+
const schema = new Schema();
|
|
765
|
+
schema.defineModel({
|
|
766
|
+
name: 'model',
|
|
767
|
+
properties: {
|
|
768
|
+
foo: {
|
|
769
|
+
type: DataType.ANY,
|
|
770
|
+
unique: true,
|
|
771
|
+
},
|
|
772
|
+
},
|
|
773
|
+
});
|
|
774
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
775
|
+
const promise = S.validate(() => 1, 'patch', 'model', {foo: 'bar'});
|
|
776
|
+
await expect(promise).to.be.rejectedWith(
|
|
777
|
+
'An existing document of the model "model" already has ' +
|
|
778
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
779
|
+
);
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
it('passes validation if the "countMethod" returns zero', async function () {
|
|
783
|
+
const schema = new Schema();
|
|
784
|
+
schema.defineModel({
|
|
785
|
+
name: 'model',
|
|
786
|
+
properties: {
|
|
787
|
+
foo: {
|
|
788
|
+
type: DataType.ANY,
|
|
789
|
+
unique: true,
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
});
|
|
793
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
794
|
+
await S.validate(() => 0, 'patch', 'model', {foo: 'bar'});
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
it('invokes the "countMethod" only for given properties which should be unique', async function () {
|
|
798
|
+
const schema = new Schema();
|
|
799
|
+
schema.defineModel({
|
|
800
|
+
name: 'model',
|
|
801
|
+
properties: {
|
|
802
|
+
foo: {
|
|
803
|
+
type: DataType.ANY,
|
|
804
|
+
unique: true,
|
|
805
|
+
},
|
|
806
|
+
bar: {
|
|
807
|
+
type: DataType.ANY,
|
|
808
|
+
unique: false,
|
|
809
|
+
},
|
|
810
|
+
baz: {
|
|
811
|
+
type: DataType.ANY,
|
|
812
|
+
unique: true,
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
817
|
+
let invoked = 0;
|
|
818
|
+
const modelData = {foo: 'val1', bar: 'val2'};
|
|
819
|
+
const countMethod = where => {
|
|
820
|
+
if (invoked === 0) expect(where).to.be.eql({foo: 'val1'});
|
|
821
|
+
invoked++;
|
|
822
|
+
return 0;
|
|
823
|
+
};
|
|
824
|
+
await S.validate(countMethod, 'patch', 'model', modelData);
|
|
825
|
+
expect(invoked).to.be.eq(1);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it('skips not provided fields to check uniqueness', async function () {
|
|
829
|
+
const schema = new Schema();
|
|
830
|
+
schema.defineModel({
|
|
831
|
+
name: 'model',
|
|
832
|
+
properties: {
|
|
833
|
+
foo: {
|
|
834
|
+
type: DataType.ANY,
|
|
835
|
+
unique: true,
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
});
|
|
839
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
840
|
+
const promise1 = S.validate(() => 1, 'patch', 'model', {foo: 'bar'});
|
|
841
|
+
const promise2 = S.validate(() => 1, 'patch', 'model', {baz: 'qux'});
|
|
842
|
+
await expect(promise1).to.be.rejectedWith(
|
|
843
|
+
'An existing document of the model "model" already has ' +
|
|
844
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
845
|
+
);
|
|
846
|
+
await expect(promise2).not.to.be.rejected;
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
describe('patchById', function () {
|
|
851
|
+
it('throws an error if the "countMethod" returns a positive number', async function () {
|
|
852
|
+
const schema = new Schema();
|
|
853
|
+
schema.defineModel({
|
|
854
|
+
name: 'model',
|
|
855
|
+
properties: {
|
|
856
|
+
foo: {
|
|
857
|
+
type: DataType.ANY,
|
|
858
|
+
unique: true,
|
|
859
|
+
},
|
|
860
|
+
},
|
|
861
|
+
});
|
|
862
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
863
|
+
const promise = S.validate(
|
|
864
|
+
() => 1,
|
|
865
|
+
'patchById',
|
|
866
|
+
'model',
|
|
867
|
+
{foo: 'bar'},
|
|
868
|
+
1,
|
|
869
|
+
);
|
|
870
|
+
await expect(promise).to.be.rejectedWith(
|
|
871
|
+
'An existing document of the model "model" already has ' +
|
|
872
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
873
|
+
);
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it('passes validation if the "countMethod" returns zero', async function () {
|
|
877
|
+
const schema = new Schema();
|
|
878
|
+
schema.defineModel({
|
|
879
|
+
name: 'model',
|
|
880
|
+
properties: {
|
|
881
|
+
foo: {
|
|
882
|
+
type: DataType.ANY,
|
|
883
|
+
unique: true,
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
});
|
|
887
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
888
|
+
await S.validate(() => 0, 'patchById', 'model', {foo: 'bar'}, 1);
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
it('invokes the "countMethod" only for given properties which should be unique', async function () {
|
|
892
|
+
const schema = new Schema();
|
|
893
|
+
schema.defineModel({
|
|
894
|
+
name: 'model',
|
|
895
|
+
properties: {
|
|
896
|
+
foo: {
|
|
897
|
+
type: DataType.ANY,
|
|
898
|
+
unique: true,
|
|
899
|
+
},
|
|
900
|
+
bar: {
|
|
901
|
+
type: DataType.ANY,
|
|
902
|
+
unique: false,
|
|
903
|
+
},
|
|
904
|
+
baz: {
|
|
905
|
+
type: DataType.ANY,
|
|
906
|
+
unique: true,
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
});
|
|
910
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
911
|
+
let invoked = 0;
|
|
912
|
+
const idValue = 1;
|
|
913
|
+
const modelData = {foo: 'val1', bar: 'val2'};
|
|
914
|
+
const countMethod = where => {
|
|
915
|
+
if (invoked === 0) {
|
|
916
|
+
expect(where).to.be.eql({
|
|
917
|
+
[DEF_PK]: {neq: idValue},
|
|
918
|
+
foo: 'val1',
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
invoked++;
|
|
922
|
+
return 0;
|
|
923
|
+
};
|
|
924
|
+
await S.validate(countMethod, 'patchById', 'model', modelData, idValue);
|
|
925
|
+
expect(invoked).to.be.eq(1);
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it('skips not provided fields to check uniqueness', async function () {
|
|
929
|
+
const schema = new Schema();
|
|
930
|
+
schema.defineModel({
|
|
931
|
+
name: 'model',
|
|
932
|
+
properties: {
|
|
933
|
+
foo: {
|
|
934
|
+
type: DataType.ANY,
|
|
935
|
+
unique: true,
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
});
|
|
939
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
940
|
+
const promise1 = S.validate(() => 1, 'patchById', 'model', {
|
|
941
|
+
foo: 'bar',
|
|
942
|
+
});
|
|
943
|
+
const promise2 = S.validate(() => 1, 'patchById', 'model', {
|
|
944
|
+
baz: 'qux',
|
|
945
|
+
});
|
|
946
|
+
await expect(promise1).to.be.rejectedWith(
|
|
947
|
+
'An existing document of the model "model" already has ' +
|
|
948
|
+
'the property "foo" with the value "bar" and should be unique.',
|
|
949
|
+
);
|
|
950
|
+
await expect(promise2).not.to.be.rejected;
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it('uses a custom primary key to check existence of the given identifier', async function () {
|
|
954
|
+
const schema = new Schema();
|
|
955
|
+
schema.defineModel({
|
|
956
|
+
name: 'model',
|
|
957
|
+
properties: {
|
|
958
|
+
myId: {
|
|
959
|
+
type: DataType.NUMBER,
|
|
960
|
+
primaryKey: true,
|
|
961
|
+
},
|
|
962
|
+
foo: {
|
|
963
|
+
type: DataType.ANY,
|
|
964
|
+
unique: true,
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
});
|
|
968
|
+
const S = schema.getService(PropertyUniquenessValidator);
|
|
969
|
+
let invoked = 0;
|
|
970
|
+
const idValue = 1;
|
|
971
|
+
const modelData = {foo: 'bar'};
|
|
972
|
+
const countMethod = where => {
|
|
973
|
+
if (invoked === 0) {
|
|
974
|
+
expect(where).to.be.eql({
|
|
975
|
+
myId: {neq: idValue},
|
|
976
|
+
foo: 'bar',
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
invoked++;
|
|
980
|
+
return 0;
|
|
981
|
+
};
|
|
982
|
+
await S.validate(countMethod, 'patchById', 'model', modelData, idValue);
|
|
983
|
+
expect(invoked).to.be.eq(1);
|
|
984
|
+
});
|
|
985
|
+
});
|
|
986
|
+
});
|
|
987
|
+
});
|