Skip to content

Commit

Permalink
Implement IP v4/6 arbitraries
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz committed Mar 27, 2019
1 parent f24ae2a commit bd719b0
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
58 changes: 58 additions & 0 deletions src/check/arbitrary/IpArbitrary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { array } from './ArrayArbitrary';
import { Arbitrary } from './definition/Arbitrary';
import { nat } from './IntegerArbitrary';
import { oneof } from './OneOfArbitrary';
import { hexaString } from './StringArbitrary';
import { tuple } from './TupleArbitrary';

/**
* For valid IP v4
*
* Following RFC 3986
* https://tools.ietf.org/html/rfc3986#section-3.2.2
*/
function ipV4(): Arbitrary<string> {
// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
return tuple(nat(255), nat(255), nat(255), nat(255)).map(([a, b, c, d]) => `${a}.${b}.${c}.${d}`);
}

/**
* For valid IP v6
*
* Following RFC 3986
* https://tools.ietf.org/html/rfc3986#section-3.2.2
*/
function ipV6(): Arbitrary<string> {
// h16 = 1*4HEXDIG
// ls32 = ( h16 ":" h16 ) / IPv4address
// IPv6address = 6( h16 ":" ) ls32
// / "::" 5( h16 ":" ) ls32
// / [ h16 ] "::" 4( h16 ":" ) ls32
// / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
// / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
// / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
// / [ *4( h16 ":" ) h16 ] "::" ls32
// / [ *5( h16 ":" ) h16 ] "::" h16
// / [ *6( h16 ":" ) h16 ] "::"
const h16Arb = hexaString(1, 4);
const ls32Arb = oneof(tuple(h16Arb, h16Arb).map(([a, b]) => `${a}:${b}`), ipV4());
return oneof(
tuple(array(h16Arb, 6, 6), ls32Arb).map(([eh, l]) => `${eh.join(':')}:${l}`),
tuple(array(h16Arb, 5, 5), ls32Arb).map(([eh, l]) => `::${eh.join(':')}:${l}`),
tuple(array(h16Arb, 0, 1), array(h16Arb, 4, 4), ls32Arb).map(
([bh, eh, l]) => `${bh.join(':')}::${eh.join(':')}:${l}`
),
tuple(array(h16Arb, 0, 2), array(h16Arb, 3, 3), ls32Arb).map(
([bh, eh, l]) => `${bh.join(':')}::${eh.join(':')}:${l}`
),
tuple(array(h16Arb, 0, 3), array(h16Arb, 2, 2), ls32Arb).map(
([bh, eh, l]) => `${bh.join(':')}::${eh.join(':')}:${l}`
),
tuple(array(h16Arb, 0, 4), h16Arb, ls32Arb).map(([bh, eh, l]) => `${bh.join(':')}::${eh}:${l}`),
tuple(array(h16Arb, 0, 5), ls32Arb).map(([bh, l]) => `${bh.join(':')}::${l}`),
tuple(array(h16Arb, 0, 6), h16Arb).map(([bh, eh]) => `${bh.join(':')}::${eh}`),
tuple(array(h16Arb, 0, 7)).map(([bh]) => `${bh.join(':')}::`)
);
}

export { ipV4, ipV6 };
3 changes: 3 additions & 0 deletions src/fast-check-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { double, float } from './check/arbitrary/FloatingPointArbitrary';
import { frequency } from './check/arbitrary/FrequencyArbitrary';
import { compareBooleanFunc, compareFunc, func } from './check/arbitrary/FunctionArbitrary';
import { integer, maxSafeInteger, maxSafeNat, nat } from './check/arbitrary/IntegerArbitrary';
import { ipV4, ipV6 } from './check/arbitrary/IpArbitrary';
import { lorem } from './check/arbitrary/LoremArbitrary';
import {
anything,
Expand Down Expand Up @@ -109,6 +110,8 @@ export {
hexaString,
base64String,
lorem,
ipV4,
ipV6,
constant,
constantFrom,
clonedConstant,
Expand Down
43 changes: 43 additions & 0 deletions test/unit/check/arbitrary/IpArbitrary.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ipV4, ipV6 } from '../../../../src/check/arbitrary/IpArbitrary';

import * as genericHelper from './generic/GenericArbitraryHelper';

const isValidIpV4 = (i: string) => {
if (typeof i !== 'string') return false;
const m = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(i);
if (m === null) return false;
return [m[1], m[2], m[3], m[4]].every(g => {
const n = +g;
return n >= 0 && n <= 255 && String(n) === g;
});
};
const isValidIpV6 = (i: string) => {
if (typeof i !== 'string') return false;
const firstElision = i.indexOf('::');
if (firstElision !== -1) {
// At most one '::'
if (i.substr(firstElision + 1).includes('::')) return false;
}
const chunks = i.split(':');
const endByIpV4 = isValidIpV4(chunks[chunks.length - 1]);

const nonEmptyChunks = chunks.filter(c => c !== '');
const hexaChunks = endByIpV4 ? nonEmptyChunks.slice(0, nonEmptyChunks.length - 1) : nonEmptyChunks;
if (!hexaChunks.every(s => /^[0-9a-f]{1,4}$/.test(s))) return false;

const equivalentChunkLength = endByIpV4 ? hexaChunks.length + 2 : hexaChunks.length;
return firstElision !== -1 ? equivalentChunkLength < 8 : equivalentChunkLength === 8;
};

describe('IpArbitrary', () => {
describe('ipV4', () => {
genericHelper.isValidArbitrary(() => ipV4(), {
isValidValue: (g: string) => isValidIpV4(g)
});
});
describe('ipV6', () => {
genericHelper.isValidArbitrary(() => ipV6(), {
isValidValue: (g: string) => isValidIpV6(g)
});
});
});

0 comments on commit bd719b0

Please sign in to comment.