@itentialopensource/adapter-git 0.1.1

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.
@@ -0,0 +1,629 @@
1
+ /* @copyright Itential, LLC 2019 (pre-modifications) */
2
+
3
+ // Set globals
4
+ /* global describe it log pronghornProps */
5
+ /* eslint global-require:warn */
6
+ /* eslint no-unused-vars: warn */
7
+
8
+ // include required items for testing & logging
9
+ const assert = require('assert');
10
+ const fs = require('fs-extra');
11
+ const mocha = require('mocha');
12
+ const path = require('path');
13
+ const util = require('util');
14
+ const winston = require('winston');
15
+ const execute = require('child_process').execSync;
16
+ const { expect } = require('chai');
17
+ const { use } = require('chai');
18
+ const td = require('testdouble');
19
+
20
+ const anything = td.matchers.anything();
21
+
22
+ // stub and attemptTimeout are used throughout the code so set them here
23
+ let logLevel = 'none';
24
+ const stub = true;
25
+ const isRapidFail = false;
26
+ const attemptTimeout = 120000;
27
+
28
+ // these variables can be changed to run in integrated mode so easier to set them here
29
+ // always check these in with bogus data!!!
30
+ const username = 'username';
31
+ const password = 'password';
32
+
33
+ // these are the adapter properties. You generally should not need to alter
34
+ // any of these after they are initially set up
35
+ global.pronghornProps = {
36
+ pathProps: {
37
+ encrypted: false
38
+ },
39
+ adapterProps: {
40
+ adapters: [{
41
+ id: 'Test-Git',
42
+ type: 'Git',
43
+ stub: true,
44
+ properties: {
45
+ authentication: {
46
+ username: 'username',
47
+ password: 'password'
48
+ }
49
+ }
50
+ }]
51
+ }
52
+ };
53
+
54
+ global.$HOME = `${__dirname}/../..`;
55
+
56
+ // set the log levels that Pronghorn uses, spam and trace are not defaulted in so without
57
+ // this you may error on log.trace calls.
58
+ const myCustomLevels = {
59
+ levels: {
60
+ spam: 6,
61
+ trace: 5,
62
+ debug: 4,
63
+ info: 3,
64
+ warn: 2,
65
+ error: 1,
66
+ none: 0
67
+ }
68
+ };
69
+
70
+ // need to see if there is a log level passed in
71
+ process.argv.forEach((val) => {
72
+ // is there a log level defined to be passed in?
73
+ if (val.indexOf('--LOG') === 0) {
74
+ // get the desired log level
75
+ const inputVal = val.split('=')[1];
76
+
77
+ // validate the log level is supported, if so set it
78
+ if (Object.hasOwnProperty.call(myCustomLevels.levels, inputVal)) {
79
+ logLevel = inputVal;
80
+ }
81
+ }
82
+ });
83
+
84
+ // need to set global logging
85
+ global.log = winston.createLogger({
86
+ level: logLevel,
87
+ levels: myCustomLevels.levels,
88
+ transports: [
89
+ new winston.transports.Console()
90
+ ]
91
+ });
92
+
93
+ /**
94
+ * Runs the error asserts for the test
95
+ */
96
+ function runErrorAsserts(data, error, code, origin, displayStr) {
97
+ assert.equal(null, data);
98
+ assert.notEqual(undefined, error);
99
+ assert.notEqual(null, error);
100
+ assert.notEqual(undefined, error.IAPerror);
101
+ assert.notEqual(null, error.IAPerror);
102
+ assert.notEqual(undefined, error.IAPerror.displayString);
103
+ assert.notEqual(null, error.IAPerror.displayString);
104
+ assert.equal(code, error.icode);
105
+ assert.equal(origin, error.IAPerror.origin);
106
+ assert.equal(displayStr, error.IAPerror.displayString);
107
+ }
108
+
109
+ // require the adapter that we are going to be using
110
+ const Git = require('../../adapter');
111
+
112
+ // delete the .DS_Store directory in entities -- otherwise this will cause errors
113
+ const dirPath = path.join(__dirname, '../../entities/.DS_Store');
114
+ if (fs.existsSync(dirPath)) {
115
+ try {
116
+ fs.removeSync(dirPath);
117
+ console.log('.DS_Store deleted');
118
+ } catch (e) {
119
+ console.log('Error when deleting .DS_Store:', e);
120
+ }
121
+ }
122
+
123
+ // begin the testing - these should be pretty well defined between the describe and the it!
124
+ describe('[unit] Git Adapter Test', () => {
125
+ describe('Git Class Tests', () => {
126
+ const a = new Git(
127
+ pronghornProps.adapterProps.adapters[0].id,
128
+ pronghornProps.adapterProps.adapters[0].properties
129
+ );
130
+
131
+ if (isRapidFail) {
132
+ const state = {};
133
+ state.passed = true;
134
+
135
+ mocha.afterEach(function x() {
136
+ state.passed = state.passed
137
+ && (this.currentTest.state === 'passed');
138
+ });
139
+ mocha.beforeEach(function x() {
140
+ if (!state.passed) {
141
+ return this.currentTest.skip();
142
+ }
143
+ return true;
144
+ });
145
+ }
146
+
147
+ describe('#class instance created', () => {
148
+ it('should be a class with properties', (done) => {
149
+ assert.notEqual(null, a);
150
+ assert.notEqual(undefined, a);
151
+ const check = global.pronghornProps.adapterProps.adapters[0].id;
152
+ assert.equal(check, a.id);
153
+ done();
154
+ }).timeout(attemptTimeout);
155
+ });
156
+
157
+ let wffunctions = [];
158
+ describe('#getWorkflowFunctions', () => {
159
+ it('should retrieve workflow functions', (done) => {
160
+ wffunctions = a.getWorkflowFunctions();
161
+ assert.notEqual(0, wffunctions.length);
162
+ done();
163
+ }).timeout(attemptTimeout);
164
+ });
165
+
166
+ describe('package.json', () => {
167
+ it('should have a package.json', (done) => {
168
+ fs.exists('package.json', (val) => {
169
+ assert.equal(true, val);
170
+ done();
171
+ });
172
+ });
173
+ it('package.json should be validated', (done) => {
174
+ const packageDotJson = require('../../package.json');
175
+ const { PJV } = require('package-json-validator');
176
+ const options = {
177
+ warnings: true, // show warnings
178
+ recommendations: true // show recommendations
179
+ };
180
+ const results = PJV.validate(JSON.stringify(packageDotJson), 'npm', options);
181
+
182
+ if (results.valid === false) {
183
+ log.error('The package.json contains the following errors: ');
184
+ log.error(util.inspect(results));
185
+ assert.equal(true, results.valid);
186
+ } else {
187
+ assert.equal(true, results.valid);
188
+ }
189
+
190
+ done();
191
+ });
192
+ it('package.json should be customized', (done) => {
193
+ const packageDotJson = require('../../package.json');
194
+ assert.notEqual(-1, packageDotJson.name.indexOf('git'));
195
+ assert.notEqual(undefined, packageDotJson.version);
196
+ assert.notEqual(null, packageDotJson.version);
197
+ assert.notEqual('', packageDotJson.version);
198
+ done();
199
+ });
200
+ });
201
+
202
+ describe('pronghorn.json', () => {
203
+ it('should have a pronghorn.json', (done) => {
204
+ fs.exists('pronghorn.json', (val) => {
205
+ assert.equal(true, val);
206
+ done();
207
+ });
208
+ });
209
+ it('pronghorn.json should be customized', (done) => {
210
+ const pronghornDotJson = require('../../pronghorn.json');
211
+ assert.notEqual(-1, pronghornDotJson.id.indexOf('git'));
212
+ assert.equal('Git', pronghornDotJson.export);
213
+ assert.equal('Git', pronghornDotJson.displayName);
214
+ assert.equal('Git', pronghornDotJson.title);
215
+ done();
216
+ });
217
+ it('pronghorn.json should only expose workflow functions', (done) => {
218
+ const pronghornDotJson = require('../../pronghorn.json');
219
+ for (let m = 0; m < pronghornDotJson.methods.length; m += 1) {
220
+ let found = false;
221
+ let paramissue = false;
222
+
223
+ for (let w = 0; w < wffunctions.length; w += 1) {
224
+ if (pronghornDotJson.methods[m].name === wffunctions[w]) {
225
+ found = true;
226
+ const methLine = execute(`grep "${wffunctions[w]}(" adapter.js | grep "callback) {"`).toString();
227
+ let wfparams = [];
228
+
229
+ if (methLine.indexOf('(') >= 0 && methLine.indexOf(')') >= 0) {
230
+ const temp = methLine.substring(methLine.indexOf('(') + 1, methLine.indexOf(')'));
231
+ wfparams = temp.split(',');
232
+
233
+ for (let t = 0; t < wfparams.length; t += 1) {
234
+ // remove default value from the parameter name
235
+ wfparams[t] = wfparams[t].substring(0, wfparams[t].search(/=/) > 0 ? wfparams[t].search(/#|\?|=/) : wfparams[t].length);
236
+ // remove spaces
237
+ wfparams[t] = wfparams[t].trim();
238
+
239
+ if (wfparams[t] === 'callback') {
240
+ wfparams.splice(t, 1);
241
+ }
242
+ }
243
+ }
244
+
245
+ // if there are inputs defined but not on the method line
246
+ if (wfparams.length === 0 && (pronghornDotJson.methods[m].input
247
+ && pronghornDotJson.methods[m].input.length > 0)) {
248
+ paramissue = true;
249
+ } else if (wfparams.length > 0 && (!pronghornDotJson.methods[m].input
250
+ || pronghornDotJson.methods[m].input.length === 0)) {
251
+ // if there are no inputs defined but there are on the method line
252
+ paramissue = true;
253
+ } else {
254
+ for (let p = 0; p < pronghornDotJson.methods[m].input.length; p += 1) {
255
+ let pfound = false;
256
+ for (let wfp = 0; wfp < wfparams.length; wfp += 1) {
257
+ if (pronghornDotJson.methods[m].input[p].name.toUpperCase() === wfparams[wfp].toUpperCase()) {
258
+ pfound = true;
259
+ }
260
+ }
261
+
262
+ if (!pfound) {
263
+ paramissue = true;
264
+ }
265
+ }
266
+ for (let wfp = 0; wfp < wfparams.length; wfp += 1) {
267
+ let pfound = false;
268
+ for (let p = 0; p < pronghornDotJson.methods[m].input.length; p += 1) {
269
+ if (pronghornDotJson.methods[m].input[p].name.toUpperCase() === wfparams[wfp].toUpperCase()) {
270
+ pfound = true;
271
+ }
272
+ }
273
+
274
+ if (!pfound) {
275
+ paramissue = true;
276
+ }
277
+ }
278
+ }
279
+
280
+ break;
281
+ }
282
+ }
283
+
284
+ if (!found) {
285
+ // this is the reason to go through both loops - log which ones are not found so
286
+ // they can be worked
287
+ log.error(`${pronghornDotJson.methods[m].name} not found in workflow functions`);
288
+ }
289
+ if (paramissue) {
290
+ // this is the reason to go through both loops - log which ones are not found so
291
+ // they can be worked
292
+ log.error(`${pronghornDotJson.methods[m].name} has a parameter mismatch`);
293
+ }
294
+ assert.equal(true, found);
295
+ assert.equal(false, paramissue);
296
+ }
297
+ done();
298
+ }).timeout(attemptTimeout);
299
+ it('pronghorn.json should expose all workflow functions', (done) => {
300
+ const pronghornDotJson = require('../../pronghorn.json');
301
+ for (let w = 0; w < wffunctions.length; w += 1) {
302
+ let found = false;
303
+
304
+ for (let m = 0; m < pronghornDotJson.methods.length; m += 1) {
305
+ if (pronghornDotJson.methods[m].name === wffunctions[w]) {
306
+ found = true;
307
+ break;
308
+ }
309
+ }
310
+
311
+ if (!found) {
312
+ // this is the reason to go through both loops - log which ones are not found so
313
+ // they can be worked
314
+ log.error(`${wffunctions[w]} not found in pronghorn.json`);
315
+ }
316
+ assert.equal(true, found);
317
+ }
318
+ done();
319
+ });
320
+ });
321
+
322
+ describe('propertiesSchema.json', () => {
323
+ it('should have a propertiesSchema.json', (done) => {
324
+ fs.exists('propertiesSchema.json', (val) => {
325
+ assert.equal(true, val);
326
+ done();
327
+ });
328
+ });
329
+ it('propertiesSchema.json should be customized', (done) => {
330
+ const propertiesDotJson = require('../../propertiesSchema.json');
331
+ assert.equal('adapter-git', propertiesDotJson.$id);
332
+ done();
333
+ });
334
+ });
335
+
336
+ describe('error.json', () => {
337
+ it('should have an error.json', (done) => {
338
+ fs.exists('error.json', (val) => {
339
+ assert.equal(true, val);
340
+ done();
341
+ });
342
+ });
343
+ });
344
+
345
+ describe('README.md', () => {
346
+ it('should have a README', (done) => {
347
+ fs.exists('README.md', (val) => {
348
+ assert.equal(true, val);
349
+ done();
350
+ });
351
+ });
352
+ it('README.md should be customized', (done) => {
353
+ fs.readFile('README.md', 'utf8', (err, data) => {
354
+ assert.equal(-1, data.indexOf('[System]'));
355
+ assert.equal(-1, data.indexOf('[system]'));
356
+ assert.equal(-1, data.indexOf('[version]'));
357
+ assert.equal(-1, data.indexOf('[namespace]'));
358
+ done();
359
+ });
360
+ });
361
+ });
362
+
363
+ describe('#connect', () => {
364
+ it('should have a connect function', (done) => {
365
+ assert.equal(true, typeof a.connect === 'function');
366
+ done();
367
+ });
368
+ });
369
+
370
+ describe('#healthCheck', () => {
371
+ it('should have a healthCheck function', (done) => {
372
+ assert.equal(true, typeof a.healthCheck === 'function');
373
+ done();
374
+ });
375
+ });
376
+
377
+ /*
378
+ -----------------------------------------------------------------------
379
+ -----------------------------------------------------------------------
380
+ *** All code above this comment will be replaced during a migration ***
381
+ ******************* DO NOT REMOVE THIS COMMENT BLOCK ******************
382
+ -----------------------------------------------------------------------
383
+ -----------------------------------------------------------------------
384
+ */
385
+
386
+ describe('#cloneRepo - errors', () => {
387
+ it('should have a cloneRepo function', (done) => {
388
+ assert.equal(true, typeof a.cloneRepo === 'function');
389
+ done();
390
+ });
391
+ it('should error on cloneRepo - no options', (done) => {
392
+ try {
393
+ a.cloneRepo(null, (data, error) => {
394
+ try {
395
+ const displayE = 'options is required';
396
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-cloneRepo', displayE);
397
+ done();
398
+ } catch (ex) {
399
+ log.error(`Test Failure: ${ex}`);
400
+ done(ex);
401
+ }
402
+ });
403
+ } catch (exc) {
404
+ log.error(`Adapter Exception: ${exc}`);
405
+ done(exc);
406
+ }
407
+ }).timeout(attemptTimeout);
408
+ });
409
+
410
+ describe('#removeFile - errors', () => {
411
+ it('should have a removeFile function', (done) => {
412
+ assert.equal(true, typeof a.removeFile === 'function');
413
+ done();
414
+ });
415
+ it('should error on removeFile - no options', (done) => {
416
+ try {
417
+ a.removeFile(null, (data, error) => {
418
+ try {
419
+ const displayE = 'options is required';
420
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeFile', displayE);
421
+ done();
422
+ } catch (ex) {
423
+ log.error(`Test Failure: ${ex}`);
424
+ done(ex);
425
+ }
426
+ });
427
+ } catch (exc) {
428
+ log.error(`Adapter Exception: ${exc}`);
429
+ done(exc);
430
+ }
431
+ }).timeout(attemptTimeout);
432
+ });
433
+
434
+ describe('#addFile - errors', () => {
435
+ it('should have a addFile function', (done) => {
436
+ assert.equal(true, typeof a.addFile === 'function');
437
+ done();
438
+ });
439
+ it('should error on addFile - no options', (done) => {
440
+ try {
441
+ a.addFile(null, (data, error) => {
442
+ try {
443
+ const displayE = 'options is required';
444
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-addFile', displayE);
445
+ done();
446
+ } catch (ex) {
447
+ log.error(`Test Failure: ${ex}`);
448
+ done(ex);
449
+ }
450
+ });
451
+ } catch (exc) {
452
+ log.error(`Adapter Exception: ${exc}`);
453
+ done(exc);
454
+ }
455
+ }).timeout(attemptTimeout);
456
+ });
457
+
458
+ describe('#getStatus - errors', () => {
459
+ it('should have a getStatus function', (done) => {
460
+ assert.equal(true, typeof a.getStatus === 'function');
461
+ done();
462
+ });
463
+ it('should error on getStatus - no options', (done) => {
464
+ try {
465
+ a.getStatus(null, (data, error) => {
466
+ try {
467
+ const displayE = 'options is required';
468
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-getStatus', displayE);
469
+ done();
470
+ } catch (ex) {
471
+ log.error(`Test Failure: ${ex}`);
472
+ done(ex);
473
+ }
474
+ });
475
+ } catch (exc) {
476
+ log.error(`Adapter Exception: ${exc}`);
477
+ done(exc);
478
+ }
479
+ }).timeout(attemptTimeout);
480
+ });
481
+
482
+ describe('#getStatusMatrix - errors', () => {
483
+ it('should have a getStatusMatrix function', (done) => {
484
+ assert.equal(true, typeof a.getStatusMatrix === 'function');
485
+ done();
486
+ });
487
+ it('should error on getStatusMatrix - no options', (done) => {
488
+ try {
489
+ a.getStatusMatrix(null, (data, error) => {
490
+ try {
491
+ const displayE = 'options is required';
492
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-getStatusMatrix', displayE);
493
+ done();
494
+ } catch (ex) {
495
+ log.error(`Test Failure: ${ex}`);
496
+ done(ex);
497
+ }
498
+ });
499
+ } catch (exc) {
500
+ log.error(`Adapter Exception: ${exc}`);
501
+ done(exc);
502
+ }
503
+ }).timeout(attemptTimeout);
504
+ });
505
+
506
+ describe('#createCommit - errors', () => {
507
+ it('should have a createCommit function', (done) => {
508
+ assert.equal(true, typeof a.createCommit === 'function');
509
+ done();
510
+ });
511
+ it('should error on createCommit - no options', (done) => {
512
+ try {
513
+ a.createCommit(null, (data, error) => {
514
+ try {
515
+ const displayE = 'options is required';
516
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-createCommit', displayE);
517
+ done();
518
+ } catch (ex) {
519
+ log.error(`Test Failure: ${ex}`);
520
+ done(ex);
521
+ }
522
+ });
523
+ } catch (exc) {
524
+ log.error(`Adapter Exception: ${exc}`);
525
+ done(exc);
526
+ }
527
+ }).timeout(attemptTimeout);
528
+ });
529
+
530
+ describe('#pushChanges - errors', () => {
531
+ it('should have a pushChanges function', (done) => {
532
+ assert.equal(true, typeof a.pushChanges === 'function');
533
+ done();
534
+ });
535
+ it('should error on pushChanges - no options', (done) => {
536
+ try {
537
+ a.pushChanges(null, (data, error) => {
538
+ try {
539
+ const displayE = 'options is required';
540
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-pushChanges', displayE);
541
+ done();
542
+ } catch (ex) {
543
+ log.error(`Test Failure: ${ex}`);
544
+ done(ex);
545
+ }
546
+ });
547
+ } catch (exc) {
548
+ log.error(`Adapter Exception: ${exc}`);
549
+ done(exc);
550
+ }
551
+ }).timeout(attemptTimeout);
552
+ });
553
+
554
+ describe('#removeAndPushFile - errors', () => {
555
+ it('should have a removeAndPushFile function', (done) => {
556
+ assert.equal(true, typeof a.removeAndPushFile === 'function');
557
+ done();
558
+ });
559
+ it('should error on removeAndPushFile - no cloneOptions', (done) => {
560
+ try {
561
+ a.removeAndPushFile(null, null, null, null, (data, error) => {
562
+ try {
563
+ const displayE = 'clone options is required';
564
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
565
+ done();
566
+ } catch (ex) {
567
+ log.error(`Test Failure: ${ex}`);
568
+ done(ex);
569
+ }
570
+ });
571
+ } catch (exc) {
572
+ log.error(`Adapter Exception: ${exc}`);
573
+ done(exc);
574
+ }
575
+ }).timeout(attemptTimeout);
576
+ it('should error on removeAndPushFile - no removeFileOptions', (done) => {
577
+ try {
578
+ a.removeAndPushFile({}, null, null, null, (data, error) => {
579
+ try {
580
+ const displayE = 'remove file options is required';
581
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
582
+ done();
583
+ } catch (ex) {
584
+ log.error(`Test Failure: ${ex}`);
585
+ done(ex);
586
+ }
587
+ });
588
+ } catch (exc) {
589
+ log.error(`Adapter Exception: ${exc}`);
590
+ done(exc);
591
+ }
592
+ }).timeout(attemptTimeout);
593
+ it('should error on removeAndPushFile - no commitOptions', (done) => {
594
+ try {
595
+ a.removeAndPushFile({}, {}, null, null, (data, error) => {
596
+ try {
597
+ const displayE = 'commit options is required';
598
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
599
+ done();
600
+ } catch (ex) {
601
+ log.error(`Test Failure: ${ex}`);
602
+ done(ex);
603
+ }
604
+ });
605
+ } catch (exc) {
606
+ log.error(`Adapter Exception: ${exc}`);
607
+ done(exc);
608
+ }
609
+ }).timeout(attemptTimeout);
610
+ it('should error on removeAndPushFile - no pushOptions', (done) => {
611
+ try {
612
+ a.removeAndPushFile({}, {}, {}, null, (data, error) => {
613
+ try {
614
+ const displayE = 'push options is required';
615
+ runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
616
+ done();
617
+ } catch (ex) {
618
+ log.error(`Test Failure: ${ex}`);
619
+ done(ex);
620
+ }
621
+ });
622
+ } catch (exc) {
623
+ log.error(`Adapter Exception: ${exc}`);
624
+ done(exc);
625
+ }
626
+ }).timeout(attemptTimeout);
627
+ });
628
+ });
629
+ });