@e22m4u/js-repository-mongodb-adapter 0.0.15 → 0.0.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-repository-mongodb-adapter",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "MongoDB адаптер для @e22m4u/js-repository",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,10 +1,10 @@
1
1
  /* eslint no-unused-vars: 0 */
2
2
  import {ObjectId} from 'mongodb';
3
3
  import {MongoClient} from 'mongodb';
4
+ import {isIsoDate} from './utils/index.js';
4
5
  import {isObjectId} from './utils/index.js';
5
6
  import {Adapter} from '@e22m4u/js-repository';
6
7
  import {DataType} from '@e22m4u/js-repository';
7
- import {isIsoDate} from './utils/is-iso-date.js';
8
8
  import {capitalize} from '@e22m4u/js-repository';
9
9
  import {createMongodbUrl} from './utils/index.js';
10
10
  import {ServiceContainer} from '@e22m4u/js-service';
@@ -71,12 +71,11 @@ const MONGODB_OPTION_NAMES = [
71
71
  /**
72
72
  * Default settings.
73
73
  *
74
- * @type {{connectTimeoutMS: number}}
74
+ * @type {object}
75
75
  */
76
76
  const DEFAULT_SETTINGS = {
77
- reconnectInterval: 2000, // adapter specific option
78
- connectTimeoutMS: 2000,
79
- serverSelectionTimeoutMS: 2000,
77
+ // connectTimeoutMS: 2500,
78
+ // serverSelectionTimeoutMS: 2500,
80
79
  };
81
80
 
82
81
  /**
@@ -86,51 +85,27 @@ export class MongodbAdapter extends Adapter {
86
85
  /**
87
86
  * Mongodb instance.
88
87
  *
88
+ * @type {MongoClient}
89
89
  * @private
90
90
  */
91
91
  _client;
92
92
 
93
93
  /**
94
- * Collections.
95
- *
96
- * @type {Map<any, any>}
97
- * @private
98
- */
99
- _collections = new Map();
100
-
101
- /**
102
- * Connected.
94
+ * Client.
103
95
  *
104
- * @type {boolean}
105
- * @private
96
+ * @returns {MongoClient}
106
97
  */
107
- _connected = false;
108
-
109
- /**
110
- * Connected.
111
- *
112
- * @return {boolean}
113
- */
114
- get connected() {
115
- return this._connected;
98
+ get client() {
99
+ return this._client;
116
100
  }
117
101
 
118
102
  /**
119
- * Connecting.
103
+ * Collections.
120
104
  *
121
- * @type {boolean}
105
+ * @type {Map<any, any>}
122
106
  * @private
123
107
  */
124
- _connecting = false;
125
-
126
- /**
127
- * Connecting.
128
- *
129
- * @return {boolean}
130
- */
131
- get connecting() {
132
- return this._connecting;
133
- }
108
+ _collections = new Map();
134
109
 
135
110
  /**
136
111
  * Constructor.
@@ -140,80 +115,14 @@ export class MongodbAdapter extends Adapter {
140
115
  */
141
116
  constructor(container, settings) {
142
117
  settings = Object.assign({}, DEFAULT_SETTINGS, settings || {});
143
- super(container, settings);
144
118
  settings.protocol = settings.protocol || 'mongodb';
145
119
  settings.hostname = settings.hostname || settings.host || '127.0.0.1';
146
120
  settings.port = settings.port || 27017;
147
- settings.database = settings.database || settings.db || 'test';
148
- }
149
-
150
- /**
151
- * Connect.
152
- *
153
- * @return {Promise<*|undefined>}
154
- * @private
155
- */
156
- async connect() {
157
- if (this._connecting) {
158
- const tryAgainAfter = 500;
159
- await new Promise(r => setTimeout(() => r(), tryAgainAfter));
160
- return this.connect();
161
- }
162
-
163
- if (this._connected) return;
164
- this._connecting = true;
165
-
121
+ settings.database = settings.database || settings.db || 'database';
122
+ super(container, settings);
166
123
  const options = selectObjectKeys(this.settings, MONGODB_OPTION_NAMES);
167
124
  const url = createMongodbUrl(this.settings);
168
-
169
- // console.log(`Connecting to ${url}`);
170
125
  this._client = new MongoClient(url, options);
171
-
172
- const {reconnectInterval} = this.settings;
173
- const connectFn = async () => {
174
- try {
175
- await this._client.connect();
176
- } catch (e) {
177
- console.error(e);
178
- // console.log('MongoDB connection failed!');
179
- // console.log(`Reconnecting after ${reconnectInterval} ms.`);
180
- await new Promise(r => setTimeout(() => r(), reconnectInterval));
181
- return connectFn();
182
- }
183
- // console.log('MongoDB is connected.');
184
- this._connected = true;
185
- this._connecting = false;
186
- };
187
-
188
- await connectFn();
189
-
190
- this._client.once('serverClosed', event => {
191
- if (this._connected) {
192
- this._connected = false;
193
- // console.log('MongoDB lost connection!');
194
- console.log(event);
195
- // console.log(`Reconnecting after ${reconnectInterval} ms.`);
196
- setTimeout(() => connectFn(), reconnectInterval);
197
- } else {
198
- // console.log('MongoDB connection closed.');
199
- }
200
- });
201
- }
202
-
203
- /**
204
- * Disconnect.
205
- *
206
- * @return {Promise<*|undefined>}
207
- */
208
- async disconnect() {
209
- if (this._connecting) {
210
- const tryAgainAfter = 500;
211
- await new Promise(r => setTimeout(() => r(), tryAgainAfter));
212
- return this.disconnect();
213
- }
214
- if (!this._connected) return;
215
- this._connected = false;
216
- if (this._client) await this._client.close();
217
126
  }
218
127
 
219
128
  /**
@@ -251,19 +160,6 @@ export class MongodbAdapter extends Adapter {
251
160
  return value;
252
161
  }
253
162
 
254
- /**
255
- * Coerce iso date.
256
- *
257
- * @param value
258
- * @return {*|Date}
259
- * @private
260
- */
261
- _coerceIsoDate(value) {
262
- if (value === null) return value;
263
- if (isIsoDate(value)) return new Date(value);
264
- return value;
265
- }
266
-
267
163
  /**
268
164
  * To database.
269
165
  *
@@ -344,7 +240,7 @@ export class MongodbAdapter extends Adapter {
344
240
  if (collection) return collection;
345
241
  const tableName =
346
242
  this.getService(ModelDefinitionUtils).getTableNameByModelName(modelName);
347
- collection = this._client.db(this.settings.database).collection(tableName);
243
+ collection = this.client.db(this.settings.database).collection(tableName);
348
244
  this._collections.set(modelName, collection);
349
245
  return collection;
350
246
  }
@@ -641,7 +537,6 @@ export class MongodbAdapter extends Adapter {
641
537
  * @return {Promise<object>}
642
538
  */
643
539
  async create(modelName, modelData, filter = undefined) {
644
- await this.connect();
645
540
  const idPropName = this._getIdPropName(modelName);
646
541
  const idValue = modelData[idPropName];
647
542
  if (idValue == null) {
@@ -677,7 +572,6 @@ export class MongodbAdapter extends Adapter {
677
572
  * @return {Promise<object>}
678
573
  */
679
574
  async replaceById(modelName, id, modelData, filter = undefined) {
680
- await this.connect();
681
575
  id = this._coerceId(id);
682
576
  const idPropName = this._getIdPropName(modelName);
683
577
  modelData[idPropName] = id;
@@ -704,7 +598,6 @@ export class MongodbAdapter extends Adapter {
704
598
  * @return {Promise<object>}
705
599
  */
706
600
  async patchById(modelName, id, modelData, filter = undefined) {
707
- await this.connect();
708
601
  id = this._coerceId(id);
709
602
  const idPropName = this._getIdPropName(modelName);
710
603
  delete modelData[idPropName];
@@ -729,7 +622,6 @@ export class MongodbAdapter extends Adapter {
729
622
  * @return {Promise<object[]>}
730
623
  */
731
624
  async find(modelName, filter = undefined) {
732
- await this.connect();
733
625
  filter = filter || {};
734
626
  const query = this._buildQuery(modelName, filter.where);
735
627
  const sort = this._buildSort(modelName, filter.order);
@@ -751,7 +643,6 @@ export class MongodbAdapter extends Adapter {
751
643
  * @return {Promise<object>}
752
644
  */
753
645
  async findById(modelName, id, filter = undefined) {
754
- await this.connect();
755
646
  id = this._coerceId(id);
756
647
  const table = this._getCollection(modelName);
757
648
  const projection = this._buildProjection(
@@ -772,7 +663,6 @@ export class MongodbAdapter extends Adapter {
772
663
  * @return {Promise<number>}
773
664
  */
774
665
  async delete(modelName, where = undefined) {
775
- await this.connect();
776
666
  const table = this._getCollection(modelName);
777
667
  const query = this._buildQuery(modelName, where);
778
668
  const {deletedCount} = await table.deleteMany(query);
@@ -787,7 +677,6 @@ export class MongodbAdapter extends Adapter {
787
677
  * @return {Promise<boolean>}
788
678
  */
789
679
  async deleteById(modelName, id) {
790
- await this.connect();
791
680
  id = this._coerceId(id);
792
681
  const table = this._getCollection(modelName);
793
682
  const {deletedCount} = await table.deleteOne({_id: id});
@@ -802,7 +691,6 @@ export class MongodbAdapter extends Adapter {
802
691
  * @return {Promise<boolean>}
803
692
  */
804
693
  async exists(modelName, id) {
805
- await this.connect();
806
694
  id = this._coerceId(id);
807
695
  const table = this._getCollection(modelName);
808
696
  const result = await table.findOne({_id: id}, {});
@@ -817,7 +705,6 @@ export class MongodbAdapter extends Adapter {
817
705
  * @return {Promise<number>}
818
706
  */
819
707
  async count(modelName, where = undefined) {
820
- await this.connect();
821
708
  const query = this._buildQuery(modelName, where);
822
709
  const table = this._getCollection(modelName);
823
710
  return await table.count(query);
@@ -2,7 +2,6 @@ import {expect} from 'chai';
2
2
  import {ObjectId} from 'mongodb';
3
3
  import {MongoClient} from 'mongodb';
4
4
  import {format} from '@e22m4u/js-format';
5
- import {Service} from '@e22m4u/js-service';
6
5
  import {Schema} from '@e22m4u/js-repository';
7
6
  import {DataType} from '@e22m4u/js-repository';
8
7
  import {createMongodbUrl} from './utils/index.js';
@@ -37,19 +36,11 @@ describe('MongodbAdapter', function () {
37
36
 
38
37
  after(async function () {
39
38
  for await (const adapter of ADAPTERS_STACK) {
40
- await adapter.disconnect();
39
+ await adapter.client.close(true);
41
40
  }
42
41
  await MDB_CLIENT.close(true);
43
42
  });
44
43
 
45
- it('able to connect and disconnect', async function () {
46
- const S = new Service();
47
- const adapter = new MongodbAdapter(S.container, CONFIG);
48
- await adapter.connect();
49
- expect(adapter.connected).to.be.true;
50
- await adapter.disconnect();
51
- });
52
-
53
44
  describe('create', function () {
54
45
  it('generates a new identifier when a value of a primary key is not provided', async function () {
55
46
  const schema = createSchema();
@@ -11,7 +11,7 @@ export function createMongodbUrl(options = {}) {
11
11
  );
12
12
  if (options.protocol && typeof options.protocol !== 'string')
13
13
  throw new InvalidArgumentError(
14
- 'MongoDB option "protocol" must be a string, but %v given.',
14
+ 'MongoDB option "protocol" must be a String, but %v given.',
15
15
  options.protocol,
16
16
  );
17
17
  if (options.hostname && typeof options.hostname !== 'string')
@@ -8,11 +8,12 @@ describe('createMongodbUrl', function () {
8
8
  expect(value).to.be.eq('mongodb://127.0.0.1:27017/database');
9
9
  });
10
10
 
11
- it('throws an error when the first argument is a non-object value', function () {
11
+ it('requires the first argument to be an object', function () {
12
12
  const throwable = v => () => createMongodbUrl(v);
13
13
  const error = v =>
14
14
  format(
15
- 'The first argument of "createMongodbUrl" must be an Object, but %s given.',
15
+ 'The first argument of "createMongodbUrl" must be ' +
16
+ 'an Object, but %s given.',
16
17
  v,
17
18
  );
18
19
  expect(throwable('')).to.throw(error('""'));
@@ -26,4 +27,257 @@ describe('createMongodbUrl', function () {
26
27
  throwable(undefined)();
27
28
  throwable({})();
28
29
  });
30
+
31
+ it('requires the "protocol" option to be a string', function () {
32
+ const throwable = v => () => createMongodbUrl({protocol: v});
33
+ const error = v =>
34
+ format(
35
+ 'MongoDB option "protocol" must be ' + 'a String, but %s given.',
36
+ v,
37
+ );
38
+ expect(throwable(10)).to.throw(error('10'));
39
+ expect(throwable(true)).to.throw(error('true'));
40
+ expect(throwable([])).to.throw(error('Array'));
41
+ expect(throwable({})).to.throw(error('Object'));
42
+ throwable('mongodb')();
43
+ throwable('')();
44
+ throwable(0)();
45
+ throwable(false)();
46
+ throwable(undefined)();
47
+ throwable(null)();
48
+ });
49
+
50
+ it('requires the "hostname" option to be a string', function () {
51
+ const throwable = v => () => createMongodbUrl({hostname: v});
52
+ const error = v =>
53
+ format(
54
+ 'MongoDB option "hostname" must be ' + 'a String, but %s given.',
55
+ v,
56
+ );
57
+ expect(throwable(10)).to.throw(error('10'));
58
+ expect(throwable(true)).to.throw(error('true'));
59
+ expect(throwable([])).to.throw(error('Array'));
60
+ expect(throwable({})).to.throw(error('Object'));
61
+ throwable('127.0.0.1')();
62
+ throwable('')();
63
+ throwable(0)();
64
+ throwable(false)();
65
+ throwable(undefined)();
66
+ throwable(null)();
67
+ });
68
+
69
+ it('requires the "host" option to be a string', function () {
70
+ const throwable = v => () => createMongodbUrl({host: v});
71
+ const error = v =>
72
+ format('MongoDB option "host" must be ' + 'a String, but %s given.', v);
73
+ expect(throwable(10)).to.throw(error('10'));
74
+ expect(throwable(true)).to.throw(error('true'));
75
+ expect(throwable([])).to.throw(error('Array'));
76
+ expect(throwable({})).to.throw(error('Object'));
77
+ throwable('127.0.0.1')();
78
+ throwable('')();
79
+ throwable(0)();
80
+ throwable(false)();
81
+ throwable(undefined)();
82
+ throwable(null)();
83
+ });
84
+
85
+ it('requires the "port" option to be a number or a string', function () {
86
+ const throwable = v => () => createMongodbUrl({port: v});
87
+ const error = v =>
88
+ format(
89
+ 'MongoDB option "port" must be a Number ' +
90
+ 'or a String, but %s given.',
91
+ v,
92
+ );
93
+ expect(throwable(true)).to.throw(error('true'));
94
+ expect(throwable([])).to.throw(error('Array'));
95
+ expect(throwable({})).to.throw(error('Object'));
96
+ throwable('127.0.0.1')();
97
+ throwable('')();
98
+ throwable(10)();
99
+ throwable(0)();
100
+ throwable(false)();
101
+ throwable(undefined)();
102
+ throwable(null)();
103
+ });
104
+
105
+ it('requires the "database" option to be a string', function () {
106
+ const throwable = v => () => createMongodbUrl({database: v});
107
+ const error = v =>
108
+ format(
109
+ 'MongoDB option "database" must be ' + 'a String, but %s given.',
110
+ v,
111
+ );
112
+ expect(throwable(10)).to.throw(error('10'));
113
+ expect(throwable(true)).to.throw(error('true'));
114
+ expect(throwable([])).to.throw(error('Array'));
115
+ expect(throwable({})).to.throw(error('Object'));
116
+ throwable('database')();
117
+ throwable('')();
118
+ throwable(0)();
119
+ throwable(false)();
120
+ throwable(undefined)();
121
+ throwable(null)();
122
+ });
123
+
124
+ it('requires the "db" option to be a string', function () {
125
+ const throwable = v => () => createMongodbUrl({db: v});
126
+ const error = v =>
127
+ format('MongoDB option "db" must be ' + 'a String, but %s given.', v);
128
+ expect(throwable(10)).to.throw(error('10'));
129
+ expect(throwable(true)).to.throw(error('true'));
130
+ expect(throwable([])).to.throw(error('Array'));
131
+ expect(throwable({})).to.throw(error('Object'));
132
+ throwable('database')();
133
+ throwable('')();
134
+ throwable(0)();
135
+ throwable(false)();
136
+ throwable(undefined)();
137
+ throwable(null)();
138
+ });
139
+
140
+ it('requires the "username" option to be a string', function () {
141
+ const throwable = v => () => createMongodbUrl({username: v});
142
+ const error = v =>
143
+ format(
144
+ 'MongoDB option "username" must be ' + 'a String, but %s given.',
145
+ v,
146
+ );
147
+ expect(throwable(10)).to.throw(error('10'));
148
+ expect(throwable(true)).to.throw(error('true'));
149
+ expect(throwable([])).to.throw(error('Array'));
150
+ expect(throwable({})).to.throw(error('Object'));
151
+ throwable('username')();
152
+ throwable('')();
153
+ throwable(0)();
154
+ throwable(false)();
155
+ throwable(undefined)();
156
+ throwable(null)();
157
+ });
158
+
159
+ it('requires the "password" option to be a string or a number', function () {
160
+ const throwable = v => () => createMongodbUrl({password: v});
161
+ const error = v =>
162
+ format(
163
+ 'MongoDB option "password" must be a String ' +
164
+ 'or a Number, but %s given.',
165
+ v,
166
+ );
167
+ expect(throwable(true)).to.throw(error('true'));
168
+ expect(throwable([])).to.throw(error('Array'));
169
+ expect(throwable({})).to.throw(error('Object'));
170
+ throwable('password')();
171
+ throwable('')();
172
+ throwable(10)();
173
+ throwable(0)();
174
+ throwable(false)();
175
+ throwable(undefined)();
176
+ throwable(null)();
177
+ });
178
+
179
+ it('requires the "pass" option to be a string or a number', function () {
180
+ const throwable = v => () => createMongodbUrl({pass: v});
181
+ const error = v =>
182
+ format(
183
+ 'MongoDB option "pass" must be a String ' +
184
+ 'or a Number, but %s given.',
185
+ v,
186
+ );
187
+ expect(throwable(true)).to.throw(error('true'));
188
+ expect(throwable([])).to.throw(error('Array'));
189
+ expect(throwable({})).to.throw(error('Object'));
190
+ throwable('pass')();
191
+ throwable('')();
192
+ throwable(10)();
193
+ throwable(0)();
194
+ throwable(false)();
195
+ throwable(undefined)();
196
+ throwable(null)();
197
+ });
198
+
199
+ it('sets the given "protocol" option', function () {
200
+ const res = createMongodbUrl({protocol: 'value'});
201
+ expect(res).to.be.eq('value://127.0.0.1:27017/database');
202
+ });
203
+
204
+ it('sets the given "hostname" option', function () {
205
+ const res = createMongodbUrl({hostname: 'value'});
206
+ expect(res).to.be.eq('mongodb://value:27017/database');
207
+ });
208
+
209
+ it('sets the given "host" option', function () {
210
+ const res = createMongodbUrl({host: 'value'});
211
+ expect(res).to.be.eq('mongodb://value:27017/database');
212
+ });
213
+
214
+ it('sets the given "port" option as a number', function () {
215
+ const res = createMongodbUrl({port: 8080});
216
+ expect(res).to.be.eq('mongodb://127.0.0.1:8080/database');
217
+ });
218
+
219
+ it('sets the given "port" option as a string', function () {
220
+ const res = createMongodbUrl({port: '8080'});
221
+ expect(res).to.be.eq('mongodb://127.0.0.1:8080/database');
222
+ });
223
+
224
+ it('sets the given "database" option', function () {
225
+ const res = createMongodbUrl({database: 'value'});
226
+ expect(res).to.be.eq('mongodb://127.0.0.1:27017/value');
227
+ });
228
+
229
+ it('sets the given "db" option', function () {
230
+ const res = createMongodbUrl({db: 'value'});
231
+ expect(res).to.be.eq('mongodb://127.0.0.1:27017/value');
232
+ });
233
+
234
+ it('does not use the provided "username" option without a password', function () {
235
+ const res = createMongodbUrl({username: 'value'});
236
+ expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
237
+ });
238
+
239
+ it('does not use the provided "user" option without a password', function () {
240
+ const res = createMongodbUrl({username: 'value'});
241
+ expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
242
+ });
243
+
244
+ it('does not use the provided "password" option without a username', function () {
245
+ const res = createMongodbUrl({password: 'value'});
246
+ expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
247
+ });
248
+
249
+ it('does not use the provided "pass" option without a username', function () {
250
+ const res = createMongodbUrl({pass: 'value'});
251
+ expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
252
+ });
253
+
254
+ it('sets the given "username" and "password" option', function () {
255
+ const res = createMongodbUrl({username: 'usr', password: 'pwd'});
256
+ expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
257
+ });
258
+
259
+ it('sets the given "username" and "pass" option', function () {
260
+ const res = createMongodbUrl({username: 'usr', pass: 'pwd'});
261
+ expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
262
+ });
263
+
264
+ it('sets the given "user" and "password" option', function () {
265
+ const res = createMongodbUrl({user: 'usr', password: 'pwd'});
266
+ expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
267
+ });
268
+
269
+ it('sets the given "user" and "pass" option', function () {
270
+ const res = createMongodbUrl({user: 'usr', pass: 'pwd'});
271
+ expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
272
+ });
273
+
274
+ it('does not use the default "port" option for "mongodb+srv" protocol', function () {
275
+ const res = createMongodbUrl({protocol: 'mongodb+srv'});
276
+ expect(res).to.be.eq('mongodb+srv://127.0.0.1/database');
277
+ });
278
+
279
+ it('does not use the provided "port" option for "mongodb+srv" protocol', function () {
280
+ const res = createMongodbUrl({protocol: 'mongodb+srv', port: 8080});
281
+ expect(res).to.be.eq('mongodb+srv://127.0.0.1/database');
282
+ });
29
283
  });
@@ -1,3 +1,4 @@
1
+ export * from './is-iso-date.js';
1
2
  export * from './is-object-id.js';
2
3
  export * from './create-mongodb-url.js';
3
4
  export * from './transform-values-deep.js';
@@ -0,0 +1,31 @@
1
+ import {expect} from 'chai';
2
+ import {isIsoDate} from './is-iso-date.js';
3
+
4
+ describe('isIsoDate', function () {
5
+ it('returns false for an empty value', function () {
6
+ expect(isIsoDate('')).to.be.false;
7
+ expect(isIsoDate(0)).to.be.false;
8
+ expect(isIsoDate(false)).to.be.false;
9
+ expect(isIsoDate(undefined)).to.be.false;
10
+ expect(isIsoDate(null)).to.be.false;
11
+ });
12
+
13
+ it('returns false for invalid values', function () {
14
+ expect(isIsoDate(10)).to.be.false;
15
+ expect(isIsoDate([])).to.be.false;
16
+ expect(isIsoDate({})).to.be.false;
17
+ expect(isIsoDate(new Map())).to.be.false;
18
+ expect(isIsoDate(NaN)).to.be.false;
19
+ expect(isIsoDate(Infinity)).to.be.false;
20
+ });
21
+
22
+ it('returns true for the Date instance', function () {
23
+ expect(isIsoDate(new Date())).to.be.true;
24
+ });
25
+
26
+ it('validates ISO string', function () {
27
+ expect(isIsoDate('2011-10-05T14:48:00.000Z')).to.be.true;
28
+ expect(isIsoDate('2018-11-10T11:22:33+00:00')).to.be.false;
29
+ expect(isIsoDate('2011-10-05T14:99:00.000Z')).to.be.false;
30
+ });
31
+ });
@@ -1,9 +1,14 @@
1
1
  import {expect} from 'chai';
2
- import {isObjectId} from './is-object-id.js';
3
2
  import {ObjectId} from 'mongodb';
3
+ import {isObjectId} from './is-object-id.js';
4
4
 
5
5
  describe('isObjectId', function () {
6
6
  it('returns true for a valid ObjectId string or an instance', function () {
7
+ expect(isObjectId(new ObjectId())).to.be.true;
8
+ expect(isObjectId(String(new ObjectId()))).to.be.true;
9
+ });
10
+
11
+ it('returns false for invalid values', function () {
7
12
  expect(isObjectId('')).to.be.false;
8
13
  expect(isObjectId('123')).to.be.false;
9
14
  expect(isObjectId(0)).to.be.false;
@@ -17,8 +22,5 @@ describe('isObjectId', function () {
17
22
  expect(isObjectId(new Date())).to.be.false;
18
23
  expect(isObjectId(null)).to.be.false;
19
24
  expect(isObjectId(undefined)).to.be.false;
20
- //
21
- expect(isObjectId(new ObjectId())).to.be.true;
22
- expect(isObjectId(String(new ObjectId()))).to.be.true;
23
25
  });
24
26
  });
@@ -11,7 +11,7 @@ export function transformValuesDeep(value, transformer) {
11
11
  if (!transformer || typeof transformer !== 'function')
12
12
  throw new InvalidArgumentError(
13
13
  'The second argument of "transformValuesDeep" ' +
14
- 'must be a Function, but %s given.',
14
+ 'must be a Function, but %v given.',
15
15
  transformer,
16
16
  );
17
17
  if (Array.isArray(value)) {
@@ -1,8 +1,9 @@
1
1
  import {expect} from 'chai';
2
+ import {format} from '@e22m4u/js-format';
2
3
  import {transformValuesDeep} from './transform-values-deep.js';
3
4
 
4
5
  describe('transformValuesDeep', function () {
5
- it('transforms property values of an object', function () {
6
+ it('transforms property values of the given object', function () {
6
7
  const object = {
7
8
  foo: 1,
8
9
  bar: {
@@ -24,13 +25,13 @@ describe('transformValuesDeep', function () {
24
25
  });
25
26
  });
26
27
 
27
- it('transforms elements of an array', function () {
28
+ it('transforms elements of the given array', function () {
28
29
  const object = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
29
30
  const result = transformValuesDeep(object, String);
30
31
  expect(result).to.be.eql(['1', '2', '3', ['4', '5', '6', ['7', '8', '9']]]);
31
32
  });
32
33
 
33
- it('transforms non-pure objects', function () {
34
+ it('transforms the Date instance', function () {
34
35
  const date = new Date();
35
36
  const str = String(date);
36
37
  const result1 = transformValuesDeep(date, String);
@@ -44,4 +45,26 @@ describe('transformValuesDeep', function () {
44
45
  expect(result4).to.be.eql({date: str});
45
46
  expect(result5).to.be.eql({foo: {date: str}});
46
47
  });
48
+
49
+ it('requires the second argument to be a function', function () {
50
+ const throwable = v => () => transformValuesDeep('val', v);
51
+ const error = v =>
52
+ format(
53
+ 'The second argument of "transformValuesDeep" ' +
54
+ 'must be a Function, but %s given.',
55
+ v,
56
+ );
57
+ expect(throwable('str')).to.throw(error('"str"'));
58
+ expect(throwable('')).to.throw(error('""'));
59
+ expect(throwable(10)).to.throw(error('10'));
60
+ expect(throwable(0)).to.throw(error('0'));
61
+ expect(throwable(true)).to.throw(error('true'));
62
+ expect(throwable(false)).to.throw(error('false'));
63
+ expect(throwable([])).to.throw(error('Array'));
64
+ expect(throwable({})).to.throw(error('Object'));
65
+ expect(throwable(undefined)).to.throw(error('undefined'));
66
+ expect(throwable(null)).to.throw(error('null'));
67
+ throwable(() => undefined)();
68
+ throwable(function () {})();
69
+ });
47
70
  });