init
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import { validateSync } from 'class-validator';
|
||||
import { MoneroNetwork } from '../../types/MoneroNetwork';
|
||||
import { IsMoneroStandardAddress } from './isMoneroStandardAddress';
|
||||
|
||||
class TestDto {
|
||||
@IsMoneroStandardAddress()
|
||||
destinationAddress: string;
|
||||
}
|
||||
|
||||
const pad = (prefix: string) => `${prefix}${'A'.repeat(95 - prefix.length)}`;
|
||||
|
||||
const MAINNET_STANDARD_ADDRESS =
|
||||
'4AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgYeYTRj5UzqtReoS44qo9mtmXCqY45DJ852K5Jv2684Rge';
|
||||
|
||||
const validateAddress = (value: string) => {
|
||||
const dto = Object.assign(new TestDto(), { destinationAddress: value });
|
||||
|
||||
return validateSync(dto);
|
||||
};
|
||||
|
||||
const accepts = (value: string) => {
|
||||
expect(validateAddress(value)).toHaveLength(0);
|
||||
};
|
||||
|
||||
const rejects = (value: string) => {
|
||||
expect(validateAddress(value).length).toBeGreaterThan(0);
|
||||
};
|
||||
|
||||
describe('IsMoneroStandardAddress', () => {
|
||||
const originalNetwork = process.env.MONERO_NETWORK;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalNetwork === undefined) {
|
||||
delete process.env.MONERO_NETWORK;
|
||||
} else {
|
||||
process.env.MONERO_NETWORK = originalNetwork;
|
||||
}
|
||||
});
|
||||
|
||||
describe('mainnet', () => {
|
||||
beforeEach(() => {
|
||||
process.env.MONERO_NETWORK = MoneroNetwork.Mainnet;
|
||||
});
|
||||
|
||||
it('accepts a real mainnet standard address', () => {
|
||||
accepts(MAINNET_STANDARD_ADDRESS);
|
||||
});
|
||||
|
||||
it('accepts mainnet standard addresses', () => {
|
||||
accepts(pad('41'));
|
||||
accepts(pad('49'));
|
||||
accepts(pad('4A'));
|
||||
accepts(pad('4B'));
|
||||
});
|
||||
|
||||
it('accepts mainnet subaddresses', () => {
|
||||
accepts(pad('82'));
|
||||
accepts(pad('89'));
|
||||
accepts(pad('8A'));
|
||||
accepts(pad('8B'));
|
||||
accepts(pad('8C'));
|
||||
});
|
||||
|
||||
it('rejects stagenet addresses', () => {
|
||||
rejects(pad('51'));
|
||||
rejects(pad('72'));
|
||||
});
|
||||
|
||||
it('rejects testnet addresses', () => {
|
||||
rejects(pad('91'));
|
||||
rejects(pad('BY'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('stagenet', () => {
|
||||
beforeEach(() => {
|
||||
process.env.MONERO_NETWORK = MoneroNetwork.Stagenet;
|
||||
});
|
||||
|
||||
it('accepts stagenet standard addresses', () => {
|
||||
accepts(pad('51'));
|
||||
accepts(pad('59'));
|
||||
accepts(pad('5A'));
|
||||
accepts(pad('5B'));
|
||||
});
|
||||
|
||||
it('accepts stagenet subaddresses', () => {
|
||||
accepts(pad('72'));
|
||||
accepts(pad('79'));
|
||||
accepts(pad('7A'));
|
||||
accepts(pad('7B'));
|
||||
});
|
||||
|
||||
it('rejects mainnet addresses', () => {
|
||||
rejects(MAINNET_STANDARD_ADDRESS);
|
||||
rejects(pad('4A'));
|
||||
rejects(pad('82'));
|
||||
});
|
||||
|
||||
it('rejects testnet addresses', () => {
|
||||
rejects(pad('91'));
|
||||
rejects(pad('BY'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('shared validation rules', () => {
|
||||
beforeEach(() => {
|
||||
process.env.MONERO_NETWORK = MoneroNetwork.Mainnet;
|
||||
});
|
||||
|
||||
it('accepts lowercase base58 characters in the body', () => {
|
||||
accepts('4AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgYeYTRj5UzqtReoS44qo9mtmXCqY45DJ852K5Jv2684Rge');
|
||||
});
|
||||
|
||||
it('trims surrounding whitespace on mainnet', () => {
|
||||
accepts(` ${pad('4A')} `);
|
||||
});
|
||||
|
||||
it('trims surrounding whitespace on stagenet', () => {
|
||||
process.env.MONERO_NETWORK = MoneroNetwork.Stagenet;
|
||||
|
||||
accepts(`\n${pad('5B')}\t`);
|
||||
});
|
||||
|
||||
it.each(['0', '1', '2', '3', '6', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G'])(
|
||||
'rejects addresses starting with %s',
|
||||
prefix => {
|
||||
rejects(pad(`${prefix}A`));
|
||||
}
|
||||
);
|
||||
|
||||
it.each(['0', 'C'])('rejects mainnet standard addresses with second character %s', second => {
|
||||
rejects(pad(`4${second}`));
|
||||
});
|
||||
|
||||
it.each(['0', '1'])('rejects mainnet subaddresses with second character %s', second => {
|
||||
rejects(pad(`8${second}`));
|
||||
});
|
||||
|
||||
it.each(['0', 'C'])('rejects stagenet standard addresses with second character %s', second => {
|
||||
process.env.MONERO_NETWORK = MoneroNetwork.Stagenet;
|
||||
|
||||
rejects(pad(`5${second}`));
|
||||
});
|
||||
|
||||
it.each(['0', '1', 'C'])('rejects stagenet subaddresses with second character %s', second => {
|
||||
process.env.MONERO_NETWORK = MoneroNetwork.Stagenet;
|
||||
|
||||
rejects(pad(`7${second}`));
|
||||
});
|
||||
|
||||
it('rejects empty input', () => {
|
||||
rejects('');
|
||||
});
|
||||
|
||||
it('rejects whitespace-only input', () => {
|
||||
rejects(' ');
|
||||
});
|
||||
|
||||
it('rejects addresses that are too short', () => {
|
||||
rejects(pad('4A').slice(0, 94));
|
||||
});
|
||||
|
||||
it('rejects addresses that are too long', () => {
|
||||
rejects(`${pad('4A')}A`);
|
||||
});
|
||||
|
||||
it('rejects integrated addresses (106 chars, also start with 4 on mainnet)', () => {
|
||||
rejects(`${MAINNET_STANDARD_ADDRESS}${'A'.repeat(11)}`);
|
||||
});
|
||||
|
||||
it.each(['0', 'O', 'I', 'l'])('rejects addresses containing %s', invalidChar => {
|
||||
const address = `${pad('4A').slice(0, 10)}${invalidChar}${pad('4A').slice(11)}`;
|
||||
|
||||
rejects(address);
|
||||
});
|
||||
|
||||
it('returns a network-specific error message', () => {
|
||||
const [error] = validateAddress('invalid');
|
||||
|
||||
expect(error.constraints?.isMoneroStandardAddress).toBe('Enter a valid mainnet Monero address.');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user