Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add node version model #766

Merged
merged 8 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

133 changes: 133 additions & 0 deletions src/model/node/NodeVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright 2021 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export class NodeVersion {
/**
* Create a NodeVersion from a given raw version number.
* @param {number} rawNodeVersion - Node version in number format.
* ex: 655367
* @returns {NodeVersion}
*/
public static createFromRawNodeVersion(rawNodeVersion: number): NodeVersion {
if (!NodeVersion.isValidRawNodeVersion(rawNodeVersion)) {
throw new Error(`Invalid node version number '${rawNodeVersion}'`);
}

return new NodeVersion(rawNodeVersion);
}

/**
* Create a NodeVersion from a given formatted version string.
* @param {string} formattedNodeVersion - Node version in string format.
* ex: 0.10.0.7
* @returns {NodeVersion}
*/
public static createFromFormattedNodeVersion(formattedNodeVersion: string): NodeVersion {
if (!NodeVersion.isValidFormattedNodeVersion(formattedNodeVersion)) {
throw new Error(`Invalid node version string '${formattedNodeVersion}'`);
}

const placeholderHex = '00';
const hexVersionNumber = formattedNodeVersion
.split('.')
.map((value) => (placeholderHex + parseInt(value).toString(16)).slice(-2))
.join('');

const rawVersionNumber = parseInt(hexVersionNumber, 16);
return new NodeVersion(rawVersionNumber);
}

/**
* Determines the validity of a raw node version number.
* @param {string} rawNodeVersion The raw node version number. Expected format 655367
* @returns {boolean} true if the raw node version number is valid, false otherwise.
*/
public static isValidRawNodeVersion = (rawNodeVersion: number): boolean => {
const maxRawNodeVersion = 4294967295;
const minRawNodeVersion = 0;

return Number.isInteger(rawNodeVersion) && rawNodeVersion >= minRawNodeVersion && rawNodeVersion <= maxRawNodeVersion;
};

/**
* Determines the validity of a formatted node version string.
* @param {string} formattedNodeVersion The formatted node version string. Expected format: 0.10.0.7
* @returns {boolean} true if the formatted node version string is valid, false otherwise.
*/
public static isValidFormattedNodeVersion = (formattedNodeVersion: string): boolean => {
const maxFormattedNodeVersionChunkValue = 255;
const minFormattedNodeVersionChunkValue = 0;

const versionChuncks = formattedNodeVersion.split('.').map((value) => parseInt(value));

if (versionChuncks.length !== 4) {
return false;
}

const isVersionChuncksValid = !versionChuncks.find(
(value) => isNaN(value) || value < minFormattedNodeVersionChunkValue || value > maxFormattedNodeVersionChunkValue,
);

return isVersionChuncksValid;
};

/**
* @internal
* @param nodeVersion
*/
private constructor(
/**
* The raw node version value.
*/
private readonly nodeVersion: number,
) {}

/**
* Get node version in formatted format ex: 0.10.0.7
* @returns {string}
*/
public formatted(): string {
const placeholderHex = '00000000';
const hexNodeVersion = (placeholderHex + this.nodeVersion.toString(16)).slice(-8);
const formattedNodeVersion = hexNodeVersion
.match(/.{1,2}/g)!
.map((hex) => parseInt(hex, 16))
.join('.');

return formattedNodeVersion;
}

/**
* Get node version in the raw numeric format ex: 655367.
* @returns {number}
*/
public raw(): number {
return this.nodeVersion;
}

/**
* Compares node versions for equality
* @param nodeVersion - Node version to compare
* @returns {boolean}
*/
public equals(nodeVersion: any): boolean {
if (nodeVersion instanceof NodeVersion) {
return this.nodeVersion === nodeVersion.raw();
}

return false;
}
}
1 change: 1 addition & 0 deletions src/model/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
export * from './NodeHealth';
export * from './NodeInfo';
export * from './NodeTime';
export * from './NodeVersion';
export * from './RoleType';
export * from './ServerInfo';
149 changes: 149 additions & 0 deletions test/model/node/NodeVersion.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright 2021 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { expect } from 'chai';
import { NodeVersion } from '../../../src/model/node/NodeVersion';

describe('NodeVersion', () => {
const validRawVersion1 = 4294967295;
const validFormattedVersion1 = '255.255.255.255';
const validRawVersion2 = 0;
const validFormattedVersion2 = '0.0.0.0';
const validRawVersion3 = 655367;
const validFormattedVersion3 = '0.10.0.7';

const invalidFormattedVersion1 = '0.0.0.0.0';
const invalidFormattedVersion2 = '-1.0.0.0';
const invalidFormattedVersion3 = '0.0.0.256';
const invalidFormattedVersion4 = 'some text';
const invalidFormattedVersion5 = '';

const invalidRawVersion1 = -1231;
const invalidRawVersion2 = 42949672955;
const invalidRawVersion3 = 23.34;
const invalidRawVersion4 = Infinity;

it('createComplete a NodeVersion by given raw version', () => {
const nodeVersion = NodeVersion.createFromRawNodeVersion(validRawVersion1);
expect(nodeVersion.raw()).to.be.equal(validRawVersion1);
});

it('createComplete a NodeVersion by given raw version', () => {
const nodeVersion = NodeVersion.createFromRawNodeVersion(validRawVersion2);
expect(nodeVersion.raw()).to.be.equal(validRawVersion2);
});

it('createComplete a NodeVersion by given raw version', () => {
const nodeVersion = NodeVersion.createFromRawNodeVersion(validRawVersion3);
expect(nodeVersion.raw()).to.be.equal(validRawVersion3);
});

it('createComplete a NodeVersion by given formatted version', () => {
const nodeVersion = NodeVersion.createFromFormattedNodeVersion(validFormattedVersion1);
expect(nodeVersion.raw()).to.be.equal(validRawVersion1);
});

it('createComplete a NodeVersion by given formatted version', () => {
const nodeVersion = NodeVersion.createFromFormattedNodeVersion(validFormattedVersion2);
expect(nodeVersion.raw()).to.be.equal(validRawVersion2);
});

it('createComplete a NodeVersion by given formatted version', () => {
const nodeVersion = NodeVersion.createFromFormattedNodeVersion(validFormattedVersion3);
expect(nodeVersion.raw()).to.be.equal(validRawVersion3);
});

it('print formatted node version', () => {
const nodeVersion = NodeVersion.createFromRawNodeVersion(validRawVersion1);
expect(nodeVersion.formatted()).to.be.equal(validFormattedVersion1);
});

it('print formatted node version', () => {
const nodeVersion = NodeVersion.createFromRawNodeVersion(validRawVersion2);
expect(nodeVersion.formatted()).to.be.equal(validFormattedVersion2);
});

it('print formatted node version', () => {
const nodeVersion = NodeVersion.createFromRawNodeVersion(validRawVersion3);
expect(nodeVersion.formatted()).to.be.equal(validFormattedVersion3);
});

it('should throw Error when negative raw version provided', () => {
expect(() => {
NodeVersion.createFromRawNodeVersion(invalidRawVersion1);
}).to.throw(`Invalid node version number '${invalidRawVersion1}'`);
});

it('should throw Error when too large raw version provided', () => {
expect(() => {
NodeVersion.createFromRawNodeVersion(invalidRawVersion2);
}).to.throw(`Invalid node version number '${invalidRawVersion2}'`);
});

it('should throw Error when float number raw version provided', () => {
expect(() => {
NodeVersion.createFromRawNodeVersion(invalidRawVersion3);
}).to.throw(`Invalid node version number '${invalidRawVersion3}'`);
});

it('should throw Error when Infinity number raw version provided', () => {
expect(() => {
NodeVersion.createFromRawNodeVersion(invalidRawVersion4);
}).to.throw(`Invalid node version number '${invalidRawVersion4}'`);
});

it('should throw Error when invalid formatted version provided', () => {
expect(() => {
NodeVersion.createFromFormattedNodeVersion(invalidFormattedVersion1);
}).to.throw(`Invalid node version string '${invalidFormattedVersion1}'`);
});

it('should throw Error when invalid formatted version with one negative provided', () => {
expect(() => {
NodeVersion.createFromFormattedNodeVersion(invalidFormattedVersion2);
}).to.throw(`Invalid node version string '${invalidFormattedVersion2}'`);
});

it('should throw Error when invalid formatted version with one large provided', () => {
expect(() => {
NodeVersion.createFromFormattedNodeVersion(invalidFormattedVersion3);
}).to.throw(`Invalid node version string '${invalidFormattedVersion3}'`);
});

it('should throw Error when invalid text string provided', () => {
expect(() => {
NodeVersion.createFromFormattedNodeVersion(invalidFormattedVersion4);
}).to.throw(`Invalid node version string '${invalidFormattedVersion4}'`);
});

it('should throw Error when empty string provided', () => {
expect(() => {
NodeVersion.createFromFormattedNodeVersion(invalidFormattedVersion5);
}).to.throw(`Invalid node version string '${invalidFormattedVersion5}'`);
});

it('should equal versions', () => {
const version = NodeVersion.createFromRawNodeVersion(validRawVersion1);
const compareVersion = NodeVersion.createFromRawNodeVersion(validRawVersion1);
expect(version.equals(compareVersion)).to.be.equal(true);
});

it('should not equal versions', () => {
const version = NodeVersion.createFromRawNodeVersion(validRawVersion1);
const compareVersion = NodeVersion.createFromRawNodeVersion(validRawVersion2);
expect(version.equals(compareVersion)).to.be.equal(false);
});
});