diff --git a/lib/iprod.js b/lib/iprod.js index 26061fb1..afc673b7 100644 --- a/lib/iprod.js +++ b/lib/iprod.js @@ -168,20 +168,25 @@ class iProd { ]; if(!cb) { // If we do not get the third param, if(option) { // If we get two params, - if(xt.getClass(option) == "Function") // If it is a function, + if(xt.getClass(option) == "Function"){ // If it is a function, cb = option; // then it is the callback. + option = "0000"; + } } else { // If we have only one param, option = "0000"; // then use *BASE as default. } } - if(option < 0 || option > 99) - option = "0000"; - else if(option < 10) - option = "000" + option; - else option = "00" + option; - + if(Number.isInteger(option)) { + if(option < 0 || option > 99) + option = "0000"; + else if(option < 10) + option = "000" + option; + else + option = "00" + option; + } + let ProdInfo = [ [prodID, "7A"], ["*ONLY", "6A"], diff --git a/lib/itoolkit.js b/lib/itoolkit.js index dda604e3..594b2135 100644 --- a/lib/itoolkit.js +++ b/lib/itoolkit.js @@ -137,8 +137,14 @@ const xmlToJson = (xml) => { let sucFlag = sqlData[i].match(successReg); if(sucFlag && sucFlag.length > 0) { rs.success = true; - if(sucFlag.length > 1) - rs.stmt = sucFlag[1]; + if(sucFlag.length > 1){ + if(sucFlag[0].includes('![CDATA')){ + let fixed = sucFlag[1].replace(/]]>/, ''); + rs.stmt = fixed; + } else{ + rs.stmt = sucFlag[1]; + } + } } else { rs.success = false; @@ -224,7 +230,7 @@ class iConn { } // setTimeout() override the default timeout value for sync mode. setTimeout(seconds) { - if(__getClass(flag) == "Number") + if(__getClass(seconds) == "Number") this.timeout = seconds * 1000; } // debug() get current verbose mode or enable/disable verbose mode for debugging. @@ -535,11 +541,14 @@ class iSql { } rowCount(options) { - this.xml += i_xml.iXmlNodeSqlRowCount(options.action, options.error); + if(options && options.error) + this.xml += i_xml.iXmlNodeSqlRowCount(options.error); + else + this.xml += i_xml.iXmlNodeSqlRowCount(); } count(options) { - this.xml += i_xml.iXmlNodeSqlRowCount(options.desc, options.error); + this.xml += i_xml.iXmlNodeSqlCount(options.desc, options.error); } describe(options) { diff --git a/lib/ixml.js b/lib/ixml.js index 5954194b..0a93a318 100644 --- a/lib/ixml.js +++ b/lib/ixml.js @@ -289,7 +289,7 @@ const iXmlNodeSqlConnect = (db, uid, pwd, option_label) => { } const iXmlNodeSqlOptions = (options) => { - const iXml = iXmlNodeOpen(I_XML_NODE_SQL_OPTIONS_OPEN); + let iXml = iXmlNodeOpen(I_XML_NODE_SQL_OPTIONS_OPEN); for(const i in options) iXml += iXmlAttrDefault(options[i].desc, options[i].value, I_XML_ATTR_VALUE_OPTIONAL); return iXml + I_XML_NODE_CLOSE + I_XML_NODE_SQL_OPTIONS_CLOSE; @@ -332,7 +332,7 @@ const iXmlNodeSqlExecuteClose = () => { const iXmlNodeSqlParmOpen = (xio) => { return iXmlNodeOpen(I_XML_NODE_PARM_OPEN) - + iXmlAttrDefault(I_XML_ATTR_KEY_NAME,xio,I_XML_ATTR_VALUE_OPTIONAL) + + iXmlAttrDefault(I_XML_ATTR_KEY_IO,xio,I_XML_ATTR_VALUE_OPTIONAL) + I_XML_NODE_CLOSE; } diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 00000000..5a1c1e91 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,12 @@ +/* eslint-disable new-cap */ +const { iConn } = require('./itoolkit.js'); + +function returnTransports(opt) { + const transports = [{ name: 'idb', me: new iConn(opt.database, opt.user, opt.password) }, + { name: 'rest', me: new iConn(opt.database, opt.user, opt.password, opt) }, + ]; + + return transports; +} + +module.exports = { returnTransports }; diff --git a/package.json b/package.json index 9a40211d..90632104 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,11 @@ ], "devDependencies": { "chai": "^4.1.2", + "mocha": "^5.2.0", + "sinon": "^7.2.3" + }, + "optionalDependencies": { "idb-connector": "^1.1.8", - "mocha": "^5.2.0" + "idb-pconnector": "^1.0.2" } } diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..98d69f51 --- /dev/null +++ b/test/README.md @@ -0,0 +1,89 @@ +# Node.js Toolkit Tests + +Ensure dependencies are installed + +From the root of the project run: `npm install` + +***NOTE*** + +Some tests require creating libraries, objects, tables, etc. + +A before hook is setup to check for theses objects and create if needed. + +These hooks are ran with `idb-pconnector` which requires to be run on IBM i. + +In any case, the functional tests test for both transports Db2 and REST. + +Using Db2 transport requires `idb-connector` which only runs on IBM i systems. + +Tests using these hooks will fail on non IBM i systems. + +# Running Tests + +From the project root + +`npm test test/foo` + +where foo is the name of subdir such as `unit` or or individual test file. + +***NOTE*** + +If you experience timeout issue with network calls add + +`"test": "./node_modules/mocha/bin/mocha" --timeout Xs ` + +within `package.json` file, where X is the number of seconds before timeout + +# Setup Rest interface + +- add to the default apache server conf: `/www/apachedft/conf/httpd.conf` + + ``` + ScriptAlias /cgi-bin/ /QSYS.LIB/QXMLSERVc .LIB/ + + AllowOverride None + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + + ``` + +- start the server + + ` STRTCPSVR SERVER(*HTTP) HTTPSVR(APACHEDFT)` + +- go to `http://HOSTNAME/cgi-bin/xmlcgi.pgm` + + you should see an XML document + +- when finished testing you can shutdown server with + + ` ENDTCPSVR SERVER(*HTTP) HTTPSVR(APACHEDFT)` + + +# Configuring Tests +Each functional test contains an config object that is used to create connections + +It is recommend to setup environment variables for these configurations + +Instead of hard coding credentials with the test file. + +you can set environment varaibales with `export KEY='value'` + +--- +- user `TKUSER` defaults to '' + +- password `TKPASS` defaults to '' + +- database `TKDB` defaults to `*LOCAL` + +For Rest Tests +--- +- HOST `TKHOST` defaults to `localhost` + +- PORT `TKPORT` defaults to `80` + +- PATH `TKPATH` defaults to `/cgi-bin/xmlcgi.pgm` + diff --git a/test/functional/commandsFunctional.js b/test/functional/commandsFunctional.js new file mode 100644 index 00000000..ec9a5407 --- /dev/null +++ b/test/functional/commandsFunctional.js @@ -0,0 +1,97 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ + +const { expect } = require('chai'); +const { + iCmd, iSh, iQsh, xmlToJson, +} = require('../../lib/itoolkit'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iSh, iCmd, iQsh, Functional Tests', () => { + describe('iCmd()', () => { + transports.forEach((transport) => { + it(`calls CL command using ${transport.name} transport`, (done) => { + const connection = transport.me; + connection.add(iCmd('RTVJOBA USRLIBL(?) SYSLIBL(?)')); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + results.forEach((result) => { + expect(result.success).to.equal(true); + }); + done(); + }); + }); + }); + }); + + describe('iSh()', () => { + transports.forEach((transport) => { + it(`calls PASE shell command using ${transport.name} transport`, (done) => { + const connection = transport.me; + + connection.add(iSh('system -i wrksyssts')); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + // xs does not return success property for iSh or iQsh + // but on error data property = '\n' + // so lets base success on contents of data. + results.forEach((result) => { + expect(result.data).not.to.equal('\n'); + expect(result.data).to.match(/(System\sStatus\sInformation)/); + }); + done(); + }); + }); + }); + }); + + describe('iQsh()', () => { + transports.forEach((transport) => { + it(`calls QSH command using ${transport.name} transport`, (done) => { + const connection = transport.me; + connection.add(iQsh('system wrksyssts')); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + // xs does not return success property for iSh or iQsh + // but on error data property = '\n' + // so lets base success on contents of data. + results.forEach((result) => { + expect(result.data).not.to.equal('\n'); + expect(result.data).to.match(/(System\sStatus\sInformation)/); + }); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iDataQueueFunctional.js b/test/functional/iDataQueueFunctional.js new file mode 100644 index 00000000..e620f8ea --- /dev/null +++ b/test/functional/iDataQueueFunctional.js @@ -0,0 +1,135 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iConn } = require('../../lib/itoolkit'); +const { iDataQueue } = require('../../lib/idataq'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const lib = 'NODETKTEST'; const dqName = 'TESTQ'; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iDataQueue Functional Tests', () => { + before('setup library for tests and create DQ', async () => { + // eslint-disable-next-line global-require + const { DBPool } = require('idb-pconnector'); + + const pool = new DBPool({ url: '*LOCAL' }, { incrementSize: 2 }); + + const qcmdexec = 'CALL QSYS2.QCMDEXC(?)'; + + const createLib = `CRTLIB LIB(${lib}) TYPE(*TEST) TEXT('Used to test Node.js toolkit')`; + + const createDQ = `CRTDTAQ DTAQ(${lib}/${dqName}) MAXLEN(100) AUT(*EXCLUDE) TEXT('TEST DQ FOR NODE TOOLKIT TESTS')`; + + const findLib = 'SELECT SCHEMA_NAME FROM qsys2.sysschemas WHERE SCHEMA_NAME = \'NODETKTEST\''; + + const findDQ = 'SELECT OBJLONGNAME FROM TABLE (QSYS2.OBJECT_STATISTICS(\'NODETKTEST\', \'*DTAQ\')) AS X'; + + const libResult = await pool.runSql(findLib); + + const dqResult = await pool.runSql(findDQ); + + if (!libResult.length) { + await pool.prepareExecute(qcmdexec, [createLib]).catch((error) => { + // eslint-disable-next-line no-console + console.log('Unable to Create Lib!'); + throw error; + }); + // eslint-disable-next-line no-console + console.log('CREATED LIB!'); + } + if (!dqResult.length) { + await pool.prepareExecute(qcmdexec, [createDQ]).catch((error) => { + // eslint-disable-next-line no-console + console.log('Unable to Create DQ!'); + throw error; + }); + // eslint-disable-next-line no-console + console.log('CREATED DQ!'); + } + }); + describe('constructor', () => { + it('creates and returns an instance of iDataQueue', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const dq = new iDataQueue(connection); + expect(dq).to.be.instanceOf(iDataQueue); + }); + }); + + describe('sendToDataQueue', () => { + transports.forEach((transport) => { + it(`sends data to specified DQ using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const dq = new iDataQueue(connection); + + dq.sendToDataQueue(dqName, lib, 'Hello from DQ!', (output) => { + expect(output).to.equal(true); + done(); + }); + }); + }); + }); + + describe('receiveFromDataQueue', () => { + transports.forEach((transport) => { + it(`receives data from specfied DQ using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const dq = new iDataQueue(connection); + + dq.receiveFromDataQueue(dqName, lib, 100, (output) => { + expect(output).to.be.a('string').and.to.equal('Hello from DQ!'); + done(); + }); + }); + }); + }); + + describe('clearDataQueue', () => { + transports.forEach((transport) => { + it(`clears the specifed DQ using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const dq = new iDataQueue(connection); + + dq.clearDataQueue(dqName, lib, (output) => { + expect(output).to.equal(true); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iNetworkFunctional.js b/test/functional/iNetworkFunctional.js new file mode 100644 index 00000000..450623d9 --- /dev/null +++ b/test/functional/iNetworkFunctional.js @@ -0,0 +1,156 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iConn } = require('../../lib/itoolkit'); +const { iNetwork } = require('../../lib/inetwork'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iNetwork Functional Tests', () => { + describe('constructor', () => { + it('creates and returns an instance of iNetwork', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const net = new iNetwork(connection); + + expect(net).to.be.instanceOf(iNetwork); + }); + }); + + describe('getTCPIPAttr', () => { + transports.forEach((transport) => { + it(`retrieves TCP/IP Attributes using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const net = new iNetwork(connection); + + net.getTCPIPAttr((output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('TCP/IPv4_stack_status'); + expect(output).to.have.a.property('How_long_active'); + expect(output).to.have.a.property('When_last_started_-_date'); + expect(output).to.have.a.property('When_last_started_-_time'); + expect(output).to.have.a.property('When_last_ended_-_date'); + expect(output).to.have.a.property('When_last_ended_-_time'); + expect(output).to.have.a.property('Who_last_started_-_job_name'); + expect(output).to.have.a.property('Who_last_started_-_job_user_name'); + expect(output).to.have.a.property('Who_last_started_-_job_number'); + expect(output).to.have.a.property('Who_last_started_-_internal_job_identifier'); + expect(output).to.have.a.property('Who_last_ended_-_job_name'); + expect(output).to.have.a.property('Who_last_ended_-_job_user_name'); + expect(output).to.have.a.property('Who_last_ended_-_job_number'); + expect(output).to.have.a.property('Who_last_ended_-_internal_job_identifier'); + expect(output).to.have.a.property('Offset_to_additional_information'); + expect(output).to.have.a.property('Length_of_additional_information'); + expect(output).to.have.a.property('Limited_mode'); + expect(output).to.have.a.property('Offset_to_list_of_Internet_addresses'); + expect(output).to.have.a.property('Number_of_Internet_addresses'); + expect(output).to.have.a.property('Entry_length_for_list_of_Internet_addresses'); + expect(output).to.have.a.property('DNS_protocol'); + expect(output).to.have.a.property('Retries'); + expect(output).to.have.a.property('Time_interval'); + expect(output).to.have.a.property('Search_order'); + expect(output).to.have.a.property('Initial_domain_name_server'); + expect(output).to.have.a.property('DNS_listening_port'); + expect(output).to.have.a.property('Host_name'); + expect(output).to.have.a.property('Domain_name'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Domain_search_list'); + done(); + }); + }); + }); + }); + + describe('getNetInterfaceData', () => { + transports.forEach((transport) => { + it(`retrieves IPv4 network interface info using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const net = new iNetwork(connection); + + net.getNetInterfaceData('127.0.0.1', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Internet_address'); + expect(output).to.have.a.property('Internet_address_binary'); + expect(output).to.have.a.property('Network_address'); + expect(output).to.have.a.property('Network_address_binary'); + expect(output).to.have.a.property('Line_description'); + expect(output).to.have.a.property('Interface_status'); + expect(output).to.have.a.property('Interface_type_of_service'); + expect(output).to.have.a.property('Interface_MTU'); + expect(output).to.have.a.property('Interface_line_type'); + expect(output).to.have.a.property('Host_address'); + expect(output).to.have.a.property('Host_address_binary'); + expect(output).to.have.a.property('Interface_subnet_mask'); + expect(output).to.have.a.property('Interface_subnet_mask_binary'); + expect(output).to.have.a.property('Directed_broadcast_address'); + expect(output).to.have.a.property('Directed_broadcast_address_binary'); + expect(output).to.have.a.property('Change_date'); + expect(output).to.have.a.property('Change_time'); + expect(output).to.have.a.property('Associated_local_interface'); + expect(output).to.have.a.property('Associated_local_interface_binary'); + expect(output).to.have.a.property('Packet_rules'); + expect(output).to.have.a.property('Change_status'); + expect(output).to.have.a.property('Automatic_start'); + expect(output).to.have.a.property('TRLAN_bit_sequencing'); + expect(output).to.have.a.property('Interface_type'); + expect(output).to.have.a.property('Proxy_ARP_allowed'); + expect(output).to.have.a.property('Proxy_ARP_enabled'); + expect(output).to.have.a.property('Configured_MTU'); + expect(output).to.have.a.property('Network_name'); + expect(output).to.have.a.property('Interface_name'); + expect(output).to.have.a.property('Alias_name'); + expect(output).to.have.a.property('Interface_description'); + expect(output).to.have.a.property('Offset_to_preferred_interface_list'); + expect(output).to.have.a.property('Number_of_entries_in_preferred_interface_list'); + expect(output).to.have.a.property('Length_of_one_preferred_interface_list_entry'); + expect(output).to.have.a.property('DHCP_created'); + expect(output).to.have.a.property('DHCP_dynamic_DNS_updates'); + expect(output).to.have.a.property('DHCP_lease_expiration'); + expect(output).to.have.a.property('DHCP_lease_expiration_-_date'); + expect(output).to.have.a.property('DHCP_lease_expiration_-_time'); + expect(output).to.have.a.property('DHCP_lease_obtained'); + expect(output).to.have.a.property('DHCP_lease_obtained_-_date'); + expect(output).to.have.a.property('DHCP_lease_obtained_-_time'); + expect(output).to.have.a.property('Use_DHCP_unique_identifier'); + expect(output).to.have.a.property('DHCP_server_IP_address'); + expect(output).to.have.a.property('Preferred_interface_Internet_address'); + expect(output).to.have.a.property('Preferred_interface_Internet_address_binary'); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iObjFunctional.js b/test/functional/iObjFunctional.js new file mode 100644 index 00000000..8f1f1846 --- /dev/null +++ b/test/functional/iObjFunctional.js @@ -0,0 +1,367 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iConn } = require('../../lib/itoolkit'); +const { iObj } = require('../../lib/iobj'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iObj Functional Tests', () => { + describe('constructor', () => { + it('creates and returns an instance of iObj', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const obj = new iObj(connection); + + expect(obj).to.be.instanceOf(iObj); + }); + }); + + describe('retrUsrAuth', () => { + transports.forEach((transport) => { + it(`returns uses's authority for an object using ${transport.name} tranport`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.retrUsrAuth('*PUBLIC', '*PGM', 'XMLCGI', 'QXMLSERV', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Object_authority_/_Data_authority'); + expect(output).to.have.a.property('Authorization_list_management'); + expect(output).to.have.a.property('Object_operational'); + expect(output).to.have.a.property('Object_management'); + expect(output).to.have.a.property('Object_existence'); + expect(output).to.have.a.property('Data_read'); + expect(output).to.have.a.property('Data_add'); + expect(output).to.have.a.property('Data_update'); + expect(output).to.have.a.property('Data_delete'); + expect(output).to.have.a.property('Authorization_list'); + expect(output).to.have.a.property('Authority_source'); + expect(output).to.have.a.property('Some_adopted_authority'); + expect(output).to.have.a.property('Adopted_object_authority'); + expect(output).to.have.a.property('Adopted_authorization_list_management'); + expect(output).to.have.a.property('Adopted_object_operational'); + expect(output).to.have.a.property('Adopted_object_management'); + expect(output).to.have.a.property('Adopted_object_existence'); + expect(output).to.have.a.property('Adopted_data_read'); + expect(output).to.have.a.property('Adopted_data_add'); + expect(output).to.have.a.property('Adopted_data_update'); + expect(output).to.have.a.property('Adopted_data_delete'); + expect(output).to.have.a.property('Adopted_data_execute'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Adopted_object_alter'); + expect(output).to.have.a.property('Adopted_object_reference'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Data_execute'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Object_alter'); + expect(output).to.have.a.property('Object_reference'); + expect(output).to.have.a.property('ASP_device_name_of_library'); + expect(output).to.have.a.property('ASP_device_name_of_object'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Offset_to_group_information_table'); + expect(output).to.have.a.property('Number_of_group_table_entries_returned'); + done(); + }); + }); + }); + }); + + describe('rtrCmdInfo', () => { + transports.forEach((transport) => { + it(`returns command info using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.retrCmdInfo('CRTLIB', '*LIBL', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Command_name'); + expect(output).to.have.a.property('Command_library_name'); + expect(output).to.have.a.property('Command_processing_program_or_proxy_target_command'); + expect(output).to.have.a.property('Command_processing_program\'s_or_proxy_target_command\'s_library_name'); + expect(output).to.have.a.property('Source_file_name'); + expect(output).to.have.a.property('Source_file_library_name'); + expect(output).to.have.a.property('Source_file_member_name'); + expect(output).to.have.a.property('Validity_check_program_name'); + expect(output).to.have.a.property('Validity_check_program_library_name'); + expect(output).to.have.a.property('Mode_information'); + expect(output).to.have.a.property('Where_allowed_to_run'); + expect(output).to.have.a.property('Allow_limited_user'); + expect(output).to.have.a.property('Maximum_positional_parameters'); + expect(output).to.have.a.property('Prompt_message_file_name'); + expect(output).to.have.a.property('Prompt_message_file_library_name'); + expect(output).to.have.a.property('Message_file_name'); + expect(output).to.have.a.property('Message_file_library_name'); + expect(output).to.have.a.property('Help_panel_group_name'); + expect(output).to.have.a.property('Help_panel_group_library_name'); + expect(output).to.have.a.property('Help_identifier'); + expect(output).to.have.a.property('Search_index_name'); + expect(output).to.have.a.property('Search_index_library_name'); + expect(output).to.have.a.property('Current_library'); + expect(output).to.have.a.property('Product_library'); + expect(output).to.have.a.property('Prompt_override_program_name'); + expect(output).to.have.a.property('Prompt_override_program_library_name'); + expect(output).to.have.a.property('Restricted_to_target_release'); + expect(output).to.have.a.property('Text_description'); + expect(output).to.have.a.property('Command_processing_program_call_state'); + expect(output).to.have.a.property('Validity_check_program_call_state'); + expect(output).to.have.a.property('Prompt_override_program_call_state'); + expect(output).to.have.a.property('Offset_to_help_bookshelf_information'); + expect(output).to.have.a.property('Length_of_help_bookshelf_information'); + expect(output).to.have.a.property('Coded_character_set_ID_(CCSID)'); + expect(output).to.have.a.property('Enabled_for_GUI_indicator'); + expect(output).to.have.a.property('Threadsafe_indicator'); + expect(output).to.have.a.property('Multithreaded_job_action'); + expect(output).to.have.a.property('Proxy_command_indicator'); + expect(output).to.have.a.property('Prompt_message_file_text_indicator'); + done(); + }); + }); + }); + }); + + describe('retrPgmInfo', () => { + transports.forEach((transport) => { + it(`returns program info using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.retrPgmInfo('XMLCGI', 'QXMLSERV', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Program_name'); + expect(output).to.have.a.property('Program_library_name'); + expect(output).to.have.a.property('Program_owner'); + expect(output).to.have.a.property('Program_attribute'); + expect(output).to.have.a.property('Creation_date_and_time'); + expect(output).to.have.a.property('Source_file_name'); + expect(output).to.have.a.property('Source_file_library_name'); + expect(output).to.have.a.property('Source_file_member_name'); + expect(output).to.have.a.property('Source_file_updated_date_and_time'); + expect(output).to.have.a.property('Observable_information'); + expect(output).to.have.a.property('User_profile_option'); + expect(output).to.have.a.property('Use_adopted_authority'); + expect(output).to.have.a.property('Log_commands'); + expect(output).to.have.a.property('Allow_RTVCLSRC'); + expect(output).to.have.a.property('Fix_decimal_data'); + expect(output).to.have.a.property('Text_description'); + expect(output).to.have.a.property('Type_of_program'); + expect(output).to.have.a.property('Teraspace_storage-enabled_program'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Minimum_number_of_parameters'); + expect(output).to.have.a.property('Maximum_number_of_parameters'); + expect(output).to.have.a.property('Program_size'); + expect(output).to.have.a.property('Associated_space_size'); + expect(output).to.have.a.property('Static_storage_size'); + expect(output).to.have.a.property('Automatic_storage_size'); + expect(output).to.have.a.property('Number_of_MI_instructions'); + expect(output).to.have.a.property('Number_of_MI_ODT_entries'); + expect(output).to.have.a.property('Program_state'); + expect(output).to.have.a.property('Compiler_identification'); + expect(output).to.have.a.property('Earliest_release_program_can_run'); + expect(output).to.have.a.property('Sort_sequence_table_name'); + expect(output).to.have.a.property('Sort_sequence_table_library_name'); + expect(output).to.have.a.property('Language_identifier'); + expect(output).to.have.a.property('Program_domain'); + expect(output).to.have.a.property('Conversion_required'); + expect(output).to.have.a.property('Conversion_details'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Optimization'); + expect(output).to.have.a.property('Paging_pool'); + expect(output).to.have.a.property('Update_program_automatic_storage_area_(PASA)'); + expect(output).to.have.a.property('Clear_program_automatic_storage_area_(PASA)'); + expect(output).to.have.a.property('Paging_amount'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Program_entry_procedure_module'); + expect(output).to.have.a.property('Program_entry_procedure_module_library'); + expect(output).to.have.a.property('Activation_group_attribute'); + expect(output).to.have.a.property('Observable_information_compressed'); + expect(output).to.have.a.property('Run-time_information_compressed'); + expect(output).to.have.a.property('Release_program_created_on'); + expect(output).to.have.a.property('Shared_activation_group'); + expect(output).to.have.a.property('Allow_update'); + expect(output).to.have.a.property('Program_CCSID'); + expect(output).to.have.a.property('Number_of_modules'); + expect(output).to.have.a.property('Number_of_service_programs'); + expect(output).to.have.a.property('Number_of_copyrights'); + expect(output).to.have.a.property('Number_of_unresolved_references'); + expect(output).to.have.a.property('Release_program_created_for'); + expect(output).to.have.a.property('Allow_static_storage_reinitialization'); + expect(output).to.have.a.property('All_creation_data'); + expect(output).to.have.a.property('Allow_bound_*SRVPGM_library_name_update'); + expect(output).to.have.a.property('Profiling_data'); + expect(output).to.have.a.property('Teraspace_storage_enabled_modules'); + expect(output).to.have.a.property('Storage_model'); + expect(output).to.have.a.property('Uses_argument_optimization_(ARGOPT)'); + done(); + }); + }); + }); + }); + + describe('retrSrvPgmInfo', () => { + transports.forEach((transport) => { + it(`returns service program info using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.retrSrvPgmInfo('QZSRVSSL', 'QHTTPSVR', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Service_program_name'); + expect(output).to.have.a.property('Service_program_name'); + expect(output).to.have.a.property('Service_program_owner'); + expect(output).to.have.a.property('Service_program_attribute'); + expect(output).to.have.a.property('Creation_date_and_time'); + expect(output).to.have.a.property('Export_source_file_name'); + expect(output).to.have.a.property('Export_source_file_library_name'); + expect(output).to.have.a.property('Export_source_file_member_name'); + expect(output).to.have.a.property('Activation_group_attribute'); + expect(output).to.have.a.property('Current_export_signature'); + expect(output).to.have.a.property('User_profile'); + expect(output).to.have.a.property('Observable_information_compressed'); + expect(output).to.have.a.property('Run-time_information_compressed'); + expect(output).to.have.a.property('Run-time_information_compressed'); + expect(output).to.have.a.property('Service_program_CCSID'); + expect(output).to.have.a.property('Number_of_modules'); + expect(output).to.have.a.property('Number_of_service_programs'); + expect(output).to.have.a.property('Number_of_copyrights'); + expect(output).to.have.a.property('Text_description'); + expect(output).to.have.a.property('Shared_activation_group'); + expect(output).to.have.a.property('Allow_update'); + expect(output).to.have.a.property('Number_of_unresolved_references'); + expect(output).to.have.a.property('Use_adopted_authority'); + expect(output).to.have.a.property('Allow_bound_*SRVPGM_library_name_update'); + expect(output).to.have.a.property('Profiling_data'); + expect(output).to.have.a.property('Teraspace_storage_enabled_modules'); + expect(output).to.have.a.property('Storage_model'); + expect(output).to.have.a.property('Uses_argument_optimization_(ARGOPT)'); + expect(output).to.have.a.property('Reserved_\'00\'X'); + expect(output).to.have.a.property('Service_program_state'); + expect(output).to.have.a.property('Service_program_domain'); + expect(output).to.have.a.property('Associated_space_size'); + expect(output).to.have.a.property('Static_storage_size'); + expect(output).to.have.a.property('Service_program_size'); + expect(output).to.have.a.property('Release_service_program_created_on'); + expect(output).to.have.a.property('Earliest_release_service_program_can_run'); + expect(output).to.have.a.property('Release_service_program_created_for'); + expect(output).to.have.a.property('Allow_static_storage_reinitialization'); + expect(output).to.have.a.property('Conversion_required'); + expect(output).to.have.a.property('All_creation_data'); + expect(output).to.have.a.property('Conversion_details'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Paging_pool'); + expect(output).to.have.a.property('Paging_amount'); + done(); + }); + }); + }); + }); + + describe('retrUserInfo', () => { + transports.forEach((transport) => { + it(`returns specified user profile info using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.retrUserInfo('QSYS', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('User_profile_name'); + expect(output).to.have.a.property('Previous_sign-on_date_and_time'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Sign-on_attempts_not_valid'); + expect(output).to.have.a.property('Status'); + expect(output).to.have.a.property('Password_change_date'); + expect(output).to.have.a.property('No_password_indicator'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Password_expiration_interval'); + expect(output).to.have.a.property('Date_password_expires'); + expect(output).to.have.a.property('Days_until_password_expires'); + expect(output).to.have.a.property('Set_password_to_expire'); + expect(output).to.have.a.property('Display_sign-on_information'); + expect(output).to.have.a.property('Local_password_management'); + expect(output).to.have.a.property('Block_password_change'); + done(); + }); + }); + }); + }); + + describe('retrUsrAuthToObj', () => { + transports.forEach((transport) => { + it(`retrieves info for users who are authorized to an object using ${transport.name} transpsort`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.retrUserAuthToObj('/home', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Profile_name'); + expect(output).to.have.a.property('User_or_group_indicator'); + expect(output).to.have.a.property('Data_authority'); + expect(output).to.have.a.property('Authorization_list_management'); + expect(output).to.have.a.property('Object_management'); + expect(output).to.have.a.property('Object_existence'); + expect(output).to.have.a.property('Object_alter'); + expect(output).to.have.a.property('Object_reference'); + expect(output).to.have.a.property('Reserved'); + expect(output).to.have.a.property('Object_operational'); + expect(output).to.have.a.property('Data_read'); + expect(output).to.have.a.property('Data_add'); + expect(output).to.have.a.property('Data_update'); + expect(output).to.have.a.property('Data_delete'); + expect(output).to.have.a.property('Data_execute'); + done(); + }); + }); + }); + }); + + describe('addToLibraryList', () => { + transports.forEach((transport) => { + it(`appends lib to user's lib list using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const obj = new iObj(connection); + + obj.addToLibraryList('QHTTPSVR', (output) => { + expect(output).to.be.a('boolean').and.to.equal(true); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iPgmFunctional.js b/test/functional/iPgmFunctional.js new file mode 100644 index 00000000..cbdda8e0 --- /dev/null +++ b/test/functional/iPgmFunctional.js @@ -0,0 +1,134 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iPgm, xmlToJson } = require('../../lib/itoolkit'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iPgm Functional Tests', () => { + describe('Test iPgm()', () => { + transports.forEach((transport) => { + it(`calls QWCRSVAL program checks if it ran successfully using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const program = new iPgm('QWCRSVAL', { lib: 'QSYS' }); + + const outBuf = [ + [0, '10i0'], + [0, '10i0'], + ['', '36h'], + ['', '10A'], + ['', '1A'], + ['', '1A'], + [0, '10i0'], + [0, '10i0'], + ]; + program.addParam(outBuf, { io: 'out' }); + program.addParam(66, '10i0'); + program.addParam(1, '10i0'); + program.addParam('QCCSID', '10A'); + program.addParam(this.errno, { io: 'both', len: 'rec2' }); + connection.add(program); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + }); + done(); + }); + }); + }); + }); + + + describe('Test iPgm()', () => { + transports.forEach((transport) => { + it(`calls QWCRSVAL program and returns arbitrarily named parameter using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const program = new iPgm('QWCRSVAL', { lib: 'QSYS' }); + + const outBuf = [ + [0, '10i0'], + [0, '10i0'], + ['', '36h'], + ['', '10A'], + ['', '1A'], + ['', '1A'], + [0, '10i0'], + [0, '10i0'], + ]; + program.addParam(outBuf, { io: 'out' }); + program.addParam(66, '10i0'); + program.addParam(1, '10i0'); + program.addParam('QCCSID', '10A'); + const paramValue = 'errno'; + + program.addParam(this.errno, { io: 'both', len: 'rec2', name: paramValue }); + connection.add(program); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + }); + done(); + }); + }); + }); + }); + + describe.skip('Test iPgm()', () => { + // Skip for now ZZSRV6 program requires XMLSERVICE built with tests + // Refer to test/rpg/zzsrv6.rpgle + transports.forEach((transport) => { + it.skip(`Should be successful with addReturn arbitrary attribute specified using using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const program = new iPgm('ZZSRV6', { lib: 'XMLSERVICE', func: 'ZZVARY4' }); + + program.addParam('Gill', '10A', { letying: '4' }); + const testValue = 'NEW_NAME'; + program.addReturn('0', '20A', { letying: '4', name: testValue }); + connection.add(program); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + expect(results[0].data[1].name).to.equal(testValue); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iProdFunctional.js b/test/functional/iProdFunctional.js new file mode 100644 index 00000000..30417546 --- /dev/null +++ b/test/functional/iProdFunctional.js @@ -0,0 +1,164 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iConn } = require('../../lib/itoolkit'); +const { iProd } = require('../../lib/iprod'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iProd Functional Tests', () => { + describe('constructor', () => { + it('creates and returns an instance of iProd', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const prod = new iProd(connection); + + expect(prod).to.be.instanceOf(iProd); + }); + }); + + describe('getPTFInfo', () => { + transports.forEach((transport) => { + it(`returns info for specified ptf using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const prod = new iProd(connection); + + prod.getPTFInfo('SI67726', (ptf) => { + expect(ptf).to.be.an('Object'); + expect(ptf).to.have.a.property('Product_ID'); + expect(ptf).to.have.a.property('PTF_ID'); + expect(ptf).to.have.a.property('Release_level'); + expect(ptf).to.have.a.property('Product_option'); + expect(ptf).to.have.a.property('Load_ID'); + expect(ptf).to.have.a.property('Loaded_status'); + expect(ptf).to.have.a.property('Cover_letter_status'); + expect(ptf).to.have.a.property('On-order_status'); + expect(ptf).to.have.a.property('Save_file_status'); + expect(ptf).to.have.a.property('File_name'); + expect(ptf).to.have.a.property('File_library_name'); + expect(ptf).to.have.a.property('PTF_type'); + expect(ptf).to.have.a.property('IPL_action'); + expect(ptf).to.have.a.property('Action_pending'); + expect(ptf).to.have.a.property('Action_required'); + expect(ptf).to.have.a.property('PTF_is_released'); + expect(ptf).to.have.a.property('Target_release'); + expect(ptf).to.have.a.property('Superseding_PTF'); + expect(ptf).to.have.a.property('Current_IPL_source'); + expect(ptf).to.have.a.property('Minimum_level'); + expect(ptf).to.have.a.property('Maximum_level'); + expect(ptf).to.have.a.property('Format_information_available'); + expect(ptf).to.have.a.property('Status_date_and_time'); + expect(ptf).to.have.a.property('Licensed_Internal_Code_group'); + expect(ptf).to.have.a.property('Superseded_by_PTF_ID'); + expect(ptf).to.have.a.property('Current_server_IPL_source'); + expect(ptf).to.have.a.property('Server_IPL_required'); + expect(ptf).to.have.a.property('Creation_date_and_time'); + expect(ptf).to.have.a.property('Technology_refresh_PTF'); + expect(ptf).to.have.a.property('Reserved'); + done(); + }); + }); + }); + }); + + describe('getProductInfo', () => { + transports.forEach((transport) => { + it(`returns info for specified product using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const prod = new iProd(connection); + + prod.getProductInfo('5770DG1', (product) => { + expect(product).to.be.an('Object'); + expect(product).to.have.a.property('Reserved'); + expect(product).to.have.a.property('Product_ID'); + expect(product).to.have.a.property('Release_level'); + expect(product).to.have.a.property('Product_option'); + expect(product).to.have.a.property('Load_ID'); + expect(product).to.have.a.property('Symbolic_load_state'); + expect(product).to.have.a.property('Load_error_indicator'); + expect(product).to.have.a.property('Load_state'); + expect(product).to.have.a.property('Supported_flag'); + expect(product).to.have.a.property('Registration_type'); + expect(product).to.have.a.property('Registration_value'); + expect(product).to.have.a.property('Offset_to_additional_information'); + expect(product).to.have.a.property('Primary_language_load_identifier'); + expect(product).to.have.a.property('Minimum_target_release'); + expect(product).to.have.a.property('Minimum_VRM_of_*BASE_required_by_option'); + expect(product).to.have.a.property('Requirements_met_between_base_and_option_value'); + expect(product).to.have.a.property('Level'); + done(); + }); + }); + }); + }); + + // REST transport currently failing with 414 URI Too Long response code + // The requested URL's length exceeds the capacity limit for this server + describe('getInstalledProducts', () => { + transports.forEach((transport) => { + // eslint-disable-next-line func-names + it(`returns info for installed products using ${transport.name} transport`, function (done) { + if (transport.name === 'rest') { + this.skip(); + } + const connection = transport.me; + + const prod = new iProd(connection); + + prod.getInstalledProducts((products) => { + expect(products).to.be.an('Array'); + expect(products.length).to.be.greaterThan(0); + + products.forEach((product) => { + expect(product).to.be.an('Object'); + expect(product).to.have.a.property('Product_ID'); + expect(product).to.have.a.property('Product_option'); + expect(product).to.have.a.property('Release_level'); + expect(product).to.have.a.property('Description_text_message_ID'); + expect(product).to.have.a.property('Description_text_object_name'); + expect(product).to.have.a.property('Description_text_library_name'); + expect(product).to.have.a.property('Installed_flag'); + expect(product).to.have.a.property('Supported_flag'); + expect(product).to.have.a.property('Registration_type'); + expect(product).to.have.a.property('Registration_value'); + expect(product).to.have.a.property('Description_text'); + }); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iSqlFunctional.js b/test/functional/iSqlFunctional.js new file mode 100644 index 00000000..bf31f00b --- /dev/null +++ b/test/functional/iSqlFunctional.js @@ -0,0 +1,420 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + + +const { expect } = require('chai'); +const { iSql, xmlToJson } = require('../../lib/itoolkit'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iSql Functional Tests', () => { + describe('prepare & execute', () => { + transports.forEach((transport) => { + it(`prepares & executes stored procedure then fetch results using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const sql = new iSql(); + + sql.prepare('call qsys2.tcpip_info()'); + sql.execute(); + sql.fetch(); + sql.free(); + connection.add(sql); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('addQuery & fetch', () => { + transports.forEach((transport) => { + it(`runs a query and fetches results using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const sql = new iSql(); + + sql.addQuery('SELECT LSTNAM, STATE FROM QIWS.QCUSTCDT'); + sql.fetch(); + sql.free(); + connection.add(sql); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('added test to ensure issue #11 was resolved', () => { + transports.forEach((transport) => { + it(`should parse SQL result set empty data tags correctly using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const sql = new iSql(); + + sql.addQuery('SELECT \'\' AS BLANK, STATE FROM QIWS.QCUSTCDT'); + sql.fetch(); + sql.free(); + connection.add(sql); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0].value).to.equal(''); + }); + }); + done(); + }); + }); + }); + }); + + describe('tables', () => { + transports.forEach((transport) => { + it(`returns meta data for specified table using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const sql = new iSql(); + // [catalog, schema, table, table type] + sql.tables(['', 'QIWS', 'QCUSTCDT', '']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('tablePriv', () => { + transports.forEach((transport) => { + it('returns privilege data for a table', (done) => { + const connection = transport.me; + + const sql = new iSql(); + // [catalog, schema, table] + sql.tablePriv(['', 'QIWS', 'QCUSTCDT']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('columns', () => { + transports.forEach((transport) => { + it('returns meta data for a column', (done) => { + const connection = transport.me; + + const sql = new iSql(); + // catalog, schema, table, column + sql.columns(['', 'QIWS', 'QCUSTCDT', 'CUSNUM']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.be.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('columnPriv', () => { + transports.forEach((transport) => { + it('returns privilege data for a column', (done) => { + const connection = transport.me; + + const sql = new iSql(); + + sql.columnPriv(['', 'QIWS', 'QCUSTCDT', 'BALDUE']); + + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('procedures', () => { + transports.forEach((transport) => { + it('returns meta data on for a procedure', (done) => { + const connection = transport.me; + + const sql = new iSql(); + // [catalog, schema, procedure] + sql.procedures(['', 'QSYS2', 'TCPIP_INFO']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('pColumns', () => { + transports.forEach((transport) => { + it('returns meta data for procedure column', (done) => { + const connection = transport.me; + + const sql = new iSql(); + // [catalog, schema, procedure, column] + sql.pColumns(['', 'QSYS2', 'QCMDEXC', 'COMMAND']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('primaryKeys', () => { + transports.forEach((transport) => { + it(`returns meta data for a primary key using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const sql = new iSql(); + // [catalog, schema, table] + sql.primaryKeys(['', 'QUSRSYS', 'QASZRAIRX']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('foreignKeys', () => { + transports.forEach((transport) => { + it('returns meta data for a foreign key', (done) => { + const connection = transport.me; + + const sql = new iSql(); + // pk: [catalog, schema, table] + // fk: [catalog, schema, table] + sql.foreignKeys(['', 'QUSRSYS', 'QASZRAIRC', '', 'QUSRSYS', 'QASZRAIRX']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe('statistics', () => { + transports.forEach((transport) => { + it('returns stats info for table', (done) => { + const connection = transport.me; + + const sql = new iSql(); + + sql.statistics(['', 'QIWS', 'QCUSTCDT', 'all']); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.result).to.be.an('array'); + result.result.forEach((row) => { + expect(row[0]).to.be.an('object'); + expect(row[0]).haveOwnProperty('desc'); + expect(row[0]).haveOwnProperty('value'); + }); + }); + done(); + }); + }); + }); + }); + + describe.skip('special', () => { + // TODO: find passing case + // Below test fails with error code 9- argument value not valid + transports.forEach((transport) => { + it.skip(`returns meta data for special columns using ${transport.name} transport`, (done) => { + // [catalog, schema, table, row | transaction |session, no | nullable] + const connection = transport.me; + + const sql = new iSql(); + + sql.special(['', 'QUSRSYS', 'QASZRAIRX', 'row', 'no'], { error: 'on' }); + connection.add(sql.toXML()); + connection.debug(true); + connection.run((xmlOut) => { + // eslint-disable-next-line no-console + console.log(`xml output: \n ${xmlOut}`); + const results = xmlToJson(xmlOut); + // eslint-disable-next-line no-console + console.log(JSON.stringify(results)); + done(); + }); + }); + }); + }); + + describe.skip('rowCount', () => { + // xmlToJson does not add check for row count currently + // Skip for now until this is added. + transports.forEach((transport) => { + it.skip(`returns the number of rows affected by statement using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const sql = new iSql(); + + const insert = 'INSERT INTO QIWS.QCUSTCDT (CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) ' + + 'VALUES (8798,\'TURNER\',\'TT\',\'MAIN\',\'NYC\',\'NY\',10001, 500, 3, 40.00, 0.00) with NONE'; + + sql.addQuery(insert); + sql.rowCount(); + sql.free(); + connection.add(sql.toXML()); + connection.run((xmlOut) => { + const results = xmlToJson(xmlOut); + done(); + results.forEach((result) => { + expect(result.success).to.equal(true); + expect(result.rowCount).to.equal(1); + }); + }); + }); + }); + }); +}); diff --git a/test/functional/iUserSpaceFunctional.js b/test/functional/iUserSpaceFunctional.js new file mode 100644 index 00000000..5f550e7c --- /dev/null +++ b/test/functional/iUserSpaceFunctional.js @@ -0,0 +1,127 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iConn } = require('../../lib/itoolkit'); +const { iUserSpace } = require('../../lib/iuserSpace'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const lib = 'NODETKTEST'; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iUserSpace Functional Tests', () => { + describe('constructor', () => { + it('returns an instance of iUserSpace', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const userSpace = new iUserSpace(connection); + + expect(userSpace).to.be.instanceOf(iUserSpace); + }); + }); + + describe('createUserSpace', () => { + transports.forEach((transport) => { + it(`creates a user space using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const userSpace = new iUserSpace(connection); + + const description = 'Node toolkit test user space'; + + const userSpaceName = `USP${(transport.name).toUpperCase()}`; + + userSpace.createUserSpace(userSpaceName, lib, 'LOG', 50, '*EXCLUDE', + description, (output) => { + expect(output).to.be.a('boolean').and.to.equal(true); + done(); + }); + }); + }); + }); + + describe('setUserSpaceData', () => { + transports.forEach((transport) => { + it(`sets data within the user space using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const userSpace = new iUserSpace(connection); + + const msg = 'Hello from userspace!'; + + const userSpaceName = `USP${(transport.name).toUpperCase()}`; + + userSpace.setUserSpaceData(userSpaceName, lib, msg.length, msg, + (output) => { + expect(output).to.be.a('boolean').and.to.equal(true); + done(); + }); + }); + }); + }); + + describe('getUserSpaceData', () => { + transports.forEach((transport) => { + it(`returns specified length of data using ${transport.name} transport`, + (done) => { + const connection = transport.me; + + const userSpace = new iUserSpace(connection); + + const userSpaceName = `USP${(transport.name).toUpperCase()}`; + + userSpace.getUserSpaceData(userSpaceName, lib, 21, (output) => { + expect(output).to.be.a('string').and.to.equal('Hello from userspace!'); + done(); + }); + }); + }); + }); + + describe('deleteUserSpace', () => { + transports.forEach((transport) => { + it(`removes a user space using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const userSpace = new iUserSpace(connection); + + const userSpaceName = `USP${(transport.name).toUpperCase()}`; + + userSpace.deleteUserSpace(userSpaceName, lib, (output) => { + expect(output).to.be.a('boolean').and.to.equal(true); + done(); + }); + }); + }); + }); +}); diff --git a/test/functional/iWorkFunctional.js b/test/functional/iWorkFunctional.js new file mode 100644 index 00000000..70acd96e --- /dev/null +++ b/test/functional/iWorkFunctional.js @@ -0,0 +1,275 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iConn } = require('../../lib/itoolkit'); +const { iWork } = require('../../lib/iwork'); + +// Set Env variables or set values here. +const opt = { + database: process.env.TKDB || '*LOCAL', + user: process.env.TKUSER || '', + password: process.env.TKPASS || '', + host: process.env.TKHOST || 'localhost', + port: process.env.TKPORT || 80, + path: process.env.TKPATH || '/cgi-bin/xmlcgi.pgm', +}; + +const { returnTransports } = require('../../lib/utils'); + +const transports = returnTransports(opt); + +describe('iWork Functional Tests', () => { + describe('constructor', () => { + it('creates and returns an instance of iWork', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const work = new iWork(connection); + + expect(work).to.be.instanceOf(iWork); + }); + }); + + describe('getSysValue', () => { + transports.forEach((transport) => { + it(`returns the value of system variable using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const work = new iWork(connection); + + work.getSysValue('QCENTURY', (output) => { + expect(output).to.be.a('string').and.to.equal('1'); + done(); + }); + }); + }); + }); + + describe('getSysStatus', () => { + transports.forEach((transport) => { + it('returns basic system status information about the signed-on users ' + + `and batch jobs using ${transport.name} transport`, + (done) => { + const connection = transport.me; + + const work = new iWork(connection); + + work.getSysStatus((output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Current_date_and_time'); + expect(output).to.have.a.property('System_name'); + expect(output).to.have.a.property('Users_currently_signed_on'); + expect(output).to.have.a.property('Users_temporarily_signed_off_(disconnected)'); + expect(output).to.have.a.property('Users_suspended_by_system_request'); + expect(output).to.have.a.property('Users_suspended_by_group_jobs'); + expect(output).to.have.a.property('Users_signed_off_with_printer_output_waiting_to_print'); + expect(output).to.have.a.property('Batch_jobs_waiting_for_messages'); + expect(output).to.have.a.property('Batch_jobs_running'); + expect(output).to.have.a.property('Batch_jobs_held_while_running'); + expect(output).to.have.a.property('Batch_jobs_ending'); + expect(output).to.have.a.property('Batch_jobs_waiting_to_run_or_already_scheduled'); + expect(output).to.have.a.property('Batch_jobs_held_on_a_job_queue'); + expect(output).to.have.a.property('Batch_jobs_on_a_held_job_queue'); + expect(output).to.have.a.property('Batch_jobs_on_an_unassigned_job_queue'); + expect(output).to.have.a.property('Batch_jobs_ended_with_printer_output_waiting_to_print'); + done(); + }); + }); + }); + }); + + describe('getSysStatusExt', () => { + transports.forEach((transport) => { + it(`returns more detailed system status info using ${transport.name} transport`, + (done) => { + const connection = transport.me; + + const work = new iWork(connection); + + work.getSysStatusExt((output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Current_date_and_time'); + expect(output).to.have.a.property('System_name'); + expect(output).to.have.a.property('Elapsed_time'); + expect(output).to.have.a.property('Restricted_state_flag'); + expect(output).to.have.a.property('%_processing_unit_used'); + expect(output).to.have.a.property('Jobs_in_system'); + expect(output).to.have.a.property('%_permanent_addresses'); + expect(output).to.have.a.property('%_temporary_addresses'); + expect(output).to.have.a.property('System_ASP'); + expect(output).to.have.a.property('%_system_ASP_used'); + expect(output).to.have.a.property('Total_auxiliary_storage'); + expect(output).to.have.a.property('Current_unprotected_storage_used'); + expect(output).to.have.a.property('Maximum_unprotected_storage_used'); + expect(output).to.have.a.property('%_DB_capability'); + expect(output).to.have.a.property('Main_storage_size'); + expect(output).to.have.a.property('Number_of_partitions'); + expect(output).to.have.a.property('Partition_identifier'); + expect(output).to.have.a.property('Current_processing_capacity'); + expect(output).to.have.a.property('Processor_sharing_attribute'); + expect(output).to.have.a.property('Number_of_processors'); + expect(output).to.have.a.property('Active_jobs_in_system'); + expect(output).to.have.a.property('Active_threads_in_system'); + expect(output).to.have.a.property('Maximum_jobs_in_system'); + expect(output).to.have.a.property('%_temporary_256MB_segments_used'); + expect(output).to.have.a.property('%_temporary_4GB_segments_used'); + expect(output).to.have.a.property('%_permanent_256MB_segments_used'); + expect(output).to.have.a.property('%_permanent_4GB_segments_used'); + expect(output).to.have.a.property('%_current_interactive_performance'); + expect(output).to.have.a.property('%_uncapped_CPU_capacity_used'); + expect(output).to.have.a.property('%_shared_processor_pool_used'); + expect(output).to.have.a.property('Main_storage_size_(long)'); + done(); + }); + }); + }); + }); + + describe('getJobStatus', () => { + transports.forEach((transport) => { + it(`returns status of specified job using ${transport.name} transport`, + (done) => { + const connection = transport.me; + + const work = new iWork(connection); + + work.getJobStatus('000000', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Job_status'); + expect(output).to.have.a.property('Fully_qualified_job_name'); + done(); + }); + }); + }); + }); + + describe('getJobInfo', () => { + transports.forEach((transport) => { + it(`returns info on specfed job using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const work = new iWork(connection); + + work.getJobInfo('SCPF', 'QSYS', '000000', (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Job_name'); + expect(output).to.have.a.property('User_name'); + expect(output).to.have.a.property('Job_number'); + expect(output).to.have.a.property('Job_status'); + expect(output).to.have.a.property('Job_type'); + expect(output).to.have.a.property('Job_subtype'); + expect(output).to.have.a.property('Subsystem_description_name'); + expect(output).to.have.a.property('Run_priority_(job)'); + expect(output).to.have.a.property('System_pool_identifier'); + expect(output).to.have.a.property('Processing_unit_time_used,_if_less_than_2,147,483,647_milliseconds'); + expect(output).to.have.a.property('Number_of_auxiliary_I/O_requests,_if_less_than_2,147,483,647'); + expect(output).to.have.a.property('Number_of_interactive_transactions'); + expect(output).to.have.a.property('Response_time_total'); + expect(output).to.have.a.property('Function_type'); + expect(output).to.have.a.property('Function_name'); + expect(output).to.have.a.property('Active_job_status'); + expect(output).to.have.a.property('Number_of_database_lock_waits'); + expect(output).to.have.a.property('Number_of_internal_machine_lock_waits'); + expect(output).to.have.a.property('Number_of_nondatabase_lock_waits'); + expect(output).to.have.a.property('Time_spent_on_database_lock_waits'); + expect(output).to.have.a.property('Time_spent_on_internal_machine_lock_waits'); + expect(output).to.have.a.property('Time_spent_on_nondatabase_lock_waits'); + expect(output).to.have.a.property('Current_system_pool_identifier'); + expect(output).to.have.a.property('Thread_count'); + expect(output).to.have.a.property('Processing_unit_time_used_-_total_for_the_job'); + expect(output).to.have.a.property('Number_of_auxiliary_I/O_requests'); + expect(output).to.have.a.property('Processing_unit_time_used_for_database_-_total_for_the_job'); + expect(output).to.have.a.property('Page_faults'); + expect(output).to.have.a.property('Active_job_status_for_jobs_ending'); + expect(output).to.have.a.property('Memory_pool_name'); + expect(output).to.have.a.property('Message_reply'); + expect(output).to.have.a.property('Message_key,_when_active_job_waiting_for_a_message'); + expect(output).to.have.a.property('Message_queue_name,_when_active_job_waiting_for_a_message'); + expect(output).to.have.a.property('Message_queue_library_name,_when_active_job_waiting_for_a_message'); + expect(output).to.have.a.property('Message_queue_library_ASP_device_name,_when_active_job_waiting_for_a_message'); + done(); + }); + }); + }); + }); + + describe('getDataArea', () => { + before('init lib, data area, and add data', async () => { + // eslint-disable-next-line global-require + const { DBPool } = require('idb-pconnector'); + const pool = new DBPool({ url: '*LOCAL' }, { incrementSize: 2 }); + + const qcmdexec = 'CALL QSYS2.QCMDEXC(?)'; + const lib = 'NODETKTEST'; + const dataArea = 'TESTDA'; + + const createLib = `CRTLIB LIB(${lib}) TYPE(*TEST) TEXT('Used to test' Node.js toolkit')`; + + const createDataArea = `CRTDTAARA DTAARA(${lib}/${dataArea}) TYPE(*CHAR) ` + + 'TEXT(\'TEST DATA AREA FOR NODE TOOLKIT\') ' + + 'VALUE(\'Hello From Test Data Area!\')'; + + const findLib = 'SELECT SCHEMA_NAME FROM qsys2.sysschemas WHERE SCHEMA_NAME = \'NODETKTEST\''; + + const findDataArea = `SELECT OBJNAME FROM TABLE (QSYS2.OBJECT_STATISTICS('${lib}', '*DTAARA')) AS X`; + + const libResult = await pool.runSql(findLib); + + const dataAreaResult = await pool.runSql(findDataArea); + + if (!libResult.length) { + await pool.prepareExecute(qcmdexec, [createLib]).catch((error) => { + // eslint-disable-next-line no-console + console.log('Unable to Create Lib!'); + throw error; + }); + // eslint-disable-next-line no-console + console.log('CREATED LIB!'); + } + if (!dataAreaResult.length) { + await pool.prepareExecute(qcmdexec, [createDataArea]).catch((error) => { + // eslint-disable-next-line no-console + console.log('Unable to Create DA!'); + throw error; + }); + // eslint-disable-next-line no-console + console.log('CREATED DA!'); + } + }); + transports.forEach((transport) => { + it(`returns contents of a data area using ${transport.name} transport`, (done) => { + const connection = transport.me; + + const work = new iWork(connection); + + work.getDataArea('AMUSSE', 'TESTDA', 20, (output) => { + expect(output).to.be.an('Object'); + expect(output).to.have.a.property('Type_of_value_returned'); + expect(output).to.have.a.property('Library_name'); + expect(output).to.have.a.property('Length_of_value_returned'); + expect(output).to.have.a.property('Number_of_decimal_positions'); + expect(output).to.have.a.property('Value'); + done(); + }); + }); + }); + }); +}); diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 27aec8e8..00000000 --- a/test/test.js +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) International Business Machines Corp. 2017 -// All Rights Reserved - -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -const assert = require('assert'); -const xt = require('../lib/itoolkit'); -const hint = 'check the "success" property in return value' -//Need change based on your server configurations -const opt = { - db : '*LOCAL', - user : 'YOURNAME', - pwd : 'PASSWORD', - host : '0.0.0.0', - port : 8080, - path : '/cgi-bin/xmlcgi.pgm' -}; - -describe('Basic Function Test', () => { - describe('Test iCmd()', () => { - it(hint, (done) => { - let conn = new xt.iConn(opt.db); - conn.add(xt.iCmd('RTVJOBA USRLIBL(?) SYSLIBL(?)')); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success = true; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success = result.success == true; - }); - if(success) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - }); - - describe('Test iSh()', () => { - it(hint, (done) => { - let conn = new xt.iConn(opt.db); - conn.add(xt.iSh('system -i wrksyssts')); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success = true; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success = result.success == true; - }); - if(success) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - }); - - describe('Test iQsh()', () => { - it(hint, (done) => { - let conn = new xt.iConn(opt.db); - conn.add(xt.iQsh('system wrksyssts')); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success = true; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success = result.success == true; - }); - if(success) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - }); - - describe('Test iPgm()', () => { - it(hint, (done) => { - let conn = new xt.iConn(opt.db); - let pgm = new xt.iPgm('QWCRSVAL', {'lib':'QSYS'}); - let outBuf = [ - [0, '10i0'], - [0, '10i0'], - ['', '36h'], - ['', '10A'], - ['', '1A'], - ['', '1A'], - [0, '10i0'], - [0, '10i0'] - ]; - pgm.addParam(outBuf, {'io':'out'}); - pgm.addParam(66, '10i0'); - pgm.addParam(1, '10i0'); - pgm.addParam('QCCSID', '10A'); - pgm.addParam(this.errno, {'io':'both', 'len' : 'rec2'}); - conn.add(pgm); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success = false; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success = result.success == true; - }); - if(success) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - it('Should return arbitrarily named parameter', (done) => { - let conn = new xt.iConn(opt.db); - let pgm = new xt.iPgm('QWCRSVAL', {'lib':'QSYS'}); - let outBuf = [ - [0, '10i0'], - [0, '10i0'], - ['', '36h'], - ['', '10A'], - ['', '1A'], - ['', '1A'], - [0, '10i0'], - [0, '10i0'] - ]; - pgm.addParam(outBuf, {'io':'out'}); - pgm.addParam(66, '10i0'); - pgm.addParam(1, '10i0'); - pgm.addParam('QCCSID', '10A'); - let paramValue = 'errno'; - pgm.addParam(this.errno, {'io':'both', 'len' : 'rec2', 'name' : paramValue }); - conn.add(pgm); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success = false; - results.every((result, i) => { - if(result.data[11].hasOwnProperty('name')) - success = result.data[11].name == paramValue; - }); - if(success) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - /*** Refer to test/rpg/zzsrv6.rpgle - it('Should return success with addReturn arbitrary attribute specified', (done) => { - let conn = new xt.iConn(opt.db); - let pgm = new xt.iPgm("ZZSRV6", {"lib":"XMLSERVICE", "func":"ZZVARY4"}); - pgm.addParam("Gill", "10A", {"letying":"4"}); - let test_value = "NEW_NAME"; - pgm.addReturn("0", "20A", {"letying":"4","name":test_value}); - conn.add(pgm); - conn.run((str) => { - let results = xt.xmlToJson(str); - if(results[0].data[1].name == test_value) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - ***/ - }); - - describe('Test iSql()', () => { - it(hint, (done) => { - let conn = new xt.iConn(opt.db); - let sql = new xt.iSql(); /* Test iSql Class */ - sql.prepare('call qsys2.tcpip_info()'); - sql.execute(); - sql.fetch(); - sql.free(); - conn.add(sql); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success = true; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success = result.success == true; - }); - if(success) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - - it('should return SQL result set', (done) => { - let conn = new xt.iConn(opt.db); - let sql = new xt.iSql(); - sql.addQuery("SELECT LSTNAM, STATE FROM QIWS.QCUSTCDT"); - sql.fetch(); - sql.free(); - conn.add(sql); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success1 = false; - let success2 = false; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success1 = result.success == true; - result.result.every((row, i) => { - success2 = row[0].hasOwnProperty('desc'); - }) - }); - if(success1 && success2) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - - it('should parse SQL result set empty data tags correctly', (done) => { - let conn = new xt.iConn(opt.db); - let sql = new xt.iSql(); - sql.addQuery("SELECT '' AS BLANK, STATE FROM QIWS.QCUSTCDT"); - sql.fetch(); - sql.free(); - conn.add(sql); - conn.run((str) => { - let results = xt.xmlToJson(str); - let success1 = false; - let success2 = false; - results.every((result, i) => { - if(result.hasOwnProperty('success')) - success1 = result.success == true; - result.result.every((row, i) => { - success2 = row[0].value === ''; - }) - }); - if(success1 && success2) done(); - else done(new Error(JSON.stringify(results))); - }); - }); - }); -}); \ No newline at end of file diff --git a/test/unit/commandsUnit.js b/test/unit/commandsUnit.js new file mode 100644 index 00000000..31b6c35c --- /dev/null +++ b/test/unit/commandsUnit.js @@ -0,0 +1,90 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +const { expect } = require('chai'); +const { iSh, iQsh, iCmd } = require('../../lib/itoolkit'); + +describe('iSh, iCmd, iQsh, Unit Tests', () => { + describe('iSh function', () => { + it('accepts command input and returns XML output', () => { + const sh = iSh('ls -lah'); + + const expectedXML = 'ls -lah'; + + expect(sh).to.be.a('string').and.to.equal(expectedXML); + }); + + it('accepts command input and options returns with optional attributes', () => { + const options = { + error: 'on', before: '65535', after: '37', rows: 'on', + }; + + const sh = iSh('ls -lah', options); + + const expectedXML = 'ls -lah'; + + expect(sh).to.be.a('string').and.to.equal(expectedXML); + }); + }); + + describe('iCmd function', () => { + it('accepts command input and returns XML output', () => { + const cmd = iCmd('RTVJOBA USRLIBL(?) SYSLIBL(?)'); + + const expectedXML = 'RTVJOBA USRLIBL(?) SYSLIBL(?)'; + + expect(cmd).to.be.a('string').and.to.equal(expectedXML); + }); + + it('accepts command input and options returns with optional attributes', () => { + const options = { + exec: 'cmd', error: 'on', before: '65535', after: '37', hex: 'on', + }; + + const cmd = iCmd('RTVJOBA USRLIBL(?) SYSLIBL(?)', options); + + const expectedXML = 'RTVJOBA USRLIBL(?) SYSLIBL(?)'; + + expect(cmd).to.be.a('string').and.to.equal(expectedXML); + }); + }); + + describe('iQsh function', () => { + it('accepts command input and returns XML output', () => { + const qsh = iQsh('RTVJOBA USRLIBL(?) SYSLIBL(?)'); + + const expectedXML = 'RTVJOBA USRLIBL(?) SYSLIBL(?)'; + + expect(qsh).to.be.a('string').and.to.equal(expectedXML); + }); + + it('accepts command input and options returns with optional attributes', () => { + const options = { + exec: 'cmd', error: 'on', before: '65535', after: '37', hex: 'on', rows: 'on', + }; + const qsh = iCmd('RTVJOBA USRLIBL(?) SYSLIBL(?)', options); + + const expectedXML = 'RTVJOBA USRLIBL(?) SYSLIBL(?)'; + + expect(qsh).to.be.a('string').and.to.equal(expectedXML); + }); + }); +}); diff --git a/test/unit/iConnUnit.js b/test/unit/iConnUnit.js new file mode 100644 index 00000000..0062b9d6 --- /dev/null +++ b/test/unit/iConnUnit.js @@ -0,0 +1,172 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const sinon = require('sinon'); +const { iConn, iSh } = require('../../lib/itoolkit'); + +// const iNetwork = require('../lib/inetwork') +// Need change based on your server configurations +/* const opt = { + db : '*LOCAL', + user : 'YOURNAME', + pwd : 'PASSWORD', + host : '0.0.0.0', + port : 8080, + path : '/cgi-bin/xmlcgi.pgm' +}; + */ +const opt = { + database: '*LOCAL', + user: process.env.USERID, + password: process.env.PASSWD, + host: 'lp13ut28.rch.stglabs.ibm.com', + port: 80, + path: '/cgi-bin/xmlcgi.pgm', +}; + + +describe('iConn Class Unit Tests', () => { + describe('constructor', () => { + it('creates and returns an instance of iConn with idb transport', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + expect(connection).to.be.instanceOf(iConn); + expect(connection.conn).to.be.an('Object'); + expect(connection.conn.I_TRANSPORT_DB2_DATABASE).to.equal(opt.database); + expect(connection.conn.I_TRANSPORT_DB2_USER).to.equal(opt.user); + expect(connection.conn.I_TRANSPORT_DB2_PASSWORD).to.equal(opt.password); + expect(connection.conn.I_TRANSPORT).to.equal('DB2'); + expect(connection.conn.I_TRANSPORT_CTL).to.equal('*here'); + expect(connection.conn.I_TRANSPORT_IPC).to.equal('*NA'); + expect(connection.conn.I_XML_SERVICE_LIB).to.equal('QXMLSERV'); + expect(connection.cmds).to.be.an('Array'); + expect(connection.cmds.length).to.equal(0); + expect(connection.timeout).to.equal(5000); + expect(connection.I_DEBUG_VERBOSE).to.equal(false); + }); + + it('creates and returns an instance of iConn with rest transport', () => { + const connection = new iConn(opt.database, opt.user, opt.password, opt); + + expect(connection).to.be.instanceOf(iConn); + expect(connection.conn).to.be.an('Object'); + expect(connection.conn.I_TRANSPORT_DB2_DATABASE).to.equal(opt.database); + expect(connection.conn.I_TRANSPORT_DB2_USER).to.equal(opt.user); + expect(connection.conn.I_TRANSPORT_DB2_PASSWORD).to.equal(opt.password); + expect(connection.conn.I_TRANSPORT).to.equal('REST'); + expect(connection.conn.I_TRANSPORT_CTL).to.equal('*here'); + expect(connection.conn.I_TRANSPORT_IPC).to.equal('*NA'); + expect(connection.conn.I_XML_SERVICE_LIB).to.equal('QXMLSERV'); + expect(connection.cmds).to.be.an('Array'); + expect(connection.cmds.length).to.equal(0); + expect(connection.timeout).to.equal(5000); + expect(connection.I_DEBUG_VERBOSE).to.equal(false); + }); + }); + + describe('add', () => { + it('appends to xml service request to the command list', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + connection.add(iSh('ls -lah')); + expect(connection.cmds.length).to.equal(1); + expect(connection.cmds[0]).to.equal('ls -lah'); + }); + }); + + + describe('debug', () => { + it('turns verbose mode on/off', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + connection.debug(true); + expect(connection.debug()).to.equal(true); + connection.debug(false); + expect(connection.debug()).to.equal(false); + }); + }); + + + describe('getConnection', () => { + it('returns conn (object) property from iConn instance', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + const returned = connection.getConnection(); + + expect(returned).to.be.an('Object'); + expect(returned.I_TRANSPORT_DB2_DATABASE).to.equal(opt.database); + expect(returned.I_TRANSPORT_DB2_USER).to.equal(opt.user); + expect(returned.I_TRANSPORT_DB2_PASSWORD).to.equal(opt.password); + expect(returned.I_TRANSPORT).to.equal('DB2'); + expect(returned.I_TRANSPORT_CTL).to.equal('*here'); + expect(returned.I_TRANSPORT_IPC).to.equal('*NA'); + expect(returned.I_XML_SERVICE_LIB).to.equal('QXMLSERV'); + }); + }); + + + describe('run', () => { + it('invokes transport to execute xml input and returns xml output in callback', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + const xmlOut = ` + + + + + HenningTX + JonesNY + VineVT + JohnsonGA + TyronNY + StevensCO + AlisonMN + DoeCA + ThomasWY + WilliamsTX + LeeNY + AbrahamMN + +++ success stmt1 + + +++ success + + + `; + + sinon.stub(connection, 'run').yields(xmlOut); + + connection.run((result) => { + expect(result).to.equal(xmlOut); + }); + }); + }); + + + describe('setTimeout', () => { + it('override timeout for sync mode', () => { + const connection = new iConn(opt.database, opt.user, opt.password); + + expect(connection.timeout).to.equal(5000); + connection.setTimeout(3); + expect(connection.timeout).to.equal(3000); + }); + }); +}); diff --git a/test/unit/iPgmUnit.js b/test/unit/iPgmUnit.js new file mode 100644 index 00000000..81d534b6 --- /dev/null +++ b/test/unit/iPgmUnit.js @@ -0,0 +1,156 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iPgm } = require('../../lib/itoolkit'); + +const outBuf = [ + [0, '10i0'], + [0, '10i0'], + ['', '36h'], + ['', '10A'], + ['', '1A'], + ['', '1A'], + [0, '10i0'], + [0, '10i0'], +]; +const errno = [ + [0, '10i0'], + [0, '10i0', { setlen: 'rec2' }], + ['', '7A'], + ['', '1A'], +]; + +describe('iPgm Class Unit Tests', () => { + describe('constructor', () => { + it('creates and returns an instance of iPgm with lib and function set', () => { + const pgm = new iPgm('QTOCNETSTS'); + expect(pgm).to.be.instanceOf(iPgm); + }); + }); + + describe('toXML', () => { + it('returns pgm XML', () => { + const pgm = new iPgm('QTOCNETSTS', + { + lib: 'QSYS', + func: 'QtoRtvTCPA', + error: 'on', + }); + + const expectedXML = ''; + + expect(pgm.toXML()).to.be.a('string').and.to.equal(expectedXML); + }); + }); + + describe('addParam', () => { + it('appends param to pgm xml', () => { + const pgm = new iPgm('QTOCNETSTS', + { + lib: 'QSYS', + func: 'QtoRtvTCPA', + error: 'fast', + }); + + pgm.addParam(outBuf, { io: 'out' }); + + let expectedXML = '' + + '0' + + '0' + + '' + + '00'; + + expect(pgm.toXML()).to.equal(expectedXML); + + pgm.addParam(66, '10i0'); + + expectedXML = '' + + '0' + + '0' + + '' + + '00' + + '66'; + + expect(pgm.toXML()).to.equal(expectedXML); + + pgm.addParam(1, '10i0'); + + expectedXML = '' + + '0' + + '0' + + '' + + '00' + + '66' + + '1'; + + expect(pgm.toXML()).to.equal(expectedXML); + + pgm.addParam('QCCSID', '10A'); + + expectedXML = '' + + '0' + + '0' + + '' + + '00' + + '66' + + '1' + + 'QCCSID'; + + expect(pgm.toXML()).to.equal(expectedXML); + + pgm.addParam(errno, { io: 'both', len: 'rec2' }); + + expectedXML = '' + + '0' + + '0' + + '' + + '00' + + '66' + + '1' + + 'QCCSID' + + '0' + + '0' + + ''; + + expect(pgm.toXML()).to.equal(expectedXML); + }); + }); + + + describe('addReturn', () => { + it('appends return to pgm xml', () => { + const pgm = new iPgm('QTOCNETSTS', + { + lib: 'QSYS', + func: 'QtoRtvTCPA', + error: 'fast', + }); + + pgm.addReturn('0', '20A'); + + const expectedXML = '0'; + + expect(pgm.toXML()).to.equal(expectedXML); + }); + }); +}); diff --git a/test/unit/iSqlUnit.js b/test/unit/iSqlUnit.js new file mode 100644 index 00000000..75ad7217 --- /dev/null +++ b/test/unit/iSqlUnit.js @@ -0,0 +1,260 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ +/* eslint-disable new-cap */ + +const { expect } = require('chai'); +const { iSql } = require('../../lib/itoolkit'); + +describe('iSql Class Unit Tests', () => { + describe('constructor', () => { + it('creates returns an instance of iSql', () => { + const sql = new iSql(); + + expect(sql).to.be.instanceOf(iSql); + }); + }); + describe('toXML', () => { + it('returns current sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('addQuery', () => { + it('appends query to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'select * from QIWS.QCUSTCDT'; + + sql.addQuery('select * from QIWS.QCUSTCDT', { error: 'on' }); + + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('fetch', () => { + it('appends fetch to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + + sql.fetch(); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('commit', () => { + it('appends commit to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + + sql.commit({ action: 'commit' }); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('prepare', () => { + it('appends prepare to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; + + sql.prepare('SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + + describe('execute', () => { + it('appends execute to sql XML', () => { + const sql = new iSql(); + + const expectedXML = '30'; + + sql.execute([[30, 'in']]); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('connect', () => { + it('appends connect to sql XML', () => { + const sql = new iSql(); + + + const expectedXML = ''; + + sql.connect({ db: 'local', uid: 'me', pwd: 'mypass' }); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('setOptions', () => { + it('appends options to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + + sql.setOptions([{ desc: 'autocommit', value: 'on' }, + { desc: 'naming', value: 'system' }]); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('tables', () => { + it('appends tables to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QIWS'; + // catalog, schema, table, table type + sql.tables(['', 'QIWS', '', '']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('tablePriv', () => { + it('appends tablepriv to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QIWSQCUSTCDT'; + // catalog, schema, table + sql.tablePriv(['', 'QIWS', 'QCUSTCDT']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('columns', () => { + it('appends columns to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QIWSQCUSTCDT'; + // catalog, schema, table, column + sql.columns(['', 'QIWS', 'QCUSTCDT', '']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('columnPriv', () => { + it('appends columnpriv to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QIWSQCUSTCDT'; + // catalog, schema, table, column + sql.columnPriv(['', 'QIWS', 'QCUSTCDT', '']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('procedures', () => { + it('appends procedures to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QSYS2TCPIP_INFO'; + // catalog, schema, procedure + sql.procedures(['', 'QSYS2', 'TCPIP_INFO']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('pColumns', () => { + it('appends pColumns to sql XML', () => { + // procedure columns: + + const sql = new iSql(); + + const expectedXML = 'QSYS2QCMDEXCCOMMAND'; + // catalog, schema, procedure, column + sql.pColumns(['', 'QSYS2', 'QCMDEXC', 'COMMAND']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('primaryKeys', () => { + it('appends primarykeys to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QUSRSYSQASZRAIRX'; + // catalog, schema, table + sql.primaryKeys(['', 'QUSRSYS', 'QASZRAIRX']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('foreignKeys', () => { + it('appends foreignkeys to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QUSRSYSQASZRAIRCQUSRSYSQASZRAIRX'; + + // pk: catalog, schema, table + // fk: catalog, schema, table + sql.foreignKeys(['', 'QUSRSYS', 'QASZRAIRC', '', 'QUSRSYS', 'QASZRAIRX']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('statistics', () => { + it('appends statistics to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QIWSQCUSTCDTall'; + // catalog, schema, table, all | unique + sql.statistics(['', 'QIWS', 'QCUSTCDT', 'all']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('special', () => { + it('appends special to sql XML', () => { + const sql = new iSql(); + + const expectedXML = 'QIWSQCUSTCDTrowno'; + // catalog, schema, table, row | transaction | session, no | unique + sql.special(['', 'QIWS', 'QCUSTCDT', 'row', 'no']); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('count', () => { + it('appends count to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + // catalog, schema, table, all | unique + sql.count({ desc: 'both' }); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('rowCount', () => { + it('appends rowcount to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + // catalog, schema, table, all | unique + sql.rowCount(); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('free', () => { + it('appends free to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + // catalog, schema, table, all | unique + sql.free(); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); + describe('describe', () => { + it('appends describe to sql XML', () => { + const sql = new iSql(); + + const expectedXML = ''; + + sql.describe({ desc: 'both' }); + expect(sql.toXML()).to.equal(expectedXML); + }); + }); +}); diff --git a/test/unit/xmlToJsonUnit.js b/test/unit/xmlToJsonUnit.js new file mode 100644 index 00000000..baa9f224 --- /dev/null +++ b/test/unit/xmlToJsonUnit.js @@ -0,0 +1,204 @@ +// Copyright (c) International Business Machines Corp. 2019 +// All Rights Reserved + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-env mocha */ + +const { expect } = require('chai'); +const { xmlToJson } = require('../../lib/itoolkit'); + +describe('xmlToJson Tests', () => { + it('converts CL command XML output to js object', () => { + const xmlOut = '' + + '+++ success RTVJOBA USRLIBL(?) SYSLIBL(?)' + + 'QGPL QTEMP QDEVELOP QBLDSYS' + + ' QBLDSYSRQSYS' + + ' QSYS2 QHLPSYS QUSRSYS'; + + const result = xmlToJson(xmlOut); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0]).to.be.an('object'); + expect(result[0]).to.haveOwnProperty('type').and.to.equal('cmd'); + const cmd = 'RTVJOBA USRLIBL(?) SYSLIBL(?)'; + expect(result[0]).to.haveOwnProperty('cmd').and.to.equal(cmd); + expect(result[0]).to.haveOwnProperty('data'); + expect(result[0].data).to.be.an('array'); + expect(result[0].data.length).to.equal(2); + expect(result[0].data[0]).to.be.an('object'); + const name = 'USRLIBL'; + expect(result[0].data[0]).to.haveOwnProperty('name').and.to.equal(name); + const value = 'QGPL QTEMP QDEVELOP QBLDSYS QBLDSYSR'; + expect(result[0].data[0]).to.haveOwnProperty('value').and.to.equal(value); + }); + + it('converts sh command XML output to js object', () => { + const xmlOut = '\n' + + 'bin\n' + + 'ccs\n' + + 'icu4c\n' + + 'includ\n' + + 'lbin\n' + + 'lib\n' + + 'lib64\n' + + 'lpp\n' + + 'sbin\n' + + 'share\n' + + 'vacpp\n' + + '\n' + + ''; + + const result = xmlToJson(xmlOut); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0]).to.be.an('object'); + expect(result[0]).to.haveOwnProperty('type').and.to.equal('sh'); + const data = '\nbin\nccs\nicu4c\ninclud\nlbin\nlib\nlib64\nlpp\nsbin\n' + + 'share\nvacpp\n'; + expect(result[0]).to.haveOwnProperty('data').and.to.equal(data); + }); + + + it('converts qsh command XML output to js object', () => { + const xmlOut = '\n' + + 'bin\n' + + 'ccs\n' + + 'icu4c\n' + + 'includ\n' + + 'lbin\n' + + 'lib\n' + + 'lib64\n' + + 'lpp\n' + + 'sbin\n' + + 'share\n' + + 'vacpp\n' + + '\n' + + ''; + + const result = xmlToJson(xmlOut); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0]).to.be.an('object'); + expect(result[0]).to.haveOwnProperty('type').and.to.equal('qsh'); + const data = '\nbin\nccs\nicu4c\ninclud\nlbin\nlib\nlib64\nlpp\nsbin\n' + + 'share\nvacpp\n'; + expect(result[0]).to.haveOwnProperty('data').and.to.equal(data); + }); + + it('converts pgm command XML output to js object', () => { + const xmlOut = ` + + + 1 + 8 + QCENTURY + C + + 1 + 1 + + + + 28 + + + 1 + + + QCENTURY + + + + 0 + 16 + + + + + + + `; + + const result = xmlToJson(xmlOut); + + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0]).to.be.an('object'); + expect(result[0]).to.haveOwnProperty('type').and.to.equal('pgm'); + expect(result[0]).to.haveOwnProperty('success').and.to.equal(true); + expect(result[0]).to.haveOwnProperty('pgm').and.to.equal('QWCRSVAL'); + expect(result[0]).to.haveOwnProperty('lib').and.to.equal('QSYS'); + expect(result[0]).to.haveOwnProperty('data'); + expect(result[0].data).to.be.an('array'); + expect(result[0].data.length).to.equal(14); + + result[0].data.forEach((part) => { + expect(part).to.be.an('object'); + expect(result[0].data[0]).to.haveOwnProperty('value'); + expect(result[0].data[0]).to.haveOwnProperty('type'); + }); + }); + + it('converts sql command XML output to js object', () => { + const xmlOut = ` + + + + + HenningTX + JonesNY + VineVT + JohnsonGA + TyronNY + StevensCO + AlisonMN + DoeCA + ThomasWY + WilliamsTX + LeeNY + AbrahamMN + +++ success stmt1 + + +++ success + + + `; + + const result = xmlToJson(xmlOut); + + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0]).to.be.an('object'); + expect(result[0]).to.haveOwnProperty('type').and.to.equal('sql'); + expect(result[0]).to.haveOwnProperty('success').and.to.equal(true); + const stmt = 'SELECT LSTNAM, STATE FROM QIWS.QCUSTCDT'; + expect(result[0]).to.haveOwnProperty('stmt').and.to.equal(stmt); + expect(result[0]).to.haveOwnProperty('result'); + expect(result[0].result).to.be.an('array'); + expect(result[0].result.length).to.equal(12); + + // result is 2D array: each of its element is another array of objects + // Propose to simplify for result to be 1D array of objects + result[0].result.forEach((childArray) => { + expect(childArray).to.be.an('array'); + childArray.forEach((element) => { + expect(element).to.haveOwnProperty('desc'); + expect(element).to.haveOwnProperty('value'); + }); + }); + }); +});