test: Coverage more @hyperlane-xyz/utils test (#4758)
### Description Add more coverage `utils` package test <!-- What's included in this PR? --> ### Drive-by changes <!-- Are there any minor or drive-by changes also included? --> ### Related issues <!-- - Fixes #[issue number here] --> ### Backward compatibility <!-- Are these changes backward compatible? Are there any infrastructure implications, e.g. changes that would prohibit deploying older commits using this infra tooling? Yes/No --> ### Testing <!-- What kind of testing have these changes undergone? None/Manual/Unit Tests --> More unittests functionpull/4772/head
parent
5dabdf3887
commit
c622bfbcf5
@ -0,0 +1,54 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { chunk, exclude, randomElement } from './arrays.js'; |
||||
|
||||
describe('Arrays utilities', () => { |
||||
describe('chunk', () => { |
||||
it('should split an array into chunks of the specified size', () => { |
||||
const result = chunk([1, 2, 3, 4, 5], 2); |
||||
expect(result).to.deep.equal([[1, 2], [3, 4], [5]]); |
||||
}); |
||||
|
||||
it('should return an empty array when input is empty', () => { |
||||
const result = chunk([], 2); |
||||
expect(result).to.deep.equal([]); |
||||
}); |
||||
|
||||
it('should handle chunk size larger than array length', () => { |
||||
const result = chunk([1, 2], 5); |
||||
expect(result).to.deep.equal([[1, 2]]); |
||||
}); |
||||
}); |
||||
|
||||
describe('exclude', () => { |
||||
it('should exclude the specified item from the list', () => { |
||||
const result = exclude(2, [1, 2, 3, 2]); |
||||
expect(result).to.deep.equal([1, 3]); |
||||
}); |
||||
|
||||
it('should return the same list if item is not found', () => { |
||||
const result = exclude(4, [1, 2, 3]); |
||||
expect(result).to.deep.equal([1, 2, 3]); |
||||
}); |
||||
|
||||
it('should return an empty list if all items are excluded', () => { |
||||
const result = exclude(1, [1, 1, 1]); |
||||
expect(result).to.deep.equal([]); |
||||
}); |
||||
}); |
||||
|
||||
describe('randomElement', () => { |
||||
beforeEach(() => {}); |
||||
|
||||
it('should return a random element from the list', () => { |
||||
const list = [10, 20, 30]; |
||||
const result = randomElement(list); |
||||
expect(result).to.be.oneOf(list); |
||||
}); |
||||
|
||||
it('should handle an empty list gracefully', () => { |
||||
const result = randomElement([]); |
||||
expect(result).to.be.undefined; |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,146 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { |
||||
concurrentMap, |
||||
fetchWithTimeout, |
||||
pollAsync, |
||||
raceWithContext, |
||||
retryAsync, |
||||
runWithTimeout, |
||||
sleep, |
||||
timeout, |
||||
} from './async.js'; |
||||
|
||||
describe('Async Utilities', () => { |
||||
describe('sleep', () => { |
||||
it('should resolve after sleep duration', async () => { |
||||
const start = Date.now(); |
||||
await sleep(100); |
||||
const duration = Date.now() - start; |
||||
expect(duration).to.be.at.least(100); |
||||
expect(duration).to.be.lessThan(200); |
||||
}); |
||||
}); |
||||
|
||||
describe('timeout', () => { |
||||
it('should timeout a promise', async () => { |
||||
const promise = new Promise((resolve) => setTimeout(resolve, 200)); |
||||
try { |
||||
await timeout(promise, 100); |
||||
throw new Error('Expected timeout error'); |
||||
} catch (error: any) { |
||||
expect(error.message).to.equal('Timeout reached'); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
describe('runWithTimeout', () => { |
||||
it('should run a callback with a timeout', async () => { |
||||
const result = await runWithTimeout(100, async () => { |
||||
await sleep(50); |
||||
return 'success'; |
||||
}); |
||||
expect(result).to.equal('success'); |
||||
}); |
||||
}); |
||||
|
||||
describe('fetchWithTimeout', () => { |
||||
it('should fetch with timeout', async () => { |
||||
// Mock fetch for testing
|
||||
global.fetch = async () => { |
||||
await sleep(50); |
||||
return new Response('ok'); |
||||
}; |
||||
|
||||
const response = await fetchWithTimeout('https://example.com', {}, 100); |
||||
expect(await response.text()).to.equal('ok'); |
||||
}); |
||||
}); |
||||
|
||||
describe('retryAsync', () => { |
||||
it('should retry async function with exponential backoff', async () => { |
||||
let attempt = 0; |
||||
const runner = async () => { |
||||
attempt++; |
||||
if (attempt < 3) throw new Error('fail'); |
||||
return 'success'; |
||||
}; |
||||
|
||||
const result = await retryAsync(runner, 5, 10); |
||||
expect(result).to.equal('success'); |
||||
}); |
||||
}); |
||||
|
||||
describe('pollAsync', () => { |
||||
it('should poll async function until success', async () => { |
||||
let attempt = 0; |
||||
const runner = async () => { |
||||
attempt++; |
||||
if (attempt < 3) throw new Error('fail'); |
||||
return 'success'; |
||||
}; |
||||
|
||||
const result = await pollAsync(runner, 10, 5); |
||||
expect(result).to.equal('success'); |
||||
}); |
||||
|
||||
it('should fail after reaching max retries', async () => { |
||||
let attempt = 0; |
||||
const runner = async () => { |
||||
attempt++; |
||||
throw new Error('fail'); |
||||
}; |
||||
|
||||
try { |
||||
await pollAsync(runner, 10, 3); // Set maxAttempts to 3
|
||||
throw new Error('Expected pollAsync to throw an error'); |
||||
} catch (error: any) { |
||||
expect(attempt).to.equal(3); // Ensure it attempted 3 times
|
||||
expect(error.message).to.equal('fail'); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
describe('raceWithContext', () => { |
||||
it('should race with context', async () => { |
||||
const promises = [ |
||||
sleep(50).then(() => 'first'), |
||||
sleep(100).then(() => 'second'), |
||||
]; |
||||
|
||||
const result = await raceWithContext(promises); |
||||
expect(result.resolved).to.equal('first'); |
||||
expect(result.index).to.equal(0); |
||||
}); |
||||
}); |
||||
|
||||
describe('concurrentMap', () => { |
||||
it('should map concurrently with correct results', async () => { |
||||
const xs = [1, 2, 3, 4, 5, 6]; |
||||
const mapFn = async (val: number) => { |
||||
await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate async work
|
||||
return val * 2; |
||||
}; |
||||
const result = await concurrentMap(2, xs, mapFn); |
||||
expect(result).to.deep.equal([2, 4, 6, 8, 10, 12]); |
||||
}); |
||||
|
||||
it('should respect concurrency limit', async () => { |
||||
const xs = [1, 2, 3, 4, 5, 6]; |
||||
const concurrency = 2; |
||||
let activeTasks = 0; |
||||
let maxActiveTasks = 0; |
||||
|
||||
const mapFn = async (val: number) => { |
||||
activeTasks++; |
||||
maxActiveTasks = Math.max(maxActiveTasks, activeTasks); |
||||
await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate async work
|
||||
activeTasks--; |
||||
return val * 2; |
||||
}; |
||||
|
||||
await concurrentMap(concurrency, xs, mapFn); |
||||
expect(maxActiveTasks).to.equal(concurrency); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,37 @@ |
||||
import { expect } from 'chai'; |
||||
import { utils } from 'ethers'; |
||||
|
||||
import { base58ToBuffer, bufferToBase58, hexOrBase58ToHex } from './base58.js'; |
||||
|
||||
describe('Base58 Utilities', () => { |
||||
describe('base58ToBuffer', () => { |
||||
it('should convert a base58 string to a buffer', () => { |
||||
const base58String = '3mJr7AoUXx2Wqd'; |
||||
const expectedBuffer = Buffer.from(utils.base58.decode(base58String)); |
||||
expect(base58ToBuffer(base58String)).to.deep.equal(expectedBuffer); |
||||
}); |
||||
}); |
||||
|
||||
describe('bufferToBase58', () => { |
||||
it('should convert a buffer to a base58 string', () => { |
||||
const buffer = Buffer.from([1, 2, 3, 4]); |
||||
const expectedBase58String = utils.base58.encode(buffer); |
||||
expect(bufferToBase58(buffer)).to.equal(expectedBase58String); |
||||
}); |
||||
}); |
||||
|
||||
describe('hexOrBase58ToHex', () => { |
||||
it('should return the hex string as is if it starts with 0x', () => { |
||||
const hexString = '0x1234abcd'; |
||||
expect(hexOrBase58ToHex(hexString)).to.equal(hexString); |
||||
}); |
||||
|
||||
it('should convert a base58 string to a hex string', () => { |
||||
const base58String = '3mJr7AoUXx2Wqd'; |
||||
const expectedHexString = utils.hexlify( |
||||
Buffer.from(utils.base58.decode(base58String)), |
||||
); |
||||
expect(hexOrBase58ToHex(base58String)).to.equal(expectedHexString); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,74 @@ |
||||
import { expect } from 'chai'; |
||||
import Sinon from 'sinon'; |
||||
|
||||
import { fromBase64, toBase64 } from './base64.js'; |
||||
import { rootLogger } from './logging.js'; |
||||
|
||||
describe('Base64 Utility Functions', () => { |
||||
let loggerStub: sinon.SinonStub; |
||||
|
||||
beforeEach(() => { |
||||
loggerStub = Sinon.stub(rootLogger, 'error'); |
||||
}); |
||||
|
||||
afterEach(() => { |
||||
loggerStub.restore(); |
||||
}); |
||||
|
||||
describe('toBase64', () => { |
||||
it('should encode a valid object to a base64 string', () => { |
||||
const data = { key: 'value' }; |
||||
const result = toBase64(data); |
||||
expect(result).to.be.a('string'); |
||||
expect(result).to.equal(btoa(JSON.stringify(data))); |
||||
}); |
||||
|
||||
it('should return undefined for null or undefined input', () => { |
||||
expect(toBase64(null)).to.be.undefined; |
||||
expect(toBase64(undefined)).to.be.undefined; |
||||
}); |
||||
|
||||
it('should log an error for invalid input', () => { |
||||
toBase64(null); |
||||
expect(loggerStub.calledOnce).to.be.true; |
||||
expect( |
||||
loggerStub.calledWith( |
||||
'Unable to serialize + encode data to base64', |
||||
null, |
||||
), |
||||
).to.be.true; |
||||
}); |
||||
}); |
||||
|
||||
describe('fromBase64', () => { |
||||
it('should decode a valid base64 string to an object', () => { |
||||
const data = { key: 'value' }; |
||||
const base64String = btoa(JSON.stringify(data)); |
||||
const result = fromBase64(base64String); |
||||
expect(result).to.deep.equal(data); |
||||
}); |
||||
|
||||
it('should return undefined for null or undefined input', () => { |
||||
expect(fromBase64(null as any)).to.be.undefined; |
||||
expect(fromBase64(undefined as any)).to.be.undefined; |
||||
}); |
||||
|
||||
it('should handle array input and decode the first element', () => { |
||||
const data = { key: 'value' }; |
||||
const base64String = btoa(JSON.stringify(data)); |
||||
const result = fromBase64([base64String, 'anotherString']); |
||||
expect(result).to.deep.equal(data); |
||||
}); |
||||
|
||||
it('should log an error for invalid base64 input', () => { |
||||
fromBase64('invalidBase64'); |
||||
expect(loggerStub.calledOnce).to.be.true; |
||||
expect( |
||||
loggerStub.calledWith( |
||||
'Unable to decode + deserialize data from base64', |
||||
'invalidBase64', |
||||
), |
||||
).to.be.true; |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,120 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { |
||||
isCheckpoint, |
||||
isS3Checkpoint, |
||||
isS3CheckpointWithId, |
||||
isValidSignature, |
||||
} from './checkpoints.js'; |
||||
import { Checkpoint, S3Checkpoint, S3CheckpointWithId } from './types.js'; |
||||
|
||||
describe('Checkpoints', () => { |
||||
describe('isValidSignature', () => { |
||||
it('should return true for valid string signature', () => { |
||||
const signature = '0x' + 'a'.repeat(130); // Example of a valid hex string
|
||||
expect(isValidSignature(signature)).to.be.true; |
||||
}); |
||||
|
||||
it('should return true for valid object signature', () => { |
||||
const signature = { |
||||
r: '0x' + 'a'.repeat(64), |
||||
s: '0x' + 'b'.repeat(64), |
||||
v: 27, |
||||
}; |
||||
expect(isValidSignature(signature)).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for invalid signature', () => { |
||||
const signature = { |
||||
r: '0x' + 'a'.repeat(64), |
||||
s: '0x' + 'b'.repeat(64), |
||||
v: 'invalid', |
||||
}; |
||||
expect(isValidSignature(signature)).to.be.false; |
||||
}); |
||||
}); |
||||
|
||||
describe('isCheckpoint', () => { |
||||
it('should return true for valid checkpoint', () => { |
||||
const checkpoint: Checkpoint = { |
||||
root: '0x' + 'a'.repeat(64), |
||||
index: 1, |
||||
merkle_tree_hook_address: '0x' + 'b'.repeat(40), |
||||
mailbox_domain: 123, |
||||
}; |
||||
expect(isCheckpoint(checkpoint)).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for invalid checkpoint', () => { |
||||
const checkpoint = { |
||||
root: 'invalid', |
||||
index: 'invalid', |
||||
merkle_tree_hook_address: 'invalid', |
||||
mailbox_domain: 'invalid', |
||||
}; |
||||
expect(isCheckpoint(checkpoint)).to.be.false; |
||||
}); |
||||
}); |
||||
|
||||
describe('isS3Checkpoint', () => { |
||||
it('should return true for valid S3Checkpoint', () => { |
||||
const s3Checkpoint: S3Checkpoint = { |
||||
signature: '0x' + 'a'.repeat(130), |
||||
value: { |
||||
root: '0x' + 'a'.repeat(64), |
||||
index: 1, |
||||
merkle_tree_hook_address: '0x' + 'b'.repeat(40), |
||||
mailbox_domain: 123, |
||||
}, |
||||
}; |
||||
expect(isS3Checkpoint(s3Checkpoint)).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for invalid S3Checkpoint', () => { |
||||
const s3Checkpoint = { |
||||
signature: 'invalid', |
||||
value: { |
||||
root: 'invalid', |
||||
index: 'invalid', |
||||
merkle_tree_hook_address: 'invalid', |
||||
mailbox_domain: 'invalid', |
||||
}, |
||||
}; |
||||
expect(isS3Checkpoint(s3Checkpoint)).to.be.false; |
||||
}); |
||||
}); |
||||
|
||||
describe('isS3CheckpointWithId', () => { |
||||
it('should return true for valid S3CheckpointWithId', () => { |
||||
const s3CheckpointWithId: S3CheckpointWithId = { |
||||
signature: '0x' + 'a'.repeat(130), |
||||
value: { |
||||
checkpoint: { |
||||
root: '0x' + 'a'.repeat(64), |
||||
index: 1, |
||||
merkle_tree_hook_address: '0x' + 'b'.repeat(40), |
||||
mailbox_domain: 123, |
||||
}, |
||||
message_id: '0x' + 'c'.repeat(64), |
||||
}, |
||||
}; |
||||
expect(isS3CheckpointWithId(s3CheckpointWithId)).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for invalid S3CheckpointWithId', () => { |
||||
const s3CheckpointWithId = { |
||||
signature: 'invalid', |
||||
value: { |
||||
checkpoint: { |
||||
root: 'invalid', |
||||
index: 'invalid', |
||||
merkle_tree_hook_address: 'invalid', |
||||
mailbox_domain: 'invalid', |
||||
}, |
||||
message_id: 'invalid', |
||||
}, |
||||
}; |
||||
expect(isS3CheckpointWithId(s3CheckpointWithId)).to.be.false; |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,18 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { safelyAccessEnvVar } from './env.js'; |
||||
|
||||
describe('Env Utilities', () => { |
||||
describe('safelyAccessEnvVar', () => { |
||||
it('should return the environment variable', () => { |
||||
process.env.TEST_VAR = '0xTEST_VAR'; |
||||
expect(safelyAccessEnvVar('TEST_VAR')).to.equal('0xTEST_VAR'); |
||||
expect(safelyAccessEnvVar('TEST_VAR', true)).to.equal('0xtest_var'); |
||||
}); |
||||
|
||||
it('should return undefined if the environment variable is not set', () => { |
||||
expect(safelyAccessEnvVar('NON_EXISTENT_VAR')).to.be.undefined; |
||||
expect(safelyAccessEnvVar('NON_EXISTENT_VAR', true)).to.be.undefined; |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,52 @@ |
||||
import { expect } from 'chai'; |
||||
import { utils } from 'ethers'; |
||||
|
||||
import { canonizeId, evmId } from './ids.js'; |
||||
|
||||
describe('ID Utilities', () => { |
||||
describe('canonizeId', () => { |
||||
it('should convert a 20-byte ID to a 32-byte ID', () => { |
||||
const id = '0x1234567890123456789012345678901234567890'; |
||||
const result = canonizeId(id); |
||||
expect(result).to.be.instanceOf(Uint8Array); |
||||
expect(result.length).to.equal(32); |
||||
expect(utils.hexlify(result)).to.equal( |
||||
'0x0000000000000000000000001234567890123456789012345678901234567890', |
||||
); |
||||
}); |
||||
|
||||
it('should throw an error for IDs longer than 32 bytes', () => { |
||||
const id = '0x' + '12'.repeat(33); |
||||
expect(() => canonizeId(id)).to.throw('Too long'); |
||||
}); |
||||
|
||||
it('should throw an error for IDs not 20 or 32 bytes', () => { |
||||
const id = '0x1234567890'; |
||||
expect(() => canonizeId(id)).to.throw( |
||||
'bad input, expect address or bytes32', |
||||
); |
||||
}); |
||||
}); |
||||
|
||||
describe('evmId', () => { |
||||
it('should convert a 32-byte ID to a 20-byte EVM address', () => { |
||||
const id = |
||||
'0x' + '00'.repeat(12) + '1234567890123456789012345678901234567890'; |
||||
const result = evmId(id); |
||||
expect(result).to.equal('0x1234567890123456789012345678901234567890'); |
||||
}); |
||||
|
||||
it('should return the same 20-byte ID as a 20-byte EVM address', () => { |
||||
const id = '0x1234567890123456789012345678901234567890'; |
||||
const result = evmId(id); |
||||
expect(result).to.equal(id); |
||||
}); |
||||
|
||||
it('should throw an error for IDs not 20 or 32 bytes', () => { |
||||
const id = '0x1234567890'; |
||||
expect(() => evmId(id)).to.throw( |
||||
'Invalid id length. expected 20 or 32. Got 5', |
||||
); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,39 @@ |
||||
import { expect } from 'chai'; |
||||
import { BigNumber } from 'ethers'; |
||||
|
||||
import { ethersBigNumberSerializer } from './logging.js'; |
||||
|
||||
describe('Logging Utilities', () => { |
||||
describe('ethersBigNumberSerializer', () => { |
||||
it('should serialize a BigNumber object correctly', () => { |
||||
const key = 'testKey'; |
||||
const value = { |
||||
type: 'BigNumber', |
||||
hex: '0x1a', |
||||
}; |
||||
const result = ethersBigNumberSerializer(key, value); |
||||
expect(result).to.equal(BigNumber.from(value.hex).toString()); |
||||
}); |
||||
|
||||
it('should return the value unchanged if it is not a BigNumber', () => { |
||||
const key = 'testKey'; |
||||
const value = { some: 'object' }; |
||||
const result = ethersBigNumberSerializer(key, value); |
||||
expect(result).to.equal(value); |
||||
}); |
||||
|
||||
it('should return the value unchanged if it is null', () => { |
||||
const key = 'testKey'; |
||||
const value = null; |
||||
const result = ethersBigNumberSerializer(key, value); |
||||
expect(result).to.equal(value); |
||||
}); |
||||
|
||||
it('should return the value unchanged if it is not an object', () => { |
||||
const key = 'testKey'; |
||||
const value = 'string'; |
||||
const result = ethersBigNumberSerializer(key, value); |
||||
expect(result).to.equal(value); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,39 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { difference, setEquality, symmetricDifference } from './sets.js'; |
||||
|
||||
describe('Set Operations', () => { |
||||
describe('difference', () => { |
||||
it('should return the difference of two sets', () => { |
||||
const setA = new Set([1, 2, 3, undefined]); |
||||
const setB = new Set([2, 3, 4]); |
||||
const result = difference(setA, setB); |
||||
expect(result).to.deep.equal(new Set([1, undefined])); |
||||
}); |
||||
}); |
||||
|
||||
describe('symmetricDifference', () => { |
||||
it('should return the symmetric difference of two sets', () => { |
||||
const setA = new Set([1, 2, 3]); |
||||
const setB = new Set([2, 3, 4]); |
||||
const result = symmetricDifference(setA, setB); |
||||
expect(result).to.deep.equal(new Set([1, 4])); |
||||
}); |
||||
}); |
||||
|
||||
describe('setEquality', () => { |
||||
it('should return true for equal sets', () => { |
||||
const setA = new Set([1, 2, 3]); |
||||
const setB = new Set([1, 2, 3]); |
||||
const result = setEquality(setA, setB); |
||||
expect(result).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for non-equal sets', () => { |
||||
const setA = new Set([1, 2, 3]); |
||||
const setB = new Set([1, 2, 4]); |
||||
const result = setEquality(setA, setB); |
||||
expect(result).to.be.false; |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,57 @@ |
||||
import { expect } from 'chai'; |
||||
import { Readable } from 'stream'; |
||||
|
||||
import { |
||||
errorToString, |
||||
fromHexString, |
||||
sanitizeString, |
||||
streamToString, |
||||
toHexString, |
||||
toTitleCase, |
||||
trimToLength, |
||||
} from './strings.js'; |
||||
|
||||
describe('String Utilities', () => { |
||||
it('should convert string to title case', () => { |
||||
expect(toTitleCase('hello world')).to.equal('Hello World'); |
||||
expect(toTitleCase('HELLO WORLD')).to.equal('Hello World'); |
||||
expect(toTitleCase('4ELLO WORLD')).to.equal('4ello World'); |
||||
expect(toTitleCase('')).to.equal(''); |
||||
}); |
||||
|
||||
it('should sanitize string by removing non-alphanumeric characters', () => { |
||||
expect(sanitizeString('Hello, World!')).to.equal('helloworld'); |
||||
expect(sanitizeString('123-456')).to.equal('123456'); |
||||
expect(sanitizeString('')).to.equal(''); |
||||
}); |
||||
|
||||
it('should trim string to specified length', () => { |
||||
expect(trimToLength('Hello, World!', 5)).to.equal('Hello...'); |
||||
expect(trimToLength('Short', 10)).to.equal('Short'); |
||||
expect(trimToLength('', 10)).to.equal(''); |
||||
}); |
||||
|
||||
it('should convert stream to string', async () => { |
||||
const stream = new Readable(); |
||||
stream.push('Hello, '); |
||||
stream.push('World!'); |
||||
stream.push(null); |
||||
|
||||
const result = await streamToString(stream); |
||||
expect(result).to.equal('Hello, World!'); |
||||
}); |
||||
|
||||
it('should convert error to string', () => { |
||||
expect(errorToString('Error message')).to.equal('Error message'); |
||||
expect(errorToString({ message: 'Error object' })).to.equal('Error object'); |
||||
expect(errorToString(404)).to.equal('Error code: 404'); |
||||
expect(errorToString(null)).to.equal('Unknown Error'); |
||||
}); |
||||
|
||||
it('should convert hex string to buffer and back', () => { |
||||
const hexString = '0x48656c6c6f'; |
||||
const buffer = fromHexString(hexString); |
||||
expect(buffer.toString('utf8')).to.equal('Hello'); |
||||
expect(toHexString(buffer)).to.equal(hexString); |
||||
}); |
||||
}); |
@ -0,0 +1,42 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { isNullish, isNumeric } from './typeof.js'; |
||||
|
||||
describe('isNullish', () => { |
||||
it('should return true for null', () => { |
||||
expect(isNullish(null)).to.be.true; |
||||
}); |
||||
|
||||
it('should return true for undefined', () => { |
||||
expect(isNullish(undefined)).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for non-nullish values', () => { |
||||
expect(isNullish('')).to.be.false; |
||||
expect(isNullish(0)).to.be.false; |
||||
expect(isNullish(false)).to.be.false; |
||||
}); |
||||
}); |
||||
|
||||
describe('isNumeric', () => { |
||||
it('should return true for numeric strings', () => { |
||||
expect(isNumeric('123')).to.be.true; |
||||
}); |
||||
|
||||
it('should return true for numbers', () => { |
||||
expect(isNumeric(123)).to.be.true; |
||||
}); |
||||
|
||||
it('should return true for negative numbers', () => { |
||||
expect(isNumeric(-123)).to.be.true; |
||||
}); |
||||
|
||||
it('should return true for floating point numbers', () => { |
||||
expect(isNumeric(123.45)).to.be.true; |
||||
}); |
||||
|
||||
it('should return false for non-numeric strings', () => { |
||||
expect(isNumeric('abc')).to.be.false; |
||||
expect(isNumeric('123abc')).to.be.false; |
||||
}); |
||||
}); |
@ -0,0 +1,13 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { assert } from './validation.js'; |
||||
|
||||
describe('assert', () => { |
||||
it('should not throw an error when the predicate is true', () => { |
||||
expect(() => assert(true, 'Error message')).to.not.throw(); |
||||
}); |
||||
|
||||
it('should throw an error when the predicate is false', () => { |
||||
expect(() => assert(false, 'Error message')).to.throw('Error message'); |
||||
}); |
||||
}); |
@ -0,0 +1,33 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { tryParseJsonOrYaml } from './yaml.js'; |
||||
|
||||
describe('tryParseJsonOrYaml', () => { |
||||
it('should parse valid JSON string', () => { |
||||
const jsonString = '{"key": "value"}'; |
||||
const result: any = tryParseJsonOrYaml(jsonString); |
||||
expect(result.success).to.be.true; |
||||
expect(result.data).to.deep.equal({ key: 'value' }); |
||||
}); |
||||
|
||||
it('should parse valid YAML string', () => { |
||||
const yamlString = 'key: value'; |
||||
const result: any = tryParseJsonOrYaml(yamlString); |
||||
expect(result.success).to.be.true; |
||||
expect(result.data).to.deep.equal({ key: 'value' }); |
||||
}); |
||||
|
||||
it('should fail for invalid JSON string', () => { |
||||
const invalidJsonString = '{"key": "value"'; |
||||
const result: any = tryParseJsonOrYaml(invalidJsonString); |
||||
expect(result.success).to.be.false; |
||||
expect(result.error).to.equal('Input is not valid JSON or YAML'); |
||||
}); |
||||
|
||||
it('should fail for invalid YAML string', () => { |
||||
const invalidYamlString = 'key: value:'; |
||||
const result: any = tryParseJsonOrYaml(invalidYamlString); |
||||
expect(result.success).to.be.false; |
||||
expect(result.error).to.equal('Input is not valid JSON or YAML'); |
||||
}); |
||||
}); |
Loading…
Reference in new issue