Skip to content

Commit

Permalink
feat: share verbose numerics (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomerAberbach authored Dec 8, 2024
1 parent 56edcc5 commit bc76276
Show file tree
Hide file tree
Showing 10 changed files with 444 additions and 31 deletions.
52 changes: 41 additions & 11 deletions src/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
Union,
} from '@typespec/compiler'
import {
any,
concat,
entries,
filter,
Expand All @@ -41,14 +42,12 @@ import type {
Arbitrary,
ArbitraryNamespace,
ArrayArbitrary,
BigIntArbitrary,
DictionaryArbitrary,
NumberArbitrary,
RecordArbitrary,
ReferenceArbitrary,
StringArbitrary,
} from './arbitrary.ts'
import { numerics } from './numerics.ts'
import { fastCheckNumerics, numerics } from './numerics.ts'

const convertProgram = (
program: Program,
Expand Down Expand Up @@ -150,19 +149,19 @@ const convertScalar = (
case `safeint`:
case `float32`:
case `float64`:
arbitrary = convertNumber(constraints, numerics[scalar.name])
arbitrary = convertNumber(scalar, constraints, numerics[scalar.name])
break
case `float`:
case `decimal128`:
case `decimal`:
case `numeric`:
arbitrary = convertNumber(constraints, numerics.float64)
arbitrary = convertNumber(scalar, constraints, numerics.float64)
break
case `int64`:
arbitrary = convertBigInt(constraints, numerics.int64)
arbitrary = convertBigInt(scalar, constraints, numerics.int64)
break
case `integer`:
arbitrary = convertBigInt(constraints)
arbitrary = convertBigInt(scalar, constraints)
break
case `bytes`:
arbitrary = memoize({ type: `bytes` })
Expand Down Expand Up @@ -193,26 +192,57 @@ const convertScalar = (
}

const convertNumber = (
scalar: Scalar,
constraints: Constraints,
{ min, max, isInteger }: { min: number; max: number; isInteger: boolean },
): NumberArbitrary =>
memoize({
): Arbitrary => {
const arbitrary = memoize({
type: `number`,
min: maxOrUndefined(constraints.min?.asNumber() ?? undefined, min),
max: minOrUndefined(constraints.max?.asNumber() ?? undefined, max),
isInteger,
})

const hasDefaultConstraints = arbitrary.min === min && arbitrary.max === max
if (!hasDefaultConstraints) {
return arbitrary
}

const matchesFastCheckNumeric = pipe(
values(fastCheckNumerics),
any(
numeric =>
arbitrary.min === numeric.min.value &&
arbitrary.max === numeric.max.value &&
arbitrary.isInteger === numeric.isInteger,
),
)
if (matchesFastCheckNumeric) {
return arbitrary
}

return ref(scalar.name, arbitrary)
}

const convertBigInt = (
scalar: Scalar,
constraints: Constraints,
{ min, max }: { min?: bigint; max?: bigint } = {},
): BigIntArbitrary =>
memoize({
): Arbitrary => {
const arbitrary = memoize({
type: `bigint`,
min: maxOrUndefined(constraints.min?.asBigInt() ?? undefined, min),
max: minOrUndefined(constraints.max?.asBigInt() ?? undefined, max),
})

const hasDefaultConstraints =
min !== undefined &&
max !== undefined &&
arbitrary.min === min &&
arbitrary.max === max
return hasDefaultConstraints ? ref(scalar.name, arbitrary) : arbitrary
}

const convertString = (constraints: Constraints): StringArbitrary =>
memoize({
type: `string`,
Expand Down
2 changes: 1 addition & 1 deletion src/numerics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const fastCheckNumerics: Record<
FastCheckNumeric
> = constFastCheckNumerics

type FastCheckNumeric = {
export type FastCheckNumeric = {
min: {
value: number
configurable: boolean | `higher`
Expand Down
61 changes: 60 additions & 1 deletion test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ test.each([
`,
},
{
name: `shared-never`,
name: `never-sharing`,
code: `
model $Model {
property1: never,
Expand Down Expand Up @@ -338,6 +338,65 @@ test.each([
scalar MinMaxValueNumeric extends numeric;
`,
},
{
name: `numeric-sharing`,
code: `
scalar FirstInt8 extends int8;
scalar SecondInt8 extends int8;
@minValue(42)
scalar ThirdInt8 extends int8;
scalar FirstInt16 extends int16;
scalar SecondInt16 extends int16;
@minValue(42)
scalar ThirdInt16 extends int16;
scalar FirstInt32 extends int32;
scalar SecondInt32 extends int32;
@minValue(42)
scalar ThirdInt32 extends int32;
scalar FirstSafeInt extends safeint;
scalar SecondSafeInt extends safeint;
@minValue(42)
scalar ThirdSafeInt extends safeint;
scalar FirstInt64 extends int64;
scalar SecondInt64 extends int64;
@minValue(42)
scalar ThirdInt64 extends int64;
scalar FirstInteger extends integer;
scalar SecondInteger extends integer;
@minValue(42)
scalar ThirdInteger extends integer;
scalar FirstFloat32 extends float32;
scalar SecondFloat32 extends float32;
@minValue(42)
scalar ThirdFloat32 extends float32;
scalar FirstFloat64 extends float64;
scalar SecondFloat64 extends float64;
@minValue(42)
scalar ThirdFloat64 extends float64;
scalar FirstDecimal128 extends decimal128;
scalar SecondDecimal128 extends decimal128;
@minValue(42)
scalar ThirdDecimal128 extends decimal128;
scalar FirstDecimal extends decimal;
scalar SecondDecimal extends decimal;
@minValue(42)
scalar ThirdDecimal extends decimal;
scalar FirstNumeric extends numeric;
scalar SecondNumeric extends numeric;
@minValue(42)
scalar ThirdNumeric extends numeric;
`,
},
{
name: `string`,
code: `
Expand Down
9 changes: 4 additions & 5 deletions test/snapshots/int16/arbitraries.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import * as fc from 'fast-check';

export const Int16 = fc.integer({
const int16 = fc.integer({
min: -32768,
max: 32767,
});

export const Int16 = int16;

export const MinValueInt16 = fc.integer({
min: -200,
max: 32767,
Expand All @@ -22,7 +24,4 @@ export const MinMaxValueInt16 = fc.integer({
max: 345,
});

export const RedundantlyMinMaxValueInt16 = fc.integer({
min: -32768,
max: 32767,
});
export const RedundantlyMinMaxValueInt16 = int16;
9 changes: 4 additions & 5 deletions test/snapshots/int8/arbitraries.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import * as fc from 'fast-check';

export const Int8 = fc.integer({
const int8 = fc.integer({
min: -128,
max: 127,
});

export const Int8 = int8;

export const MinValueInt8 = fc.integer({
min: -10,
max: 127,
Expand All @@ -22,7 +24,4 @@ export const MinMaxValueInt8 = fc.integer({
max: 20,
});

export const RedundantlyMinMaxValueInt8 = fc.integer({
min: -128,
max: 127,
});
export const RedundantlyMinMaxValueInt8 = int8;
15 changes: 7 additions & 8 deletions test/snapshots/model-optional-property/arbitraries.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import * as fc from 'fast-check';

const int64 = fc.bigInt({
min: -9223372036854775808n,
max: 9223372036854775807n,
});

export const EmptyModel = fc.record({});

export const AllRequiredModel = fc.record({
Expand All @@ -12,10 +17,7 @@ export const AllOptionalModel = fc.record(
{
a: fc.integer(),
b: fc.string(),
c: fc.bigInt({
min: -9223372036854775808n,
max: 9223372036854775807n,
}),
c: int64,
},
{ withDeletedKeys: true },
);
Expand All @@ -24,10 +26,7 @@ export const SomeOptionalModel = fc.record(
{
a: fc.integer(),
b: fc.string(),
c: fc.bigInt({
min: -9223372036854775808n,
max: 9223372036854775807n,
}),
c: int64,
},
{ requiredKeys: ['b'] },
);
File renamed without changes.
File renamed without changes.
94 changes: 94 additions & 0 deletions test/snapshots/numeric-sharing/arbitraries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as fc from 'fast-check';

const int8 = fc.integer({
min: -128,
max: 127,
});

const int16 = fc.integer({
min: -32768,
max: 32767,
});

const int64 = fc.bigInt({
min: -9223372036854775808n,
max: 9223372036854775807n,
});

export const FirstInt8 = int8;

export const SecondInt8 = int8;

export const ThirdInt8 = fc.integer({
min: 42,
max: 127,
});

export const FirstInt16 = int16;

export const SecondInt16 = int16;

export const ThirdInt16 = fc.integer({
min: 42,
max: 32767,
});

export const FirstInt32 = fc.integer();

export const SecondInt32 = fc.integer();

export const ThirdInt32 = fc.integer({ min: 42 });

export const FirstSafeInt = fc.maxSafeInteger();

export const SecondSafeInt = fc.maxSafeInteger();

export const ThirdSafeInt = fc.integer({
min: 42,
max: 9007199254740991,
});

export const FirstInt64 = int64;

export const SecondInt64 = int64;

export const ThirdInt64 = fc.bigInt({
min: 42n,
max: 9223372036854775807n,
});

export const FirstInteger = fc.bigInt();

export const SecondInteger = fc.bigInt();

export const ThirdInteger = fc.bigInt({ min: 42n });

export const FirstFloat32 = fc.float();

export const SecondFloat32 = fc.float();

export const ThirdFloat32 = fc.float({ min: 42 });

export const FirstFloat64 = fc.double();

export const SecondFloat64 = fc.double();

export const ThirdFloat64 = fc.double({ min: 42 });

export const FirstDecimal128 = fc.double();

export const SecondDecimal128 = fc.double();

export const ThirdDecimal128 = fc.double({ min: 42 });

export const FirstDecimal = fc.double();

export const SecondDecimal = fc.double();

export const ThirdDecimal = fc.double({ min: 42 });

export const FirstNumeric = fc.double();

export const SecondNumeric = fc.double();

export const ThirdNumeric = fc.double({ min: 42 });
Loading

0 comments on commit bc76276

Please sign in to comment.