UNPKG

electrode-ota-server

Version:

electrode-ota-server === This project is part of the [electrode-ota](https://github.com/electrode-io/electrode-ota-server) project. This is a default electrode-ota-server

531 lines (476 loc) 22.7 kB
import uuid from 'uuid'; import path from 'path'; import AccountManager from 'code-push/script/management-sdk'; import {expect} from 'chai'; import {AcquisitionManager, AcquisitionStatus} from 'code-push/script/acquisition-sdk'; const dir = path.join.bind(path, __dirname); const {env} = process; //EVIL - But we need snoopage env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; const join = path.join.bind(path, __dirname); const server = process.env.MS_CONFIG ? require('electrode-ota-server-test-support/lib/server-ms').default : require('electrode-ota-server-test-support/lib/server-init').default; const verbs = 'GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH'.toLowerCase().split(/,\s*/); const uploadFile = dir("./fixtures/upload.zip"); const uploadFile2 = dir("./fixtures/upload2.zip"); const uploadFile3 = dir("./fixtures/upload3.zip"); const step1 = join('./fixtures/step.1.blob.zip'); const step2 = join('./fixtures/step.2.blob.zip'); const step3 = join('./fixtures/step.3.blob.zip'); const clientUniqueId = uuid().toUpperCase(); const clientUniqueId2 = uuid().toUpperCase(); const expected = (eql = null) => (result) => expect(result).to.eql(eql); const contains = (eql = null) => (result) => { expect(result).to.contains(eql); return result }; const P = (r, e) => new Promise(r, e); const hasKeys = (...keys) => (obj) => { for (const key of keys) { expect(key in obj).to.be.true; } }; const last = (val) => val && val.length ? val[val.length - 1] : val; const delay = (timeout) => (...args) => new Promise(resolve => setTimeout(resolve, timeout, ...args)); describe('managment-sdk', function () { this.timeout(50000); let am; let agent; let updateServerUrl; let collaborator; let extraCollaborator; let aquistionServerUrl; let stop; let accessKey; before(() => server().then((setup) => { updateServerUrl = setup.serverUrl; aquistionServerUrl = setup.aquistionServerUrl || ''; accessKey = setup.accessKey; collaborator = setup.collaborator; extraCollaborator = setup.extraCollaborator; agent = setup.agent; am = new AccountManager(setup.accessKey, {}, setup.serverUrl, setup.proxy); stop = setup.stop; })); after(() => { return stop && stop(); }); const aq = (req) => { const configuration = Object.assign({ serverUrl: aquistionServerUrl, headers: { 'content-type': 'application/json' } }, req); return new AcquisitionManager({ request(type, url, body, cb){ if (!cb) cb = body; const req = agent[verbs[type]](url).set(configuration.headers); req.send(body).end(function (err, response) { if (response && typeof response.body === 'object') { response.body = JSON.stringify(response.body); } cb(err, response); }); } }, configuration); }; describe('isAuthenticated', () => { it('should reply', () => { return am.isAuthenticated().then(expected(true)) }); }); describe('access-key', () => { let akey; beforeEach(() => { akey = `access-key-${Date.now()}`; }); it('should add key', () => am.addAccessKey(akey, 3000) .then(({name}) => expect(name).to.eql(akey)) .then(_ => am.removeAccessKey(akey))); it('should patch key', () => { const nkey = `new-key-${Date.now()}`; return am.addAccessKey(akey).then(_ => am.patchAccessKey(akey, nkey)).then(({name}) => { expect(name).to.eql(nkey) akey = nkey; }); }); it('should remove key', () => am.addAccessKey(akey).then(_ => am.removeAccessKey(akey)).then(expected())); it('should list key', () => am.addAccessKey(akey) .then(_ => am.getAccessKeys()) .then((r) => { //noinspection BadExpressionStatementJS expect(r.length > 1).to.be.true; //noinspection BadExpressionStatementJS expect(r.find(f => f.name === akey)).to.exist; })); }); describe('app', () => { let name; beforeEach(function () { name = `${this.currentTest.title.replace(/\s+?/g, '_')}-${Date.now()}`; }); it('should add app', () => am.addApp(name).then(expected({name}))); it('should remove app', () => am.addApp(name).then(_ => am.removeApp(name)).then(expected())); it('should list app', () => am.addApp(name).then(_ => am.getApps()) .then(apps => apps.find(v => v.name == name)) .then(expected({ "collaborators": { [collaborator]: { "isCurrentAccount": true, "permission": "Owner" } }, "deployments": [ "Production", "Staging" ], name }))); it('should rename app', () => am.addApp(name).then(_ => am.renameApp(name, `rename-to-${Date.now()}`)).then(expected())); it('should transfer app', () => am.addApp(name).then(_ => am.transferApp(name, extraCollaborator)).then(_ => am.getApp(name)) .then(expected({ name, "collaborators": { [collaborator]: { "permission": "Collaborator", "isCurrentAccount": true }, [extraCollaborator]: { "permission": "Owner" } }, "deployments": ["Production", "Staging"] }))); }); describe('collaborator', () => { let name; beforeEach(function () { name = `${this.currentTest.title.replace(/\s+?/, '_')}-${Date.now()}`; }); it('should add collaborator', () => am.addApp(name) .then(_ => am.addCollaborator(name, extraCollaborator)) .then(_ => am.getApp(name)) .then(expected({ name, "collaborators": { [collaborator]: {"permission": "Owner", "isCurrentAccount": true}, [extraCollaborator]: {"permission": "Collaborator"} }, "deployments": ["Production", "Staging"] }))); it('should remove collaborator', () => am.addApp(name) .then(_ => am.addCollaborator(name, extraCollaborator)) .then(_ => am.removeCollaborator(name, extraCollaborator)) .then(_ => am.getApp(name)) .then(expected({ name, "collaborators": { [collaborator]: {"permission": "Owner", "isCurrentAccount": true} }, "deployments": ["Production", "Staging"] }))); it('should list collaborator', () => am.addApp(name) .then(_ => am.addCollaborator(name, extraCollaborator)) .then(_ => am.getCollaborators(name)) .then(expected({ [collaborator]: {"permission": "Owner", "isCurrentAccount": true}, [extraCollaborator]: {"permission": "Collaborator"} }))); }); describe('deployment', function () { it('should add deployment', () => { const name = `deployment-add-${Date.now()}`; return am.addApp(name) .then(_ => am.addDeployment(name, 'NewDeployment')) .then(_ => am.getApp(name)) .then(res => { expect(res.deployments.indexOf('NewDeployment') > -1).to.be.true; delete res.deployments; return res; }) .then(expected({ "collaborators": { [collaborator]: { "isCurrentAccount": true, "permission": "Owner" } }, name })); }); it('should remove deployment', () => { const name = `deployment-add-${Date.now()}`; return am.addApp(name) .then(_ => am.removeDeployment(name, 'Production')) .then(_ => am.getApp(name)) .then(expected({ "collaborators": { [collaborator]: { "isCurrentAccount": true, "permission": "Owner" } }, "deployments": [ "Staging" ], name })); }); it('should list deployment', () => { const name = `deployment-add-${Date.now()}`; return am.addApp(name) .then(_ => am.getDeployments(name)) .then(deployments => { expect(deployments[0].name).to.eql("Production"); expect(deployments[1].name).to.eql("Staging"); }) }); it('should add history to deployment', () => { const name = `deployment-add-${Date.now()}`; //noinspection CommaExpressionJS return am.addApp(name) .then(_ => am.getDeploymentHistory(name, 'Staging')) .then(expected([])) .then(_ => am.release(name, 'Staging', uploadFile, '1.0.0', {})) //this will trigger history .then(_ => am.release(name, 'Staging', uploadFile2, '1.0.0', {})) .then(_ => am.release(name, 'Staging', uploadFile3, '1.0.0', {})) .then(_ => am.getDeploymentHistory(name, 'Staging')) .then(history => history.map(v => (delete v.uploadTime, v))) .then(history => { expect(history.length).to.eql(3); }); /* .then(expected([ { "appVersion": "1.0.0", "blobUrl": "https://codepush.blob.core.windows.net/storagev2/UlC4pBQj6R3HnxWmNYSXWCMZ8XRvEyi8xQMDZ", "label": "v1", "manifestBlobUrl": "https://codepush.blob.core.windows.net/storagev2/Cbdc-lF4zS5B9frrVwGwfTsNLTx_Eyi8xQMDZ", "packageHash": "7d4a08789ff7b201dad44b7b782a1c9f06a24be30e3184536233fa1e26454764", "releaseMethod": "Upload", "releasedBy": "speajus2@gmail.com", "rollout": null, "size": 203902 }, { "appVersion": "1.0.0", "blobUrl": "https://codepush.blob.core.windows.net/storagev2/7ElqkWuKjIbUsBbLGWBNr9BTtznOEyi8xQMDZ", "diffPackageMap": { "7d4a08789ff7b201dad44b7b782a1c9f06a24be30e3184536233fa1e26454764": { "size": 174769, "url": "https://codepush.blob.core.windows.net/storagev2/xtGQVU7ngcG6PQR5avPPDPsVy49KEyi8xQMDZ" } }, "label": "v2", "manifestBlobUrl": "https://codepush.blob.core.windows.net/storagev2/C8YOMC1T4AzGin45j12c60OPRSi7Eyi8xQMDZ", "packageHash": "463b2ed68f2e79b5874aa8fb9eaf9a17e24bbfe03f5fab95c3c3775b1f3fc568", "releaseMethod": "Upload", "releasedBy": "speajus2@gmail.com", "size": 203904 } ])); */ }); it('should clear deployment history', () => { const name = `deployment-clear-${Date.now()}`; return am.addApp(name) .then(_ => am.release(name, 'Staging', uploadFile, '1.0', {})) .then(_ => am.release(name, 'Staging', uploadFile2, '1.0', {})) .then(_ => am.clearDeploymentHistory(name, 'Staging')) .then(_ => am.getDeploymentHistory(name, 'Staging')) .then(expected([])); }); it('should rename deployment', () => { const name = `deployment-clear-${Date.now()}`; return am.addApp(name) .then(_ => am.renameDeployment(name, 'Staging', 'Whatever')) .then(_ => { return am.getDeployment(name, 'Whatever') }) .then(deployment => { expect(deployment).to.exist expect(deployment.name).to.eql('Whatever'); }); }); it('should get deployment metrics', () => { const name = `deployment-metrics-${Date.now()}`; return am.addApp(name) .then(_ => am.getDeploymentMetrics(name, 'Staging')) .then(_ => am.getDeployment(name, 'Staging')) .then(deployment => P((resolve, reject) => { return aq({ clientUniqueId, appVersion: '1.0.0', deploymentKey: deployment.key, headers: { 'Accept': 'application/text', 'content-type': 'application/json' } }).queryUpdateWithCurrentPackage({appVersion: '1.0.0'}, (e, o) => e ? reject(e) : resolve(o)) })) .then((obj) => { expect(obj).to.eql(null); }) .then(_ => am.release(name, 'Staging', uploadFile, '1.0.0', {})) .then(_ => am.getDeployment(name, 'Staging')) .then(deployment => P((resolve, reject) => { return aq({ clientUniqueId, appVersion: '1.0.0', deploymentKey: deployment.key }).queryUpdateWithCurrentPackage({appVersion: '1.0.0'}, (e, o) => e ? reject(e) : resolve(o)) }).then(contains({ "deploymentKey": deployment.key, "label": "v1", "appVersion": "1.0.0", "packageSize": 203902 })).then(hasKeys('downloadUrl', 'packageHash'))) .then(_ => am.getDeployment(name, 'Staging')) .then(deployment => P((resolve, reject) => aq({ clientUniqueId, appVersion: '1.0.0', deploymentKey: deployment.key }).reportStatusDownload({label: 'v1'}, (e, o) => e ? reject(e) : resolve(o))) .then(_ => am.getDeploymentMetrics(name, 'Staging')) .then(expected({ "v1": { "active": 0, "downloaded": 1, "installed": 0, "failed": 0 } })) .then(_ => P((resolve, reject) => aq({ clientUniqueId, appVersion: '1.0.0', deploymentKey: deployment.key }).reportStatusDeploy(deployment.package, AcquisitionStatus.DeploymentSucceeded, '1.0.0', deployment.key, (e, o) => e ? reject(e) : resolve(o) ))) .then(_ => am.getDeploymentMetrics(name, 'Staging')) .then(r => { expect(r["v1"]).to.eql({ "active": 1, "downloaded": 1, "failed": 0, "installed": 1 }); }) ); }); describe('patch', () => { const name = `patch-${Date.now()}`; it('should patch label', () => am.addApp(name) .then(_ => am.release(name, 'Staging', uploadFile, '1.0', {rollout: 25})) .then(_ => am.patchRelease(name, 'Staging', 'v1', { appVersion: '2.0.0', description: 'description', isDisabled: true, isMandatory: true, rollout: 50 })) .then(expected())); it('should fail patching rollout lower value', () => { }); }); describe('promote', () => { const name = `promote-${Date.now()}`; it('should promote', () => am.addApp(name) .then(_ => am.release(name, 'Staging', uploadFile, '1.0.0', {})) .then(_ => am.promote(name, 'Staging', 'Production', {rollout: 50})) .then(expected()) .then(_ => am.getDeployment(name, 'Production')) .then((resp) => { expect(resp).to.have.property('name', 'Production'); expect(resp).to.have.property('package').contain({ originalDeployment: 'Staging', rollout: 50 }); })); }); describe('release', () => { const name = `release-${Date.now()}`; it('should release', () => am.addApp(name) .then(_ => am.release(name, 'Production', step1, '1.2.3', { isDisabled: true, description: 'super', isMandatory: true })) .then(_ => am.getDeployment(name, 'Production').then((deployment) => { return am.getDeploymentHistory(name, 'Production') .then(last) .then(resp => { const {appVersion, manifestBlobUrl, label, isDisabled, packageHash, isMandatory, description} = resp; expect(appVersion).to.eql('1.2.3'); expect(isDisabled).to.be.true; expect(isMandatory).to.be.true; expect(!manifestBlobUrl).to.be.false; expect(description).to.eql('super'); return am.release(name, 'Production', step2, '1.2.3', { isDisabled: false, description: 'super', isMandatory: true }) //Delay makes it work, apparently the MS Server takes a few seconds to do its magic. .then(delay(2000)) .then((npackage) => P((resolve, reject) => { //The MS Server does diffPackageMap lazily, so we have to call and do a check. //for it for it to return with the magic. const aaq = aq({ clientUniqueId, deploymentKey: deployment.key, headers: { 'Accept': 'application/text', 'content-type': 'application/json' } }); return aaq.queryUpdateWithCurrentPackage({ appVersion: '1.2.3', label, packageHash }, (e, o) => e ? reject(e) : resolve(o)) }).then(shouldUpdate => { expect(shouldUpdate.appVersion, 'appVersion').to.eql('1.2.3'); expect(shouldUpdate.deploymentKey, 'deploymentKey').to.eql(deployment.key); expect(shouldUpdate.isMandatory, 'isMandatory').to.eql(true); return am.getDeploymentHistory(name, 'Production') .then(resp => { const {appVersion, manifestBlobUrl, diffPackageMap, isDisabled, isMandatory, description} = resp[0]; expect(appVersion, 'appVersion').to.eql('1.2.3'); expect(isDisabled, 'isDisabled').to.be.false; expect(isMandatory, 'isMandatory').to.be.true; expect(!manifestBlobUrl, 'manifestBlobUrl').to.be.false; expect(description, 'description').to.eql('super'); expect(diffPackageMap[packageHash].url, `diffPackageMap[${shouldUpdate.packageHash}].url`).to.eql(shouldUpdate.downloadUrl); }); }) ); }) }) )); }); describe('rollback', () => { const name = `rollback-${Date.now()}`; it('should rollback', () => am.addApp(name) .then(_ => am.release(name, 'Production', uploadFile, '1.0', {})) .then(_ => am.release(name, 'Production', uploadFile2, '1.0', {})) .then(_ => am.rollback(name, 'Production', 'v1')) .then(_ => am.getDeployment(name, 'Production')) .then(r => expect(r.package.label).to.eql('v3'))); }); describe('whoami', () => { it('should say who i am', () => am.getAccountInfo().then(v => (delete v.name, v)).then(expected({ "email": collaborator, "linkedProviders": [ "GitHub" ] }))); }); describe('session', () => { it('should return session', () => am.getSessions() .then(({length}) => expect(length).to.eql(1))); }); }) });