@e22m4u/js-repository 0.0.37 → 0.0.39

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 CHANGED
@@ -2,26 +2,45 @@
2
2
 
3
3
  Абстракция для работы с базами данных для Node.js
4
4
 
5
- | адаптер | описание |
6
- |---------|-------------------------------------------------------------------------------------------------------------------------------------------------|
7
- | memory | виртуальная база в памяти процесса (для разработки и тестирования) |
8
- | mongodb | MongoDB - система управления NoSQL базами данных (*[требует установки](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
9
-
10
5
  ## Установка
11
6
 
12
7
  ```bash
13
8
  npm install @e22m4u/js-repository
14
9
  ```
15
10
 
16
- Опционально устанавливаем адаптер, например [mongodb](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter)
11
+ Опционально устанавливаем адаптер. Например, если используемой базой
12
+ является *MongoDB*, то для подключения потребуется добавить
13
+ [адаптер mongodb](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter)
14
+ как отдельную зависимость.
17
15
 
18
16
  ```bash
19
17
  npm install @e22m4u/js-repository-mongodb-adapter
20
18
  ```
21
19
 
22
- ## Пример
20
+ Список доступных адаптеров:
21
+
22
+ | адаптер | описание |
23
+ |-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
24
+ | `memory` | виртуальная база в памяти процесса (для разработки и тестирования) |
25
+ | `mongodb` | MongoDB - система управления NoSQL базами (*[требует установки](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
26
+
27
+ ## Концепция
28
+
29
+ Модуль позволяет спроектировать систему связанных данных, доступ к которым
30
+ осуществляется посредством репозиториев. Каждый репозиторий имеет собственную
31
+ модель, которая описывает структуру определенной коллекции в базе,
32
+ а так же определяет связи к другим коллекциям.
23
33
 
24
- Создаем экземпляр класса `Schema`
34
+ ```mermaid
35
+ flowchart LR
36
+
37
+ A[Datasource]-->B[Model]-->С[Repository];
38
+ ```
39
+
40
+ ## Использование
41
+
42
+ Определения источников и моделей хранятся в экземпляре класса `Schema`,
43
+ и первым шагом будет создание данного экземпляра.
25
44
 
26
45
  ```js
27
46
  import {Schema} from '@e22m4u/js-repository';
@@ -29,7 +48,38 @@ import {Schema} from '@e22m4u/js-repository';
29
48
  const schema = new Schema();
30
49
  ```
31
50
 
32
- Создаем источник данных `myMemory`
51
+ Интерфейс `Schema` содержит три основных метода:
52
+
53
+ - `defineDatasource(datasourceDef: object): this` - добавить источник
54
+ - `defineModel(modelDef: object): this` - добавить модель
55
+ - `getRepository(modelName: string): Repository` - получить репозиторий
56
+
57
+ #### Источник данных
58
+
59
+ Источник описывает способ подключения к базе и используемый адаптер.
60
+ Если адаптер имеет настройки, то они передаются вместе с объектом
61
+ определения источника методом `defineDatasource`, как это показано
62
+ ниже.
63
+
64
+ ```js
65
+ schema.defineDatasource({
66
+ name: 'myMongo', // название нового источника
67
+ adapter: 'mongodb', // название выбранного адаптера
68
+ // настройки адаптера mongodb
69
+ host: '127.0.0.1',
70
+ port: 27017,
71
+ database: 'data'
72
+ });
73
+ ```
74
+
75
+ Параметры источника:
76
+
77
+ - `name: string` уникальное название
78
+ - `adapter: string` выбранный адаптер
79
+
80
+ При желании можно использовать встроенный адаптер `memory`, который хранит
81
+ данные в памяти процесса. У него нет специальных настроек, и он отлично
82
+ подходит для тестов и прототипирования.
33
83
 
34
84
  ```js
35
85
  schema.defineDatasource({
@@ -38,6 +88,127 @@ schema.defineDatasource({
38
88
  });
39
89
  ```
40
90
 
91
+ #### Модель данных
92
+
93
+ Когда источники определены, можно перейти к описанию моделей данных.
94
+ Модель может определять как структуру какого-либо объекта,
95
+ так и являться отражением реальной коллекции базы.
96
+
97
+ Представьте себе коллекцию торговых точек, у каждой из которых имеются
98
+ координаты `lat` и `lng`. Мы можем заранее определить модель для
99
+ объекта координат методом `defineModel` и использовать ее в других
100
+ коллекциях.
101
+
102
+ ```js
103
+ schema.defineModel({
104
+ name: 'latLng', // название новой модели
105
+ properties: { // поля модели
106
+ lat: DataType.NUMBER, // поле широты
107
+ lng: DataType.NUMBER, // поле долготы
108
+ },
109
+ });
110
+ ```
111
+
112
+ Параметры модели:
113
+
114
+ - `name: string` уникальное название (обязательно)
115
+ - `datasource: string` выбранный источник данных
116
+ - `properties: object` определения полей модели
117
+ - `relations: object` определения связей модели
118
+
119
+ Параметр `properties` принимает объект, ключи которого являются именами
120
+ полей, а значением тип поля или объект с дополнительными параметрами.
121
+
122
+ ```js
123
+ schema.defineModel({
124
+ name: 'latLng',
125
+ properties: {
126
+ lat: DataType.NUMBER, // краткое определение поля "lat"
127
+ lng: { // определение поля "lng" с параметром "required"
128
+ type: DataType.NUMBER,
129
+ required: true, // исключает null и undefined
130
+ },
131
+ },
132
+ });
133
+ ```
134
+
135
+ Типы данных:
136
+
137
+ - `DataType.ANY`
138
+ - `DataType.STRING`
139
+ - `DataType.NUMBER`
140
+ - `DataType.BOOLEAN`
141
+ - `DataType.ARRAY`
142
+ - `DataType.OBJECT`
143
+
144
+ Модель `latLng` всего лишь описывает структуру объекта координат, тогда
145
+ как торговая точка должна иметь реальную таблицу в базе. По аналогии с
146
+ предыдущим примером, добавим модель `place`, но дополнительно укажем
147
+ источник данных в параметре `datasource`
148
+
149
+ ```js
150
+ schema.defineModel({
151
+ name: 'place',
152
+ datasource: 'myMemory', // выбранный источник данных
153
+ properties: {
154
+ name: DataType.STRING, // поле для названия торговой точки
155
+ location: { // поле объекта координат
156
+ type: DataType.OBJECT, // допускать только объекты
157
+ model: 'latLng', // определение структуры объекта
158
+ },
159
+ },
160
+ });
161
+ ```
162
+
163
+ В примере выше мы использовали модель `latLng` как структуру допустимого
164
+ значения поля `location`. Возможный документ данной коллекции может
165
+ выглядеть так:
166
+
167
+ ```json
168
+ {
169
+ "id": 1,
170
+ "name": "Burger King at Avenue Mall",
171
+ "location": {
172
+ "lat": 32.412891,
173
+ "lng": 34.7660061
174
+ }
175
+ }
176
+ ```
177
+
178
+ Стоит обратить внимание, что мы могли бы не объявлять параметр `properties`,
179
+ при этом теряя возможность проверки данных перед записью в базу.
180
+
181
+ ```js
182
+ schema.defineModel({
183
+ name: 'place',
184
+ adapter: 'myMemory',
185
+ // параметр "properties" не указан
186
+ });
187
+ ```
188
+
189
+ Параметры поля:
190
+
191
+ - `type: string` тип допустимого значения (обязательно)
192
+ - `itemType: string` тип элемента массива (для `type: 'array'`)
193
+ - `model: string` модель объекта (для `type: 'object'`)
194
+ - `primaryKey: boolean` объявить поле первичным ключом
195
+ - `columnName: string` переопределение названия колонки
196
+ - `columnType: string` тип колонки (определяется адаптером)
197
+ - `required: boolean` объявить поле обязательным
198
+ - `default: any` значение по умолчанию
199
+
200
+ #### Репозиторий
201
+
202
+ В отличие от `latLng`, модель `place` имеет источник данных с названием
203
+ `myMemory`, который был объявлен ранее. Наличие источника позволяет получить
204
+ репозиторий по названию модели.
205
+
206
+ ```js
207
+ const rep = schema.getRepository('place');
208
+ ```
209
+
210
+ ## Пример
211
+
41
212
  Создаем модель `user`
42
213
 
43
214
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-repository",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "Абстракция для работы с базами данных для Node.js",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -39,9 +39,9 @@ export class Adapter extends Service {
39
39
  this._settings = settings;
40
40
  // decorate only extended classes
41
41
  if (this.constructor !== Adapter) {
42
- this.getService(DataValidationDecorator).decorate(this);
43
42
  this.getService(DataSanitizingDecorator).decorate(this);
44
43
  this.getService(DefaultValuesDecorator).decorate(this);
44
+ this.getService(DataValidationDecorator).decorate(this);
45
45
  this.getService(FieldsFilteringDecorator).decorate(this);
46
46
  this.getService(InclusionDecorator).decorate(this);
47
47
  }
@@ -33,9 +33,9 @@ describe('Adapter', function () {
33
33
 
34
34
  it('decorates only extended adapter', function () {
35
35
  const schema = new Schema();
36
- const dec1 = schema.getService(DataValidationDecorator);
37
- const dec2 = schema.getService(DataSanitizingDecorator);
38
- const dec3 = schema.getService(DefaultValuesDecorator);
36
+ const dec1 = schema.getService(DataSanitizingDecorator);
37
+ const dec2 = schema.getService(DefaultValuesDecorator);
38
+ const dec3 = schema.getService(DataValidationDecorator);
39
39
  const dec4 = schema.getService(FieldsFilteringDecorator);
40
40
  const dec5 = schema.getService(InclusionDecorator);
41
41
  const order = [];
@@ -31,7 +31,7 @@ export class ModelDataValidator extends Service {
31
31
  propNames.forEach(propName => {
32
32
  const propDef = propDefs[propName];
33
33
  if (!propDef) return;
34
- this.validatePropertyValue(
34
+ this._validatePropertyValue(
35
35
  modelName,
36
36
  propName,
37
37
  propDef,
@@ -48,7 +48,7 @@ export class ModelDataValidator extends Service {
48
48
  * @param {string|object} propDef
49
49
  * @param {*} propValue
50
50
  */
51
- validatePropertyValue(modelName, propName, propDef, propValue) {
51
+ _validatePropertyValue(modelName, propName, propDef, propValue) {
52
52
  // undefined and null
53
53
  if (propValue == null) {
54
54
  const isRequired =
@@ -1587,6 +1587,61 @@ describe('ModelDataValidator', function () {
1587
1587
  'an Array, but Object given.',
1588
1588
  );
1589
1589
  });
1590
+
1591
+ describe('the "model" option', function () {
1592
+ it('throws an error when the given object element has an invalid model', function () {
1593
+ const S = new Schema();
1594
+ S.defineModel({
1595
+ name: 'modelA',
1596
+ properties: {
1597
+ foo: DataType.STRING,
1598
+ },
1599
+ });
1600
+ S.defineModel({
1601
+ name: 'modelB',
1602
+ datasource: 'datasource',
1603
+ properties: {
1604
+ bar: {
1605
+ type: DataType.ARRAY,
1606
+ itemType: DataType.OBJECT,
1607
+ model: 'modelA',
1608
+ },
1609
+ },
1610
+ });
1611
+ const throwable = () =>
1612
+ S.getService(ModelDataValidator).validate('modelB', {
1613
+ bar: [{foo: 10}],
1614
+ });
1615
+ expect(throwable).to.throw(
1616
+ 'The property "foo" of the model "modelA" must have ' +
1617
+ 'a String, but Number given.',
1618
+ );
1619
+ });
1620
+
1621
+ it('does not throw an error when the given object element has a valid model', function () {
1622
+ const S = new Schema();
1623
+ S.defineModel({
1624
+ name: 'modelA',
1625
+ properties: {
1626
+ foo: DataType.STRING,
1627
+ },
1628
+ });
1629
+ S.defineModel({
1630
+ name: 'modelB',
1631
+ datasource: 'datasource',
1632
+ properties: {
1633
+ bar: {
1634
+ type: DataType.ARRAY,
1635
+ itemType: DataType.OBJECT,
1636
+ model: 'modelA',
1637
+ },
1638
+ },
1639
+ });
1640
+ S.getService(ModelDataValidator).validate('modelB', {
1641
+ bar: [{foo: '10'}],
1642
+ });
1643
+ });
1644
+ });
1590
1645
  });
1591
1646
  });
1592
1647
 
@@ -1883,6 +1938,59 @@ describe('ModelDataValidator', function () {
1883
1938
  foo: {},
1884
1939
  });
1885
1940
  });
1941
+
1942
+ describe('the "model" option', function () {
1943
+ it('throws an error when the given object has an invalid model', function () {
1944
+ const S = new Schema();
1945
+ S.defineModel({
1946
+ name: 'modelA',
1947
+ properties: {
1948
+ foo: DataType.STRING,
1949
+ },
1950
+ });
1951
+ S.defineModel({
1952
+ name: 'modelB',
1953
+ datasource: 'datasource',
1954
+ properties: {
1955
+ bar: {
1956
+ type: DataType.OBJECT,
1957
+ model: 'modelA',
1958
+ },
1959
+ },
1960
+ });
1961
+ const throwable = () =>
1962
+ S.getService(ModelDataValidator).validate('modelB', {
1963
+ bar: {foo: 10},
1964
+ });
1965
+ expect(throwable).to.throw(
1966
+ 'The property "foo" of the model "modelA" must have ' +
1967
+ 'a String, but Number given.',
1968
+ );
1969
+ });
1970
+
1971
+ it('does not throw an error when the given object has a valid model', function () {
1972
+ const S = new Schema();
1973
+ S.defineModel({
1974
+ name: 'modelA',
1975
+ properties: {
1976
+ foo: DataType.STRING,
1977
+ },
1978
+ });
1979
+ S.defineModel({
1980
+ name: 'modelB',
1981
+ datasource: 'datasource',
1982
+ properties: {
1983
+ bar: {
1984
+ type: DataType.OBJECT,
1985
+ model: 'modelA',
1986
+ },
1987
+ },
1988
+ });
1989
+ S.getService(ModelDataValidator).validate('modelB', {
1990
+ bar: {foo: '10'},
1991
+ });
1992
+ });
1993
+ });
1886
1994
  });
1887
1995
  });
1888
1996
  });
@@ -2,4 +2,3 @@ export * from './data-type.js';
2
2
  export * from './property-definition.js';
3
3
  export * from './properties-definition-validator.js';
4
4
  export * from './primary-keys-definition-validator.js';
5
- export * from './default-values-definition-validator.js';
@@ -1,4 +1,3 @@
1
1
  export * from './data-type.js';
2
2
  export * from './properties-definition-validator.js';
3
3
  export * from './primary-keys-definition-validator.js';
4
- export * from './default-values-definition-validator.js';
@@ -1,8 +1,8 @@
1
1
  import {Service} from '@e22m4u/js-service';
2
2
  import {DataType as Type} from './data-type.js';
3
+ import {capitalize} from '../../../utils/index.js';
3
4
  import {InvalidArgumentError} from '../../../errors/index.js';
4
5
  import {PrimaryKeysDefinitionValidator} from './primary-keys-definition-validator.js';
5
- import {DefaultValuesDefinitionValidator} from './default-values-definition-validator.js';
6
6
 
7
7
  /**
8
8
  * Properties definition validator.
@@ -38,10 +38,6 @@ export class PropertiesDefinitionValidator extends Service {
38
38
  modelName,
39
39
  propDefs,
40
40
  );
41
- this.getService(DefaultValuesDefinitionValidator).validate(
42
- modelName,
43
- propDefs,
44
- );
45
41
  }
46
42
 
47
43
  /**
@@ -182,13 +178,30 @@ export class PropertiesDefinitionValidator extends Service {
182
178
  modelName,
183
179
  propDef.type,
184
180
  );
185
- if (propDef.model && propDef.type !== Type.OBJECT)
186
- throw new InvalidArgumentError(
187
- 'The property %v of the model %v has the non-object type, ' +
188
- 'so it should not have the option "model" to be provided.',
189
- propName,
190
- modelName,
191
- propDef.type,
192
- );
181
+ if (
182
+ propDef.model &&
183
+ propDef.type !== Type.OBJECT &&
184
+ propDef.itemType !== Type.OBJECT
185
+ ) {
186
+ if (propDef.type !== Type.ARRAY) {
187
+ throw new InvalidArgumentError(
188
+ 'The option "model" is not supported for %s property type, ' +
189
+ 'so the property %v of the model %v should not have ' +
190
+ 'the option "model" to be provided.',
191
+ capitalize(propDef.type),
192
+ propName,
193
+ modelName,
194
+ );
195
+ } else {
196
+ throw new InvalidArgumentError(
197
+ 'The option "model" is not supported for Array property type of %s, ' +
198
+ 'so the property %v of the model %v should not have ' +
199
+ 'the option "model" to be provided.',
200
+ capitalize(propDef.itemType),
201
+ propName,
202
+ modelName,
203
+ );
204
+ }
205
+ }
193
206
  }
194
207
  }
@@ -4,7 +4,6 @@ import {DataType} from './data-type.js';
4
4
  import {format} from '@e22m4u/js-format';
5
5
  import {PropertiesDefinitionValidator} from './properties-definition-validator.js';
6
6
  import {PrimaryKeysDefinitionValidator} from './primary-keys-definition-validator.js';
7
- import {DefaultValuesDefinitionValidator} from './default-values-definition-validator.js';
8
7
 
9
8
  const S = new PropertiesDefinitionValidator();
10
9
  const sandbox = chai.spy.sandbox();
@@ -333,7 +332,7 @@ describe('PropertiesDefinitionValidator', function () {
333
332
  validate(DataType.ARRAY);
334
333
  });
335
334
 
336
- it('expects a non-object property should not have the option "model" to be provided', function () {
335
+ it('the option "model" requires the "object" property type', function () {
337
336
  const validate = v => () => {
338
337
  const foo = {
339
338
  type: v,
@@ -341,28 +340,46 @@ describe('PropertiesDefinitionValidator', function () {
341
340
  };
342
341
  S.validate('model', {foo});
343
342
  };
344
- const error =
345
- 'The property "foo" of the model "model" has the non-object type, ' +
346
- 'so it should not have the option "model" to be provided.';
347
- expect(validate(DataType.ANY)).to.throw(error);
348
- expect(validate(DataType.STRING)).to.throw(error);
349
- expect(validate(DataType.NUMBER)).to.throw(error);
350
- expect(validate(DataType.BOOLEAN)).to.throw(error);
351
- expect(validate(DataType.ARRAY)).to.throw(error);
352
- validate(DataType.OBJECT);
343
+ const error = v =>
344
+ format(
345
+ 'The option "model" is not supported for %s property type, ' +
346
+ 'so the property "foo" of the model "model" should not have ' +
347
+ 'the option "model" to be provided.',
348
+ v,
349
+ );
350
+ expect(validate(DataType.ANY)).to.throw(error('Any'));
351
+ expect(validate(DataType.STRING)).to.throw(error('String'));
352
+ expect(validate(DataType.NUMBER)).to.throw(error('Number'));
353
+ expect(validate(DataType.BOOLEAN)).to.throw(error('Boolean'));
354
+ validate(DataType.OBJECT)();
353
355
  });
354
356
 
355
- it('uses PrimaryKeysDefinitionValidator to validate primary keys', function () {
356
- const V = S.getService(PrimaryKeysDefinitionValidator);
357
- sandbox.on(V, 'validate');
358
- const propDefs = {};
359
- S.validate('model', propDefs);
360
- expect(V.validate).to.have.been.called.once;
361
- expect(V.validate).to.have.been.called.with.exactly('model', propDefs);
357
+ it('the option "model" requires the "object" item type', function () {
358
+ const validate = v => () => {
359
+ const foo = {
360
+ type: DataType.ARRAY,
361
+ itemType: v,
362
+ model: 'model',
363
+ };
364
+ S.validate('model', {foo});
365
+ };
366
+ const error = v =>
367
+ format(
368
+ 'The option "model" is not supported for Array property type of %s, ' +
369
+ 'so the property "foo" of the model "model" should not have ' +
370
+ 'the option "model" to be provided.',
371
+ v,
372
+ );
373
+ expect(validate(DataType.ANY)).to.throw(error('Any'));
374
+ expect(validate(DataType.STRING)).to.throw(error('String'));
375
+ expect(validate(DataType.NUMBER)).to.throw(error('Number'));
376
+ expect(validate(DataType.BOOLEAN)).to.throw(error('Boolean'));
377
+ expect(validate(DataType.ARRAY)).to.throw(error('Array'));
378
+ validate(DataType.OBJECT)();
362
379
  });
363
380
 
364
- it('uses DefaultValuesDefinitionValidator to validate default values', function () {
365
- const V = S.getService(DefaultValuesDefinitionValidator);
381
+ it('uses PrimaryKeysDefinitionValidator to validate primary keys', function () {
382
+ const V = S.getService(PrimaryKeysDefinitionValidator);
366
383
  sandbox.on(V, 'validate');
367
384
  const propDefs = {};
368
385
  S.validate('model', propDefs);
@@ -237,11 +237,9 @@ export type NormalizedFieldsClause = string[];
237
237
  */
238
238
  export declare type IncludeClause =
239
239
  | string
240
- | string[]
241
240
  | NestedIncludeClause
242
- | NestedIncludeClause[]
243
241
  | NormalizedIncludeClause
244
- | NormalizedIncludeClause[];
242
+ | IncludeClause[];
245
243
 
246
244
  /**
247
245
  * Nested include clause.
@@ -1,15 +0,0 @@
1
- import {Service} from '@e22m4u/js-service';
2
- import {PropertyDefinitionMap} from '../model-definition.js';
3
-
4
- /**
5
- * Default values definition validator.
6
- */
7
- export declare class DefaultValuesDefinitionValidator extends Service {
8
- /**
9
- * Validate.
10
- *
11
- * @param modelName
12
- * @param propDefs
13
- */
14
- validate(modelName: string, propDefs: PropertyDefinitionMap): void;
15
- }
@@ -1,53 +0,0 @@
1
- import {Service} from '@e22m4u/js-service';
2
- import {ModelDataValidator} from '../model-data-validator.js';
3
- import {InvalidArgumentError} from '../../../errors/index.js';
4
-
5
- /**
6
- * Default values definition validator.
7
- */
8
- export class DefaultValuesDefinitionValidator extends Service {
9
- /**
10
- * Validate.
11
- *
12
- * @param {string} modelName
13
- * @param {object} propDefs
14
- */
15
- validate(modelName, propDefs) {
16
- if (!modelName || typeof modelName !== 'string')
17
- throw new InvalidArgumentError(
18
- 'The first argument of DefaultValuesDefinitionValidator.validate ' +
19
- 'should be a non-empty String, but %v given.',
20
- modelName,
21
- );
22
- if (!propDefs || typeof propDefs !== 'object' || Array.isArray(propDefs))
23
- throw new InvalidArgumentError(
24
- 'The provided option "properties" of the model %v ' +
25
- 'should be an Object, but %v given.',
26
- modelName,
27
- propDefs,
28
- );
29
- Object.keys(propDefs).forEach(propName => {
30
- const propDef = propDefs[propName];
31
- if (typeof propDef === 'string') return;
32
- if (!('default' in propDef)) return;
33
- const propValue =
34
- propDef.default instanceof Function
35
- ? propDef.default()
36
- : propDef.default;
37
- try {
38
- this.getService(ModelDataValidator).validatePropertyValue(
39
- modelName,
40
- propName,
41
- propDef,
42
- propValue,
43
- );
44
- } catch (error) {
45
- if (error instanceof InvalidArgumentError)
46
- throw new InvalidArgumentError(
47
- `A default value is invalid. ${error.message}`,
48
- );
49
- throw error;
50
- }
51
- });
52
- }
53
- }
@@ -1,136 +0,0 @@
1
- import {expect} from 'chai';
2
- import {DataType} from './data-type.js';
3
- import {format} from '@e22m4u/js-format';
4
- import {DefaultValuesDefinitionValidator} from './default-values-definition-validator.js';
5
-
6
- const S = new DefaultValuesDefinitionValidator();
7
-
8
- describe('DefaultValuesDefinitionValidator', function () {
9
- describe('validate', function () {
10
- it('requires a first argument to be a non-empty string', function () {
11
- const validate = v => () => S.validate(v, {});
12
- const error = v =>
13
- format(
14
- 'The first argument of DefaultValuesDefinitionValidator.validate ' +
15
- 'should be a non-empty String, but %s given.',
16
- v,
17
- );
18
- expect(validate('')).to.throw(error('""'));
19
- expect(validate(10)).to.throw(error('10'));
20
- expect(validate(true)).to.throw(error('true'));
21
- expect(validate(false)).to.throw(error('false'));
22
- expect(validate([])).to.throw(error('Array'));
23
- expect(validate({})).to.throw(error('Object'));
24
- expect(validate(undefined)).to.throw(error('undefined'));
25
- expect(validate(null)).to.throw(error('null'));
26
- validate('model')();
27
- });
28
-
29
- it('requires a second argument to be an object', function () {
30
- const validate = v => () => S.validate('model', v);
31
- const error = v =>
32
- format(
33
- 'The provided option "properties" of the model "model" ' +
34
- 'should be an Object, but %s given.',
35
- v,
36
- );
37
- expect(validate('str')).to.throw(error('"str"'));
38
- expect(validate(10)).to.throw(error('10'));
39
- expect(validate(true)).to.throw(error('true'));
40
- expect(validate(false)).to.throw(error('false'));
41
- expect(validate([])).to.throw(error('Array'));
42
- expect(validate(undefined)).to.throw(error('undefined'));
43
- expect(validate(null)).to.throw(error('null'));
44
- validate({})();
45
- });
46
-
47
- it('does not throw an error if no properties defined', function () {
48
- S.validate('model', {});
49
- });
50
-
51
- it('does not throw an error if no default value specified for a required property', function () {
52
- S.validate('model', {
53
- foo: {
54
- type: DataType.STRING,
55
- required: true,
56
- },
57
- });
58
- });
59
-
60
- it('does not throw an error if a default value matches a property type', function () {
61
- S.validate('model', {
62
- foo: {
63
- type: DataType.BOOLEAN,
64
- default: false,
65
- },
66
- });
67
- });
68
-
69
- it('does not throw an error if a default value from a factory function matches a property type', function () {
70
- S.validate('model', {
71
- foo: {
72
- type: DataType.BOOLEAN,
73
- default: () => false,
74
- },
75
- });
76
- });
77
-
78
- it('throws an error if a default value does not match a property type', function () {
79
- const throwable = () =>
80
- S.validate('model', {
81
- foo: {
82
- type: DataType.STRING,
83
- default: 10,
84
- },
85
- });
86
- expect(throwable).to.throw(
87
- 'A default value is invalid. The property "foo" of the model ' +
88
- '"model" must have a String, but Number given.',
89
- );
90
- });
91
-
92
- it('throws an error if a default value from a factory function does not match a property type', function () {
93
- const throwable = () =>
94
- S.validate('model', {
95
- foo: {
96
- type: DataType.STRING,
97
- default: () => 10,
98
- },
99
- });
100
- expect(throwable).to.throw(
101
- 'A default value is invalid. The property "foo" of the model ' +
102
- '"model" must have a String, but Number given.',
103
- );
104
- });
105
-
106
- it('throws an error if an array element of a default value does not match an item type', function () {
107
- const throwable = () =>
108
- S.validate('model', {
109
- foo: {
110
- type: DataType.ARRAY,
111
- itemType: DataType.STRING,
112
- default: [10],
113
- },
114
- });
115
- expect(throwable).to.throw(
116
- 'A default value is invalid. The array property "foo" of the model "model" ' +
117
- 'must have a String element, but Number given.',
118
- );
119
- });
120
-
121
- it('throws an error if an array element from a default value factory does not match an item type', function () {
122
- const throwable = () =>
123
- S.validate('model', {
124
- foo: {
125
- type: DataType.ARRAY,
126
- itemType: DataType.STRING,
127
- default: () => [10],
128
- },
129
- });
130
- expect(throwable).to.throw(
131
- 'A default value is invalid. The array property "foo" of the model "model" ' +
132
- 'must have a String element, but Number given.',
133
- );
134
- });
135
- });
136
- });