@itentialopensource/adapter-git
Version:
Itential adapter to run git commands
654 lines (606 loc) • 22.4 kB
JavaScript
/* @copyright Itential, LLC 2019 (pre-modifications) */
// Set globals
/* global describe it log pronghornProps */
/* eslint global-require:warn */
/* eslint no-unused-vars: warn */
// include required items for testing & logging
const assert = require('assert');
const fs = require('fs-extra');
const mocha = require('mocha');
const path = require('path');
const util = require('util');
const winston = require('winston');
const execute = require('child_process').execSync;
const { expect } = require('chai');
const { use } = require('chai');
const td = require('testdouble');
const Ajv = require('ajv');
const log = require('../../utils/logger');
const ajv = new Ajv({ strictSchema: false, allErrors: true, allowUnionTypes: true });
const anything = td.matchers.anything();
// stub and attemptTimeout are used throughout the code so set them here
const stub = true;
const isRapidFail = false;
const attemptTimeout = 120000;
// these variables can be changed to run in integrated mode so easier to set them here
// always check these in with bogus data!!!
const username = 'username';
const password = 'password';
// these are the adapter properties. You generally should not need to alter
// any of these after they are initially set up
global.pronghornProps = {
pathProps: {
encrypted: false
},
adapterProps: {
adapters: [{
id: 'Test-Git',
type: 'Git',
stub: true,
properties: {
authentication: {
username: 'username',
password: 'password'
}
}
}]
}
};
global.$HOME = `${__dirname}/../..`;
/**
* Runs the error asserts for the test
*/
function runErrorAsserts(data, error, code, origin, displayStr) {
assert.equal(null, data);
assert.notEqual(undefined, error);
assert.notEqual(null, error);
assert.notEqual(undefined, error.IAPerror);
assert.notEqual(null, error.IAPerror);
assert.notEqual(undefined, error.IAPerror.displayString);
assert.notEqual(null, error.IAPerror.displayString);
assert.equal(code, error.icode);
assert.equal(origin, error.IAPerror.origin);
assert.equal(displayStr, error.IAPerror.displayString);
}
// require the adapter that we are going to be using
const Git = require('../../adapter');
// delete the .DS_Store directory in entities -- otherwise this will cause errors
const dirPath = path.join(__dirname, '../../entities/.DS_Store');
if (fs.existsSync(dirPath)) {
try {
fs.removeSync(dirPath);
console.log('.DS_Store deleted');
} catch (e) {
console.log('Error when deleting .DS_Store:', e);
}
}
// begin the testing - these should be pretty well defined between the describe and the it!
describe('[unit] Git Adapter Test', () => {
describe('Git Class Tests', () => {
const a = new Git(
pronghornProps.adapterProps.adapters[0].id,
pronghornProps.adapterProps.adapters[0].properties
);
if (isRapidFail) {
const state = {};
state.passed = true;
mocha.afterEach(function x() {
state.passed = state.passed
&& (this.currentTest.state === 'passed');
});
mocha.beforeEach(function x() {
if (!state.passed) {
return this.currentTest.skip();
}
return true;
});
}
describe('#class instance created', () => {
it('should be a class with properties', (done) => {
assert.notEqual(null, a);
assert.notEqual(undefined, a);
const check = global.pronghornProps.adapterProps.adapters[0].id;
assert.equal(check, a.id);
done();
}).timeout(attemptTimeout);
});
let wffunctions = [];
describe('#getWorkflowFunctions', () => {
it('should retrieve workflow functions', (done) => {
wffunctions = a.getWorkflowFunctions();
assert.notEqual(0, wffunctions.length);
done();
}).timeout(attemptTimeout);
});
describe('package.json', () => {
it('should have a package.json', (done) => {
fs.exists('package.json', (val) => {
assert.equal(true, val);
done();
});
});
it('package.json should be validated', (done) => {
try {
const packageDotJson = require('../../package.json');
// Define the JSON schema for package.json
const packageJsonSchema = {
type: 'object',
properties: {
name: { type: 'string' },
version: { type: 'string' }
// May need to add more properties as needed
},
required: ['name', 'version']
};
const validate = ajv.compile(packageJsonSchema);
const isValid = validate(packageDotJson);
if (isValid === false) {
log.error('The package.json contains errors');
assert.equal(true, isValid);
} else {
assert.equal(true, isValid);
}
done();
} catch (error) {
log.error(`Test Failure: ${error}`);
done(error);
}
});
it('package.json should be customized', (done) => {
const packageDotJson = require('../../package.json');
assert.notEqual(-1, packageDotJson.name.indexOf('git'));
assert.notEqual(undefined, packageDotJson.version);
assert.notEqual(null, packageDotJson.version);
assert.notEqual('', packageDotJson.version);
done();
});
});
describe('pronghorn.json', () => {
it('should have a pronghorn.json', (done) => {
fs.exists('pronghorn.json', (val) => {
assert.equal(true, val);
done();
});
});
it('pronghorn.json should be customized', (done) => {
const pronghornDotJson = require('../../pronghorn.json');
assert.notEqual(-1, pronghornDotJson.id.indexOf('git'));
assert.equal('Git', pronghornDotJson.export);
assert.equal('Git', pronghornDotJson.displayName);
assert.equal('Git', pronghornDotJson.title);
done();
});
it('pronghorn.json should only expose workflow functions', (done) => {
const pronghornDotJson = require('../../pronghorn.json');
for (let m = 0; m < pronghornDotJson.methods.length; m += 1) {
let found = false;
let paramissue = false;
for (let w = 0; w < wffunctions.length; w += 1) {
if (pronghornDotJson.methods[m].name === wffunctions[w]) {
found = true;
const methLine = execute(`grep "${wffunctions[w]}(" adapter.js | grep "callback) {"`).toString();
let wfparams = [];
if (methLine.indexOf('(') >= 0 && methLine.indexOf(')') >= 0) {
const temp = methLine.substring(methLine.indexOf('(') + 1, methLine.indexOf(')'));
wfparams = temp.split(',');
for (let t = 0; t < wfparams.length; t += 1) {
// remove default value from the parameter name
wfparams[t] = wfparams[t].substring(0, wfparams[t].search(/=/) > 0 ? wfparams[t].search(/#|\?|=/) : wfparams[t].length);
// remove spaces
wfparams[t] = wfparams[t].trim();
if (wfparams[t] === 'callback') {
wfparams.splice(t, 1);
}
}
}
// if there are inputs defined but not on the method line
if (wfparams.length === 0 && (pronghornDotJson.methods[m].input
&& pronghornDotJson.methods[m].input.length > 0)) {
paramissue = true;
} else if (wfparams.length > 0 && (!pronghornDotJson.methods[m].input
|| pronghornDotJson.methods[m].input.length === 0)) {
// if there are no inputs defined but there are on the method line
paramissue = true;
} else {
for (let p = 0; p < pronghornDotJson.methods[m].input.length; p += 1) {
let pfound = false;
for (let wfp = 0; wfp < wfparams.length; wfp += 1) {
if (pronghornDotJson.methods[m].input[p].name.toUpperCase() === wfparams[wfp].toUpperCase()) {
pfound = true;
}
}
if (!pfound) {
paramissue = true;
}
}
for (let wfp = 0; wfp < wfparams.length; wfp += 1) {
let pfound = false;
for (let p = 0; p < pronghornDotJson.methods[m].input.length; p += 1) {
if (pronghornDotJson.methods[m].input[p].name.toUpperCase() === wfparams[wfp].toUpperCase()) {
pfound = true;
}
}
if (!pfound) {
paramissue = true;
}
}
}
break;
}
}
if (!found) {
// this is the reason to go through both loops - log which ones are not found so
// they can be worked
log.error(`${pronghornDotJson.methods[m].name} not found in workflow functions`);
}
if (paramissue) {
// this is the reason to go through both loops - log which ones are not found so
// they can be worked
log.error(`${pronghornDotJson.methods[m].name} has a parameter mismatch`);
}
assert.equal(true, found);
assert.equal(false, paramissue);
}
done();
}).timeout(attemptTimeout);
it('pronghorn.json should expose all workflow functions', (done) => {
const pronghornDotJson = require('../../pronghorn.json');
for (let w = 0; w < wffunctions.length; w += 1) {
let found = false;
for (let m = 0; m < pronghornDotJson.methods.length; m += 1) {
if (pronghornDotJson.methods[m].name === wffunctions[w]) {
found = true;
break;
}
}
if (!found) {
// this is the reason to go through both loops - log which ones are not found so
// they can be worked
log.error(`${wffunctions[w]} not found in pronghorn.json`);
}
assert.equal(true, found);
}
done();
});
});
describe('propertiesSchema.json', () => {
it('should have a propertiesSchema.json', (done) => {
fs.exists('propertiesSchema.json', (val) => {
assert.equal(true, val);
done();
});
});
it('propertiesSchema.json should be customized', (done) => {
const propertiesDotJson = require('../../propertiesSchema.json');
assert.equal('adapter-git', propertiesDotJson.$id);
done();
});
});
describe('error.json', () => {
it('should have an error.json', (done) => {
fs.exists('error.json', (val) => {
assert.equal(true, val);
done();
});
});
});
describe('README.md', () => {
it('should have a README', (done) => {
fs.exists('README.md', (val) => {
assert.equal(true, val);
done();
});
});
it('README.md should be customized', (done) => {
fs.readFile('README.md', 'utf8', (err, data) => {
assert.equal(-1, data.indexOf('[System]'));
assert.equal(-1, data.indexOf('[system]'));
assert.equal(-1, data.indexOf('[version]'));
assert.equal(-1, data.indexOf('[namespace]'));
done();
});
});
});
describe('#connect', () => {
it('should have a connect function', (done) => {
assert.equal(true, typeof a.connect === 'function');
done();
});
});
describe('#healthCheck', () => {
it('should have a healthCheck function', (done) => {
assert.equal(true, typeof a.healthCheck === 'function');
done();
});
});
/*
-----------------------------------------------------------------------
-----------------------------------------------------------------------
*** All code above this comment will be replaced during a migration ***
******************* DO NOT REMOVE THIS COMMENT BLOCK ******************
-----------------------------------------------------------------------
-----------------------------------------------------------------------
*/
describe('#cloneRepo - errors', () => {
it('should have a cloneRepo function', (done) => {
assert.equal(true, typeof a.cloneRepo === 'function');
done();
});
it('should error on cloneRepo - no options', (done) => {
try {
a.cloneRepo(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-cloneRepo', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#removeFile - errors', () => {
it('should have a removeFile function', (done) => {
assert.equal(true, typeof a.removeFile === 'function');
done();
});
it('should error on removeFile - no options', (done) => {
try {
a.removeFile(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeFile', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#addFile - errors', () => {
it('should have a addFile function', (done) => {
assert.equal(true, typeof a.addFile === 'function');
done();
});
it('should error on addFile - no options', (done) => {
try {
a.addFile(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-addFile', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#getStatus - errors', () => {
it('should have a getStatus function', (done) => {
assert.equal(true, typeof a.getStatus === 'function');
done();
});
it('should error on getStatus - no options', (done) => {
try {
a.getStatus(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-getStatus', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#getStatusMatrix - errors', () => {
it('should have a getStatusMatrix function', (done) => {
assert.equal(true, typeof a.getStatusMatrix === 'function');
done();
});
it('should error on getStatusMatrix - no options', (done) => {
try {
a.getStatusMatrix(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-getStatusMatrix', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#createCommit - errors', () => {
it('should have a createCommit function', (done) => {
assert.equal(true, typeof a.createCommit === 'function');
done();
});
it('should error on createCommit - no options', (done) => {
try {
a.createCommit(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-createCommit', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#pushChanges - errors', () => {
it('should have a pushChanges function', (done) => {
assert.equal(true, typeof a.pushChanges === 'function');
done();
});
it('should error on pushChanges - no options', (done) => {
try {
a.pushChanges(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-pushChanges', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#createBranch - errors', () => {
it('should have a createBranch function', (done) => {
assert.equal(true, typeof a.createBranch === 'function');
done();
});
it('should error on createBranch - no options', (done) => {
try {
a.createBranch(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-createBranch', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#checkoutBranch - errors', () => {
it('should have a checkoutBranch function', (done) => {
assert.equal(true, typeof a.checkoutBranch === 'function');
done();
});
it('should error on checkoutBranch - no options', (done) => {
try {
a.checkoutBranch(null, (data, error) => {
try {
const displayE = 'options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-checkoutBranch', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
describe('#removeAndPushFile - errors', () => {
it('should have a removeAndPushFile function', (done) => {
assert.equal(true, typeof a.removeAndPushFile === 'function');
done();
});
it('should error on removeAndPushFile - no cloneOptions', (done) => {
try {
a.removeAndPushFile(null, null, null, null, null, null, null, null, (data, error) => {
try {
const displayE = 'clone options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
it('should error on removeAndPushFile - no removeFileOptions', (done) => {
try {
a.removeAndPushFile({}, null, null, null, null, null, null, null, (data, error) => {
try {
const displayE = 'remove file options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
it('should error on removeAndPushFile - no commitOptions', (done) => {
try {
a.removeAndPushFile({}, null, null, null, null, {}, null, null, (data, error) => {
try {
const displayE = 'commit options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
it('should error on removeAndPushFile - no pushOptions', (done) => {
try {
a.removeAndPushFile({}, null, null, null, null, {}, {}, null, (data, error) => {
try {
const displayE = 'push options is required';
runErrorAsserts(data, error, 'AD.300', 'Test-Git-adapter-removeAndPushFile', displayE);
done();
} catch (ex) {
log.error(`Test Failure: ${ex}`);
done(ex);
}
});
} catch (exc) {
log.error(`Adapter Exception: ${exc}`);
done(exc);
}
}).timeout(attemptTimeout);
});
});
});