@stordata/vsphere-soapify
Version:
A NodeJS abstraction layer for the vSphere SOAP API
763 lines (634 loc) • 21.5 kB
JavaScript
;
const { expect } = require('chai'),
Parser = require('./parser'),
Client = require('./client'),
sdk = require('./sdk');
describe('The Parser class', function() {
let parser,
client;
beforeEach(function() {
client = new Client('https://url/sdk');
parser = new Parser(client);
});
describe('The parse method', function() {
describe('xsd:string', function() {
it('should default to parsing a string', function() {
expect(parser.parse('abcd')).to.equal('abcd');
});
it('should parse a string', function() {
expect(parser.parse('abcd', 'xsd:string')).to.equal('abcd');
});
it('should parse undefined as undefined', function() {
expect(parser.parse(undefined, 'xsd:string')).to.equal(undefined);
});
});
describe('xsd:boolean', function() {
it('should parse "true" as true for "xsd:boolean" type', function() {
expect(parser.parse('true', 'xsd:boolean')).to.equal(true);
});
it('should parse true as true for "xsd:boolean" type', function() {
expect(parser.parse(true, 'xsd:boolean')).to.equal(true);
});
it('should parse false as false for "xsd:boolean" type', function() {
expect(parser.parse(false, 'xsd:boolean')).to.equal(false);
});
it('should parse "false" as false for "xsd:boolean" type', function() {
expect(parser.parse('false', 'xsd:boolean')).to.equal(false);
});
it('should parse a random string as true for "xsd:boolean" type', function() {
expect(parser.parse('notABoolean', 'xsd:boolean')).to.equal(false);
});
it('should parse "" as false for "xsd:boolean" type', function() {
expect(parser.parse('', 'xsd:boolean')).to.equal(false);
});
it('should parse undefined as an undefined', function() {
expect(parser.parse(undefined, 'xsd:boolean')).to.equal(undefined);
});
});
describe('xsd:byte, xsd:short, xsd:int, xsd:long', function() {
['byte', 'short', 'int', 'long'].forEach((type) => {
['123', '-45', '67.89'].forEach((value) => {
it(`should parse "${value}" as ${value} for "xsd:${type}" type`, function() {
expect(parser.parse(value, `xsd:${type}`)).to.equal(Number(value));
});
it(`should parse ${value} as ${value} for "xsd:${type}" type`, function() {
expect(parser.parse(Number(value), `xsd:${type}`)).to.equal(Number(value));
});
});
it(`should parse an invalid number as NaN for "xsd:${type}" type`, function() {
expect(parser.parse('notANumber', `xsd:${type}`)).to.deep.equal(NaN);
});
it('should parse undefined as undefined', function() {
expect(parser.parse(undefined, `xsd:${type}`)).to.equal(undefined);
});
});
});
describe('xsd:dateTime', function() {
it('should parse a valid XML date and time as a Date for "xsd:dateTime" type', function() {
expect(parser.parse('2019-01-01T12:34:45.678Z', 'xsd:dateTime')).to.equalTime(new Date(2019, 0, 1, 12, 34, 45, 678));
});
it('should parse a Date as a Date for "xsd:dateTime" type', function() {
expect(parser.parse(new Date(2019, 0, 1, 12, 34, 45, 678), 'xsd:dateTime')).to.equalTime(new Date(2019, 0, 1, 12, 34, 45, 678));
});
it('should parse a valid XML date as a Date for "xsd:dateTime" type', function() {
expect(parser.parse('2019-01-01', 'xsd:dateTime')).to.equalTime(new Date(2019, 0, 1));
});
it('should parse undefined as undefined', function() {
expect(parser.parse(undefined, 'xsd:dateTime')).to.equal(undefined);
});
});
describe('ArrayOfString', function() {
it('should parse an array of strings', function() {
expect(parser.parse({ string: ['str1', 'str2'] }, 'ArrayOfString')).to.deep.equal(['str1', 'str2']);
});
it('should parse a JS array too', function() {
expect(parser.parse(['str1', 'str2'], 'ArrayOfString')).to.deep.equal(['str1', 'str2']);
});
});
describe('ArrayOfBoolean', function() {
it('should parse an array of booleans', function() {
expect(parser.parse({ boolean: ['true', 'false'] }, 'ArrayOfBoolean')).to.deep.equal([true, false]);
});
it('should parse a JS array too', function() {
expect(parser.parse(['true', 'false'], 'ArrayOfBoolean')).to.deep.equal([true, false]);
});
});
['Short', 'Int', 'Long'].forEach((type) => {
describe(`ArrayOf${type}`, function() {
it(`should parse an array of ${type} values`, function() {
expect(parser.parse({ [type.toLowerCase()]: ['123', '12.34', '-42'] }, `ArrayOf${type}`)).to.deep.equal([123, 12.34, -42]);
});
});
});
describe('ArrayOfKeyAnyValue', function() {
it('should parse an array of KeyAnyValue to an object, parsing values according to their type', function() {
expect(parser.parse([{
key: 'a',
value: {
attributes: {
'xsi:type': 'xsd:int'
},
$value: '1'
}
}, {
key: 'b',
value: {
attributes: {
'xsi:type': 'xsd:string'
},
$value: 'str1'
}
}, {
key: 'c',
value: {
attributes: {
'xsi:type': 'xsd:boolean'
},
$value: 'true'
}
}], 'ArrayOfKeyAnyValue')).to.deep.equal({
a: 1,
b: 'str1',
c: true
});
});
it('should parse a raw XML object too', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfKeyAnyValue'
},
KeyAnyValue: [{
key: 'a',
value: {
attributes: {
'xsi:type': 'xsd:int'
},
$value: '1'
}
}, {
key: 'b',
value: {
attributes: {
'xsi:type': 'xsd:string'
},
$value: 'str1'
}
}, {
key: 'c',
value: {
attributes: {
'xsi:type': 'xsd:boolean'
},
$value: 'true'
}
}]
}, 'ArrayOfKeyAnyValue')).to.deep.equal({
a: 1,
b: 'str1',
c: true
});
});
});
describe('ArrayOfOptionValue', function() {
it('should parse an array of OptionValue to an object, parsing values according to their type', function() {
expect(parser.parse([{
key: 'a',
value: {
attributes: {
'xsi:type': 'xsd:int'
},
$value: '1'
}
}, {
key: 'b',
value: {
attributes: {
'xsi:type': 'xsd:string'
},
$value: 'str1'
}
}, {
key: 'c',
value: {
attributes: {
'xsi:type': 'xsd:boolean'
},
$value: 'true'
}
}], 'ArrayOfOptionValue')).to.deep.equal({
a: 1,
b: 'str1',
c: true
});
});
it('should parse a raw XML object too', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfOptionValue'
},
OptionValue: [{
key: 'a',
value: {
attributes: {
'xsi:type': 'xsd:int'
},
$value: '1'
}
}, {
key: 'b',
value: {
attributes: {
'xsi:type': 'xsd:string'
},
$value: 'str1'
}
}, {
key: 'c',
value: {
attributes: {
'xsi:type': 'xsd:boolean'
},
$value: 'true'
}
}]
}, 'ArrayOfOptionValue')).to.deep.equal({
a: 1,
b: 'str1',
c: true
});
});
});
describe('ArrayOfObjectContent', function() {
it('should parse an array of managed objets', function() {
const parsed = parser.parse([{
obj: {
attributes: {
type: 'HostSystem'
},
$value: 'myId'
},
propSet: [{
name: 'name',
val: {
attributes: {
'xsi:type': 'xsd:string'
},
$value: 'myName'
}
}]
}], 'ArrayOfObjectContent');
expect(parsed).to.have.length(1);
expect(parsed[0]).to.be.a.instanceOf(sdk.managed.HostSystem);
expect(parsed[0]).to.deep.equal({
name: 'myName',
ref: 'myId'
});
});
});
describe('ManagedObjectReference', function() {
it('should parse a managed objet', function() {
const parsed = parser.parse({
attributes: {
type: 'HostSystem'
},
$value: 'myId'
}, 'ManagedObjectReference');
expect(parsed).to.be.a.instanceOf(sdk.managed.HostSystem);
expect(parsed).to.deep.equal({
ref: 'myId'
});
});
it('should parse a ManagedEntity when object is unknown', function() {
const parsed = parser.parse({
attributes: {
type: 'UnknownCustomManagedObject'
},
$value: 'myId'
}, 'ManagedObjectReference');
expect(parsed.constructor.name).to.equal(sdk.managed.ManagedEntity.name);
expect(parsed).to.deep.equal({
ref: 'myId'
});
});
it('should parse a ManagedEntity when no type is defined', function() {
const parsed = parser.parse({
$value: 'myId'
}, 'ManagedObjectReference');
expect(parsed.constructor.name).to.equal(sdk.managed.ManagedEntity.name);
expect(parsed).to.deep.equal({
ref: 'myId'
});
});
});
describe('ArrayOfManagedObjectReference', function() {
it('should parse an array of ManagedObjectReference', function() {
const parsed = parser.parse({
'xsi:type': 'ArrayOfManagedObjectReference',
ManagedObjectReference: [{
attributes: {
type: 'HostSystem'
},
$value: 'myId'
}, {
attributes: {
type: 'Datastore'
},
$value: 'myId_2'
}]
}, 'ArrayOfManagedObjectReference');
expect(parsed).to.have.length(2);
expect(parsed[0]).to.be.a.instanceOf(sdk.managed.HostSystem);
expect(parsed[0]).to.deep.equal({
ref: 'myId'
});
expect(parsed[1]).to.be.a.instanceOf(sdk.managed.Datastore);
expect(parsed[1]).to.deep.equal({
ref: 'myId_2'
});
});
it('should support an empty array', function() {
const parsed = parser.parse({
'xsi:type': 'ArrayOfManagedObjectReference',
ManagedObjectReference: []
}, 'ArrayOfManagedObjectReference');
expect(parsed).to.have.length(0);
});
it('should support a single object (i.e.: not an array)', function() {
const parsed = parser.parse({
attributes: {
'xsi:type': 'ArrayOfManagedObjectReference'
},
ManagedObjectReference: {
attributes: {
type: 'Datastore'
},
$value: '/tmp/govcsim-DC0-LocalDS_0-813051446@folder-5'
}
}, 'ArrayOfManagedObjectReference');
expect(parsed).to.deep.equal([{
ref: '/tmp/govcsim-DC0-LocalDS_0-813051446@folder-5'
}]);
});
});
it('should return an empty array when ArrayOf value does not contain the values', function() {
expect(parser.parse({}, 'ArrayOfUnknownCustomType')).to.deep.equal([]);
});
it('should return undefined when ArrayOf value is undefined', function() {
expect(parser.parse(undefined, 'ArrayOfUnknownCustomType')).to.deep.equal(undefined);
});
describe('ComplexType', function() {
it('should return a complex type instance', function() {
const parsed = parser.parse({ key: 'myAlarm1', acknowledged: true }, 'AlarmState');
expect(parsed).to.be.an.instanceof(sdk.data.AlarmState);
expect(parsed).to.deep.equal({
acknowledged: true,
key: 'myAlarm1'
});
});
it('should support passing the actual type directly', function() {
const parsed = parser.parse({ key: 'myAlarm1', acknowledged: true }, sdk.data.AlarmState);
expect(parsed).to.be.an.instanceof(sdk.data.AlarmState);
expect(parsed).to.deep.equal({
acknowledged: true,
key: 'myAlarm1'
});
});
});
describe('ArrayOfComplexType', function() {
it('should return an array of complex type instances', function() {
expect(parser.parse([{
key: 'myAlarm1',
acknowledged: true
}, {
key: 'myAlarm2',
acknowledged: true
}], 'ArrayOfAlarmState')).to.deep.equal([
{
acknowledged: true,
key: 'myAlarm1'
},
{
acknowledged: true,
key: 'myAlarm2'
}
]);
});
});
describe('With raw XML objects', function() {
describe('xsd:string', function() {
it('should parse a xsd:string object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:string'
},
$value: '1'
})).to.equal('1');
});
it('should parse a xsd:string object when string is empty', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:string'
},
$value: ''
})).to.equal('');
});
});
it('should parse a xsd:boolean object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:boolean'
},
$value: 'true'
})).to.equal(true);
});
it('should parse a xsd:short object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:short'
},
$value: '1'
})).to.equal(1);
});
it('should parse a xsd:int object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:int'
},
$value: '1.23'
})).to.equal(1.23);
});
it('should parse a xsd:long object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:long'
},
$value: '-1'
})).to.equal(-1);
});
it('should parse a xsd:dateTime object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'xsd:dateTime'
},
$value: '2019-01-01T12:34:45.678Z'
})).to.equalTime(new Date(2019, 0, 1, 12, 34, 45, 678));
});
it('should parse a ArrayOfString object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfString'
},
string: ['str1', 'str2']
})).to.deep.equal(['str1', 'str2']);
});
it('should parse a ArrayOfBoolean object', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfBoolean'
},
boolean: ['true', 'false']
})).to.deep.equal([true, false]);
});
['Short', 'Int', 'Long'].forEach((type) => {
it(`should parse a ArrayOf${type} object`, function() {
expect(parser.parse({
attributes: {
'xsi:type': `ArrayOf${type}`
},
[type.toLowerCase()]: ['123', '12.34', '-42']
})).to.deep.equal([123, 12.34, -42]);
});
});
it('should parse a complex type, if it is mapped in the library', function() {
const parsed = parser.parse({
attributes: {
'xsi:type': sdk.data.HostHardwareSummary.name
},
cpuMhz: '2048',
cpuModel: 'Xeon E5-2640v2',
memorySize: '409600',
numCpuCores: '20',
model: 'R820',
numCpuPkgs: '2',
numCpuThreads: '40',
numHBAs: '2',
numNics: '3',
uuid: '123456-0000-0000-123456',
vendor: 'Dell'
});
expect(parsed).to.be.a.instanceOf(sdk.data.HostHardwareSummary);
expect(parsed).to.deep.equal({
cpuMhz: 2048,
cpuModel: 'Xeon E5-2640v2',
memorySize: 409600,
model: 'R820',
numCpuCores: 20,
numCpuPkgs: 2,
numCpuThreads: 40,
numHBAs: 2,
numNics: 3,
uuid: '123456-0000-0000-123456',
vendor: 'Dell'
});
});
it('should support "XMLSchema-instance" namespace too', function() {
const parsed = parser.parse({
attributes: {
'XMLSchema-instance:type': sdk.data.HostHardwareSummary.name
},
cpuMhz: '2048',
cpuModel: 'Xeon E5-2640v2'
});
expect(parsed).to.be.a.instanceOf(sdk.data.HostHardwareSummary);
expect(parsed).to.deep.equal({
cpuMhz: 2048,
cpuModel: 'Xeon E5-2640v2'
});
});
describe('ComplexType', function() {
it('should return a complex type when the actual type is mapped', function() {
const parsed = parser.parse({
attributes: {
'xsi:type': 'ClusterComputeResourceSummary'
},
effectiveCpu: '123'
}, 'ComputeResourceSummary');
expect(parsed).to.be.an.instanceof(sdk.data.ClusterComputeResourceSummary);
expect(parsed).to.deep.equal({ effectiveCpu: 123 });
});
it('should return a complex type of the default type when the actual type is mapped', function() {
const parsed = parser.parse({
attributes: {
'xsi:type': 'AnotherUnmappedComputeResourceSummary'
},
effectiveCpu: '123'
}, 'ComputeResourceSummary');
expect(parsed).to.be.an.instanceof(sdk.data.ComputeResourceSummary);
expect(parsed).to.deep.equal({ effectiveCpu: 123 });
});
});
describe('ArrayOfComplexType', function() {
it('should return an array of complex type instances', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfAlarmState'
},
AlarmState: [{
key: 'myAlarm1',
acknowledged: true
}, {
key: 'myAlarm2',
acknowledged: false
}]
}, 'ArrayOfAlarmState')).to.deep.equal([
{
acknowledged: true,
key: 'myAlarm1'
},
{
acknowledged: false,
key: 'myAlarm2'
}
]);
});
it('should return an empty array when there is no instances', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfAlarmState'
},
AlarmState: []
}, 'ArrayOfAlarmState')).to.deep.equal([]);
});
it('should return an empty array when instances attribute does not exist', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'ArrayOfAlarmState'
}
}, 'ArrayOfAlarmState')).to.deep.equal([]);
});
it('should return undefined when undefined is given', function() {
expect(parser.parse(undefined, 'ArrayOfAlarmState')).to.equal(undefined);
});
});
describe('Enum', function() {
it('should parse an enum value as a String', function() {
expect(parser.parse('running', 'VirtualMachinePowerState')).to.equal('running');
});
it('should parse an enum XML object as a String', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'VirtualMachinePowerState'
},
$value: 'running'
}, 'VirtualMachinePowerState')).to.equal('running');
});
});
});
it('should return the object unparsed when everything failed and there is no default type', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'UnmappedObject'
},
a: 1
})).to.deep.equal({
attributes: {
'xsi:type': 'UnmappedObject'
},
a: 1
});
});
it('should return the object unparsed when everything failed, including the default type', function() {
expect(parser.parse({
attributes: {
'xsi:type': 'UnmappedObject'
},
a: 1
}, 'AnotherUnmappedDefaultType')).to.deep.equal({
attributes: {
'xsi:type': 'UnmappedObject'
},
a: 1
});
});
});
});