diff --git a/__tests__/helpers.spec.js b/__tests__/helpers.spec.js index fe65089..a652647 100644 --- a/__tests__/helpers.spec.js +++ b/__tests__/helpers.spec.js @@ -1,6 +1,7 @@ const os = require('os'); const fs = require('fs'); const glob = require('glob'); +const microtime = require('microtime'); const helpers = require('../lib/helpers'); const pjson = require('../package.json'); @@ -20,9 +21,38 @@ describe('Helpers', () => { }); }); + describe('formatMicrosecondsToISOString', () => { + test('converts microseconds to ISO string with microseconds precision', () => { + const input = 1726842755304456; + const expected = '2024-09-20T14:32:35.304456Z'; + const result = helpers.formatMicrosecondsToISOString(input); + expect(result).toBe(expected); + }); + + test('handles microseconds at the start of the epoch', () => { + const input = 654321; + const expected = '1970-01-01T00:00:00.654321Z'; + const result = helpers.formatMicrosecondsToISOString(input); + expect(result).toBe(expected); + }); + + test('handles rounding down of microseconds correctly', () => { + const input = 1000001; + const expected = '1970-01-01T00:00:01.000001Z'; + const result = helpers.formatMicrosecondsToISOString(input); + expect(result).toBe(expected); + }); + }); + describe('now', () => { - it('returns milliseconds from unix time', () => { - expect(new Date() - helpers.now()).toBeLessThan(100); // less than 100 miliseconds difference + it('should return the current timestamp with microseconds precision in ISO string format', () => { + const spyMicrotime = jest.spyOn(microtime, 'now').mockReturnValue(1726842755304456); + const expectedISOString = '2024-09-20T14:32:35.304456Z'; + + const result = helpers.now(); + + expect(spyMicrotime).toHaveBeenCalled(); + expect(result).toBe(expectedISOString); }); }); diff --git a/lib/helpers.js b/lib/helpers.js index 6e3e11e..113d39c 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,6 +1,7 @@ const fs = require('fs'); const glob = require('glob'); const os = require('os'); +const microtime = require('microtime'); const RestClient = require('./rest'); const pjson = require('../package.json'); @@ -11,92 +12,110 @@ const PJSON_NAME = pjson.name; const getUUIDFromFileName = (filename) => filename.match(/rplaunch-(.*)\.tmp/)[1]; -module.exports = { - formatName(name) { - const len = name.length; - // eslint-disable-next-line no-mixed-operators - return (len < MIN ? name + new Array(MIN - len + 1).join('.') : name).slice(-MAX); - }, - - now() { - return new Date().valueOf(); - }, - - // TODO: deprecate and remove - getServerResult(url, request, options, method) { - return new RestClient(options).request(method, url, request, options); - }, - - readLaunchesFromFile() { - const files = glob.sync('rplaunch-*.tmp'); - const ids = files.map(getUUIDFromFileName); - - return ids; - }, - - saveLaunchIdToFile(launchId) { - const filename = `rplaunch-${launchId}.tmp`; - fs.open(filename, 'w', (err) => { - if (err) { - throw err; - } - }); - }, - - getSystemAttribute() { - const osType = os.type(); - const osArchitecture = os.arch(); - const RAMSize = os.totalmem(); - const nodeVersion = process.version; - const systemAttr = [ - { - key: 'client', - value: `${PJSON_NAME}|${PJSON_VERSION}`, - system: true, - }, - { - key: 'os', - value: `${osType}|${osArchitecture}`, - system: true, - }, - { - key: 'RAMSize', - value: RAMSize, - system: true, - }, - { - key: 'nodeJS', - value: nodeVersion, - system: true, - }, - ]; - - return systemAttr; - }, - - generateTestCaseId(codeRef, params) { - if (!codeRef) { - return; +const formatName = (name) => { + const len = name.length; + // eslint-disable-next-line no-mixed-operators + return (len < MIN ? name + new Array(MIN - len + 1).join('.') : name).slice(-MAX); +}; + +const formatMicrosecondsToISOString = (timestampInMicroseconds) => { + const milliseconds = Math.floor(timestampInMicroseconds / 1000); + const microseconds = String(timestampInMicroseconds).slice(-3); + const isoDate = new Date(milliseconds).toISOString(); + + return isoDate.replace('Z', `${microseconds}Z`); +}; + +const now = () => { + return formatMicrosecondsToISOString(microtime.now()); +}; + +// TODO: deprecate and remove +const getServerResult = (url, request, options, method) => { + return new RestClient(options).request(method, url, request, options); +}; + +const readLaunchesFromFile = () => { + const files = glob.sync('rplaunch-*.tmp'); + const ids = files.map(getUUIDFromFileName); + + return ids; +}; + +const saveLaunchIdToFile = (launchId) => { + const filename = `rplaunch-${launchId}.tmp`; + fs.open(filename, 'w', (err) => { + if (err) { + throw err; } + }); +}; + +const getSystemAttribute = () => { + const osType = os.type(); + const osArchitecture = os.arch(); + const RAMSize = os.totalmem(); + const nodeVersion = process.version; + const systemAttr = [ + { + key: 'client', + value: `${PJSON_NAME}|${PJSON_VERSION}`, + system: true, + }, + { + key: 'os', + value: `${osType}|${osArchitecture}`, + system: true, + }, + { + key: 'RAMSize', + value: RAMSize, + system: true, + }, + { + key: 'nodeJS', + value: nodeVersion, + system: true, + }, + ]; + + return systemAttr; +}; + +const generateTestCaseId = (codeRef, params) => { + if (!codeRef) { + return; + } - if (!params) { - return codeRef; + if (!params) { + return codeRef; + } + + const parameters = params.reduce( + (result, item) => (item.value ? result.concat(item.value) : result), + [], + ); + + return `${codeRef}[${parameters}]`; +}; + +const saveLaunchUuidToFile = (launchUuid) => { + const filename = `rp-launch-uuid-${launchUuid}.tmp`; + fs.open(filename, 'w', (err) => { + if (err) { + throw err; } + }); +}; - const parameters = params.reduce( - (result, item) => (item.value ? result.concat(item.value) : result), - [], - ); - - return `${codeRef}[${parameters}]`; - }, - - saveLaunchUuidToFile(launchUuid) { - const filename = `rp-launch-uuid-${launchUuid}.tmp`; - fs.open(filename, 'w', (err) => { - if (err) { - throw err; - } - }); - }, +module.exports = { + formatName, + formatMicrosecondsToISOString, + now, + getServerResult, + readLaunchesFromFile, + saveLaunchIdToFile, + getSystemAttribute, + generateTestCaseId, + saveLaunchUuidToFile, }; diff --git a/package-lock.json b/package-lock.json index 75def79..5e82fee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,14 +6,14 @@ "packages": { "": { "name": "@reportportal/client-javascript", - "version": "5.1.4", + "version": "5.2.0", "license": "Apache-2.0", "dependencies": { "axios": "^1.7.7", "axios-retry": "^4.1.0", "glob": "^8.1.0", "ini": "^2.0.0", - "node-fetch": "^2.7.0", + "microtime": "^3.1.1", "uniqid": "^5.4.0", "uuid": "^9.0.1" }, @@ -4905,6 +4905,19 @@ "node": ">=8.6" } }, + "node_modules/microtime": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/microtime/-/microtime-3.1.1.tgz", + "integrity": "sha512-to1r7o24cDsud9IhN6/8wGmMx5R2kT0w2Xwm5okbYI3d1dk6Xv0m+Z+jg2vS9pt+ocgQHTCtgs/YuyJhySzxNg==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.4.0" + }, + "engines": { + "node": ">= 14.13.0" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4986,23 +4999,19 @@ "node": ">= 10.13" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-gyp-build": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, "node_modules/node-int64": { @@ -6098,11 +6107,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/ts-jest": { "version": "29.1.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", @@ -6488,20 +6492,6 @@ "makeerror": "1.0.12" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 2593c46..133d7fa 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "glob": "^8.1.0", "ini": "^2.0.0", "uniqid": "^5.4.0", - "uuid": "^9.0.1" + "uuid": "^9.0.1", + "microtime": "^3.1.1" }, "license": "Apache-2.0", "devDependencies": {