Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
refactor(experimental): errors: codecs-numbers package
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec authored and steveluscher committed Feb 29, 2024
1 parent 7d67615 commit 27bf3a2
Show file tree
Hide file tree
Showing 18 changed files with 139 additions and 45 deletions.
3 changes: 2 additions & 1 deletion packages/codecs-numbers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
"node": ">=17.4"
},
"dependencies": {
"@solana/codecs-core": "workspace:*"
"@solana/codecs-core": "workspace:*",
"@solana/errors": "workspace:*"
},
"devDependencies": {
"@solana/build-scripts": "workspace:*",
Expand Down
23 changes: 21 additions & 2 deletions packages/codecs-numbers/src/__tests__/__setup__.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Codec, createCodec, Encoder } from '@solana/codecs-core';
import { SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE, SolanaError } from '@solana/errors';

export const assertValid = <T>(codec: Codec<T>, number: T, bytes: string, decodedNumber?: T): void => {
// Serialize.
Expand All @@ -17,8 +18,26 @@ export const assertValid = <T>(codec: Codec<T>, number: T, bytes: string, decode
expect(deserializationWithOffset[1]).toBe(actualBytes.length + 3);
};

export const assertRangeError = <T>(encoder: Encoder<T>, number: T): void => {
expect(() => encoder.encode(number)).toThrow('expected number to be in the range');
type RangeErrorValues = {
codecDescription: string;
max: bigint | number;
min: bigint | number;
};

export const assertRangeError = <T extends bigint | number>(
config: RangeErrorValues,
encoder: Encoder<T>,
value: T,
): void => {
const { codecDescription, max, min } = config;
expect(() => encoder.encode(value)).toThrow(
new SolanaError(SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE, {
codecDescription,
max,
min,
value,
}),
);
};

export const base16: Codec<string> = createCodec({
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/i128-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = -BigInt('0x7fffffffffffffffffffffffffffffff') - 1n;
const MAX = BigInt('0x7fffffffffffffffffffffffffffffff');
const i128 = getI128Codec;
const rangeErrorValues = {
codecDescription: 'i128',
max: MAX,
min: MIN,
};

describe('getI128Codec', () => {
it('encodes and decodes i128 numbers', () => {
Expand Down Expand Up @@ -36,10 +41,10 @@ describe('getI128Codec', () => {
assertValid(i128BE, MAX, '7fffffffffffffffffffffffffffffff');

// Out of range.
assertRangeError(i128LE, MIN - 1n);
assertRangeError(i128BE, MIN - 1n);
assertRangeError(i128LE, MAX + 1n);
assertRangeError(i128BE, MAX + 1n);
assertRangeError(rangeErrorValues, i128LE, MIN - 1n);
assertRangeError(rangeErrorValues, i128BE, MIN - 1n);
assertRangeError(rangeErrorValues, i128LE, MAX + 1n);
assertRangeError(rangeErrorValues, i128BE, MAX + 1n);
});

it('has the right size', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/i16-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = -Number('0x7fff') - 1;
const MAX = Number('0x7fff');
const i16 = getI16Codec;
const rangeErrorValues = {
codecDescription: 'i16',
max: MAX,
min: MIN,
};

describe('getI16Codec', () => {
it('encodes and decodes i16 numbers', () => {
Expand Down Expand Up @@ -36,10 +41,10 @@ describe('getI16Codec', () => {
assertValid(i16BE, MAX, '7fff');

// Out of range.
assertRangeError(i16LE, MIN - 1);
assertRangeError(i16BE, MIN - 1);
assertRangeError(i16LE, MAX + 1);
assertRangeError(i16BE, MAX + 1);
assertRangeError(rangeErrorValues, i16LE, MIN - 1);
assertRangeError(rangeErrorValues, i16BE, MIN - 1);
assertRangeError(rangeErrorValues, i16LE, MAX + 1);
assertRangeError(rangeErrorValues, i16BE, MAX + 1);
});

it('has the right size', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/i32-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = -Number('0x7fffffff') - 1;
const MAX = Number('0x7fffffff');
const i32 = getI32Codec;
const rangeErrorValues = {
codecDescription: 'i32',
max: MAX,
min: MIN,
};

describe('getI32Codec', () => {
it('encodes and decodes i32 numbers', () => {
Expand Down Expand Up @@ -36,10 +41,10 @@ describe('getI32Codec', () => {
assertValid(i32BE, MAX, '7fffffff');

// Out of range.
assertRangeError(i32LE, MIN - 1);
assertRangeError(i32BE, MIN - 1);
assertRangeError(i32LE, MAX + 1);
assertRangeError(i32BE, MAX + 1);
assertRangeError(rangeErrorValues, i32LE, MIN - 1);
assertRangeError(rangeErrorValues, i32BE, MIN - 1);
assertRangeError(rangeErrorValues, i32LE, MAX + 1);
assertRangeError(rangeErrorValues, i32BE, MAX + 1);
});

it('has the right size', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/i64-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = -BigInt('0x7fffffffffffffff') - 1n;
const MAX = BigInt('0x7fffffffffffffff');
const i64 = getI64Codec;
const rangeErrorValues = {
codecDescription: 'i64',
max: MAX,
min: MIN,
};

describe('getI64Codec', () => {
it('encodes and decodes i64 numbers', () => {
Expand Down Expand Up @@ -36,10 +41,10 @@ describe('getI64Codec', () => {
assertValid(i64BE, MAX, '7fffffffffffffff');

// Out of range.
assertRangeError(i64LE, MIN - 1n);
assertRangeError(i64BE, MIN - 1n);
assertRangeError(i64LE, MAX + 1n);
assertRangeError(i64BE, MAX + 1n);
assertRangeError(rangeErrorValues, i64LE, MIN - 1n);
assertRangeError(rangeErrorValues, i64BE, MIN - 1n);
assertRangeError(rangeErrorValues, i64LE, MAX + 1n);
assertRangeError(rangeErrorValues, i64BE, MAX + 1n);
});

it('has the right size', () => {
Expand Down
9 changes: 7 additions & 2 deletions packages/codecs-numbers/src/__tests__/i8-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = -Number('0x7f') - 1;
const MAX = Number('0x7f');
const i8 = getI8Codec;
const rangeErrorValues = {
codecDescription: 'i8',
max: MAX,
min: MIN,
};

describe('getI8Codec', () => {
it('encodes and decodes i8 numbers', () => {
Expand All @@ -23,8 +28,8 @@ describe('getI8Codec', () => {
assertValid(i8(), MAX, '7f');

// Out of range.
assertRangeError(i8(), MIN - 1);
assertRangeError(i8(), MAX + 1);
assertRangeError(rangeErrorValues, i8(), MIN - 1);
assertRangeError(rangeErrorValues, i8(), MAX + 1);
});

it('has the right size', () => {
Expand Down
9 changes: 7 additions & 2 deletions packages/codecs-numbers/src/__tests__/short-u16-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = 0;
const MAX = 65535;
const shortU16 = getShortU16Codec;
const rangeErrorValues = {
codecDescription: 'shortU16',
max: MAX,
min: MIN,
};

describe('getShortU16Codec', () => {
it('encodes and decodes short u16 numbers', () => {
Expand All @@ -25,8 +30,8 @@ describe('getShortU16Codec', () => {
assertValid(shortU16(), MAX, 'ffff03');

// Out of range.
assertRangeError(shortU16(), MIN - 1);
assertRangeError(shortU16(), MAX + 1);
assertRangeError(rangeErrorValues, shortU16(), MIN - 1);
assertRangeError(rangeErrorValues, shortU16(), MAX + 1);

// Assert re-serialization.
const codec = shortU16();
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/u128-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const MIN = 0n;
const MAX = BigInt('0xffffffffffffffffffffffffffffffff');
const HALF = BigInt('0xffffffffffffffff');
const u128 = getU128Codec;
const rangeErrorValues = {
codecDescription: 'u128',
max: MAX,
min: MIN,
};

describe('getU128Codec', () => {
it('encodes and decodes u128 numbers', () => {
Expand Down Expand Up @@ -35,10 +40,10 @@ describe('getU128Codec', () => {
assertValid(u128BE, MAX, 'ffffffffffffffffffffffffffffffff');

// Out of range.
assertRangeError(u128LE, MIN - 1n);
assertRangeError(u128BE, MIN - 1n);
assertRangeError(u128LE, MAX + 1n);
assertRangeError(u128BE, MAX + 1n);
assertRangeError(rangeErrorValues, u128LE, MIN - 1n);
assertRangeError(rangeErrorValues, u128BE, MIN - 1n);
assertRangeError(rangeErrorValues, u128LE, MAX + 1n);
assertRangeError(rangeErrorValues, u128BE, MAX + 1n);
});

it('has the right size', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/u16-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const MIN = 0;
const MAX = Number('0xffff');
const HALF = Number('0xff');
const u16 = getU16Codec;
const rangeErrorValues = {
codecDescription: 'u16',
max: MAX,
min: MIN,
};

describe('getU16Codec', () => {
it('encodes and decodes u16 numbers', () => {
Expand Down Expand Up @@ -35,10 +40,10 @@ describe('getU16Codec', () => {
assertValid(u16BE, MAX, 'ffff');

// Out of range.
assertRangeError(u16LE, MIN - 1);
assertRangeError(u16BE, MIN - 1);
assertRangeError(u16LE, MAX + 1);
assertRangeError(u16BE, MAX + 1);
assertRangeError(rangeErrorValues, u16LE, MIN - 1);
assertRangeError(rangeErrorValues, u16BE, MIN - 1);
assertRangeError(rangeErrorValues, u16LE, MAX + 1);
assertRangeError(rangeErrorValues, u16BE, MAX + 1);
});

it('has the right size', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/u32-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const MIN = 0;
const MAX = Number('0xffffffff');
const HALF = Number('0xffff');
const u32 = getU32Codec;
const rangeErrorValues = {
codecDescription: 'u32',
max: MAX,
min: MIN,
};

describe('getU32Codec', () => {
it('encodes and decodes u32 numbers', () => {
Expand Down Expand Up @@ -35,10 +40,10 @@ describe('getU32Codec', () => {
assertValid(u32BE, MAX, 'ffffffff');

// Out of range.
assertRangeError(u32LE, MIN - 1);
assertRangeError(u32BE, MIN - 1);
assertRangeError(u32LE, MAX + 1);
assertRangeError(u32BE, MAX + 1);
assertRangeError(rangeErrorValues, u32LE, MIN - 1);
assertRangeError(rangeErrorValues, u32BE, MIN - 1);
assertRangeError(rangeErrorValues, u32LE, MAX + 1);
assertRangeError(rangeErrorValues, u32BE, MAX + 1);
});

it('has the right size', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/codecs-numbers/src/__tests__/u64-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const MIN = 0n;
const MAX = BigInt('0xffffffffffffffff');
const HALF = BigInt('0xffffffff');
const u64 = getU64Codec;
const rangeErrorValues = {
codecDescription: 'u64',
max: MAX,
min: MIN,
};

describe('getU64Codec', () => {
it('encodes and decodes u64 numbers', () => {
Expand Down Expand Up @@ -35,10 +40,10 @@ describe('getU64Codec', () => {
assertValid(u64BE, MAX, 'ffffffffffffffff');

// Out of range.
assertRangeError(u64LE, MIN - 1n);
assertRangeError(u64BE, MIN - 1n);
assertRangeError(u64LE, MAX + 1n);
assertRangeError(u64BE, MAX + 1n);
assertRangeError(rangeErrorValues, u64LE, MIN - 1n);
assertRangeError(rangeErrorValues, u64BE, MIN - 1n);
assertRangeError(rangeErrorValues, u64LE, MAX + 1n);
assertRangeError(rangeErrorValues, u64BE, MAX + 1n);
});

it('has the right size', () => {
Expand Down
9 changes: 7 additions & 2 deletions packages/codecs-numbers/src/__tests__/u8-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { assertRangeError, assertValid } from './__setup__';
const MIN = 0;
const MAX = Number('0xff');
const u8 = getU8Codec;
const rangeErrorValues = {
codecDescription: 'u8',
max: MAX,
min: MIN,
};

describe('getU8Codec', () => {
it('encodes and decodes u8 numbers', () => {
Expand All @@ -20,8 +25,8 @@ describe('getU8Codec', () => {
assertValid(u8(), MAX, 'ff');

// Out of range.
assertRangeError(u8(), MIN - 1);
assertRangeError(u8(), MAX + 1);
assertRangeError(rangeErrorValues, u8(), MIN - 1);
assertRangeError(rangeErrorValues, u8(), MAX + 1);
});

it('has the right size', () => {
Expand Down
12 changes: 8 additions & 4 deletions packages/codecs-numbers/src/assertions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE, SolanaError } from '@solana/errors';

/**
* Asserts that a given number is between a given range.
*/
Expand All @@ -8,9 +10,11 @@ export function assertNumberIsBetweenForCodec(
value: number | bigint,
) {
if (value < min || value > max) {
// TODO: Coded error.
throw new Error(
`Codec [${codecDescription}] expected number to be in the range [${min}, ${max}], got ${value}.`,
);
throw new SolanaError(SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE, {
codecDescription,
max,
min,
value,
});
}
}
2 changes: 2 additions & 0 deletions packages/errors/src/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const SOLANA_ERROR__CODECS_INVALID_SCALAR_ENUM_VARIANT = 42 as const;
export const SOLANA_ERROR__CODECS_FIXED_NULLABLE_WITH_VARIABLE_SIZE_CODEC = 43 as const;
export const SOLANA_ERROR__CODECS_FIXED_NULLABLE_WITH_VARIABLE_SIZE_PREFIX = 44 as const;
export const SOLANA_ERROR__CODECS_CODEC_REQUIRES_FIXED_SIZE = 45 as const;
export const SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE = 46 as const;
// Reserve error codes starting with [4615000-4615999] for the Rust enum `InstructionError`
export const SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN = 4615000 as const;
export const SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR = 4615001 as const;
Expand Down Expand Up @@ -218,6 +219,7 @@ export type SolanaErrorCode =
| typeof SOLANA_ERROR__CODECS_FIXED_NULLABLE_WITH_VARIABLE_SIZE_CODEC
| typeof SOLANA_ERROR__CODECS_FIXED_NULLABLE_WITH_VARIABLE_SIZE_PREFIX
| typeof SOLANA_ERROR__CODECS_CODEC_REQUIRES_FIXED_SIZE
| typeof SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE
| typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN
| typeof SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR
| typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT
Expand Down
7 changes: 7 additions & 0 deletions packages/errors/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SOLANA_ERROR__CODECS_FIXED_SIZE_ENCODER_DECODER_SIZE_MISMATCH,
SOLANA_ERROR__CODECS_INVALID_DATA_ENUM_VARIANT,
SOLANA_ERROR__CODECS_INVALID_SCALAR_ENUM_VARIANT,
SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE,
SOLANA_ERROR__CODECS_VARIABLE_SIZE_ENCODER_DECODER_MAX_SIZE_MISMATCH,
SOLANA_ERROR__CODECS_WRONG_NUMBER_OF_BYTES,
SOLANA_ERROR__CODECS_WRONG_NUMBER_OF_ITEMS,
Expand Down Expand Up @@ -196,6 +197,12 @@ export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined<
value: number | string;
variants: string[];
};
[SOLANA_ERROR__CODECS_NUMBER_OUT_OF_RANGE]: {
codecDescription: string;
max: bigint | number;
min: bigint | number;
value: bigint | number;
};
[SOLANA_ERROR__CODECS_VARIABLE_SIZE_ENCODER_DECODER_MAX_SIZE_MISMATCH]: {
decoderMaxSize: number | undefined;
encoderMaxSize: number | undefined;
Expand Down
Loading

0 comments on commit 27bf3a2

Please sign in to comment.