Skip to content

Commit ccc57f6

Browse files
committed
test(@probitas/probitas): add serialization edge case scenarios
JSON.stringify cannot handle various JavaScript types (BigInt, Symbol, Function, circular references, etc.), which could cause failures in IPC-based step context passing. These scenarios verify the framework handles these edge cases correctly. Covers: BigInt, Function, Symbol, undefined, circular references, Map, Set, RegExp, Date, Error, TypedArray, ArrayBuffer, DataView. Addresses #80
1 parent e5fb7c1 commit ccc57f6

10 files changed

Lines changed: 260 additions & 0 deletions

probitas/80-bigint.probitas.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* BigInt Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("BigInt Serialization")
7+
.step("Return BigInt value", () => {
8+
// BigInt cannot be serialized by JSON.stringify
9+
return {
10+
value: BigInt(9007199254740991),
11+
timestamp: BigInt(Date.now()),
12+
};
13+
})
14+
.step("This step should also run", (ctx) => {
15+
// This step accesses the BigInt values
16+
return {
17+
received: typeof ctx.previous.value === "bigint",
18+
};
19+
})
20+
.build();

probitas/81-function.probitas.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Function Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Function Serialization")
7+
.step("Return Function value", () => {
8+
// Functions are omitted when serialized by JSON.stringify
9+
return {
10+
callback: () => "hello",
11+
method: function namedFn() {
12+
return 42;
13+
},
14+
arrow: (x: number) => x * 2,
15+
};
16+
})
17+
.step("This step should also run", (ctx) => {
18+
// This step accesses the Function values
19+
return {
20+
hasCallback: typeof ctx.previous.callback === "function",
21+
hasMethod: typeof ctx.previous.method === "function",
22+
hasArrow: typeof ctx.previous.arrow === "function",
23+
};
24+
})
25+
.build();

probitas/82-symbol.probitas.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Symbol Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Symbol Serialization")
7+
.step("Return Symbol value", () => {
8+
// Symbols are omitted when serialized by JSON.stringify
9+
return {
10+
id: Symbol("unique-id"),
11+
tag: Symbol.for("global-tag"),
12+
iterator: Symbol.iterator,
13+
};
14+
})
15+
.step("This step should also run", (ctx) => {
16+
// This step accesses the Symbol values
17+
return {
18+
hasId: typeof ctx.previous.id === "symbol",
19+
hasTag: typeof ctx.previous.tag === "symbol",
20+
hasIterator: typeof ctx.previous.iterator === "symbol",
21+
};
22+
})
23+
.build();

probitas/83-circular.probitas.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Circular Reference Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Circular Reference Serialization")
7+
.step("Return circular reference", () => {
8+
// Circular references cause TypeError in JSON.stringify
9+
const obj: Record<string, unknown> = { name: "parent" };
10+
obj.self = obj;
11+
return obj;
12+
})
13+
.step("This step should also run", (ctx) => {
14+
// This step accesses the circular reference
15+
return {
16+
hasName: ctx.previous.name === "parent",
17+
hasSelf: ctx.previous.self === ctx.previous,
18+
};
19+
})
20+
.build();

probitas/84-undefined.probitas.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Undefined Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Undefined Serialization")
7+
.step("Return undefined values", () => {
8+
// undefined values in object properties are omitted by JSON.stringify
9+
// undefined in arrays becomes null
10+
return {
11+
explicit: undefined,
12+
nested: {
13+
value: undefined,
14+
defined: "exists",
15+
},
16+
array: [1, undefined, 3],
17+
};
18+
})
19+
.step("This step should also run", (ctx) => {
20+
// This step accesses the undefined values
21+
return {
22+
hasExplicit: "explicit" in ctx.previous,
23+
explicitIsUndefined: ctx.previous.explicit === undefined,
24+
nestedValueIsUndefined: ctx.previous.nested.value === undefined,
25+
nestedDefinedExists: ctx.previous.nested.defined === "exists",
26+
arraySecondIsUndefined: ctx.previous.array[1] === undefined,
27+
};
28+
})
29+
.build();

probitas/85-map-set.probitas.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Map/Set Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Map/Set Serialization")
7+
.step("Return Map and Set values", () => {
8+
// Map and Set are serialized as empty objects {} by JSON.stringify
9+
const map = new Map<string, number>([
10+
["a", 1],
11+
["b", 2],
12+
]);
13+
const set = new Set([1, 2, 3]);
14+
return {
15+
map,
16+
set,
17+
weakMap: new WeakMap(),
18+
weakSet: new WeakSet(),
19+
};
20+
})
21+
.step("This step should also run", (ctx) => {
22+
// This step accesses the Map and Set values
23+
return {
24+
mapIsMap: ctx.previous.map instanceof Map,
25+
mapSize: ctx.previous.map.size,
26+
setIsSet: ctx.previous.set instanceof Set,
27+
setSize: ctx.previous.set.size,
28+
};
29+
})
30+
.build();

probitas/86-regexp.probitas.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* RegExp Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("RegExp Serialization")
7+
.step("Return RegExp values", () => {
8+
// RegExp is serialized as empty object {} by JSON.stringify
9+
return {
10+
pattern: /hello\s+world/gi,
11+
email: new RegExp("^[a-z]+@[a-z]+\\.[a-z]+$", "i"),
12+
unicode: /\p{Script=Hiragana}+/u,
13+
};
14+
})
15+
.step("This step should also run", (ctx) => {
16+
// This step accesses the RegExp values
17+
return {
18+
patternIsRegExp: ctx.previous.pattern instanceof RegExp,
19+
patternSource: ctx.previous.pattern.source,
20+
emailIsRegExp: ctx.previous.email instanceof RegExp,
21+
unicodeFlags: ctx.previous.unicode.flags,
22+
};
23+
})
24+
.build();

probitas/87-error.probitas.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Error Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Error Serialization")
7+
.step("Return Error values", () => {
8+
// Error objects lose most properties when serialized by JSON.stringify
9+
// Only enumerable own properties are kept (message, name, stack are not enumerable)
10+
const error = new Error("Something went wrong");
11+
const typeError = new TypeError("Invalid type");
12+
const customError = Object.assign(new Error("Custom"), {
13+
code: "ERR_CUSTOM",
14+
details: { foo: "bar" },
15+
});
16+
return {
17+
error,
18+
typeError,
19+
customError,
20+
};
21+
})
22+
.step("This step should also run", (ctx) => {
23+
// This step accesses the Error values
24+
return {
25+
errorIsError: ctx.previous.error instanceof Error,
26+
errorMessage: ctx.previous.error.message,
27+
errorHasStack: typeof ctx.previous.error.stack === "string",
28+
typeErrorName: ctx.previous.typeError.name,
29+
customErrorCode: ctx.previous.customError.code,
30+
};
31+
})
32+
.build();

probitas/88-date.probitas.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Date Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("Date Serialization")
7+
.step("Return Date values", () => {
8+
// Date is serialized as ISO string by JSON.stringify (loses type info)
9+
return {
10+
now: new Date(),
11+
epoch: new Date(0),
12+
specific: new Date("2024-01-15T12:00:00Z"),
13+
invalid: new Date("invalid"),
14+
};
15+
})
16+
.step("This step should also run", (ctx) => {
17+
// This step accesses the Date values
18+
return {
19+
nowIsDate: ctx.previous.now instanceof Date,
20+
nowTime: ctx.previous.now.getTime(),
21+
epochIsDate: ctx.previous.epoch instanceof Date,
22+
epochTime: ctx.previous.epoch.getTime(),
23+
specificYear: ctx.previous.specific.getUTCFullYear(),
24+
invalidIsNaN: Number.isNaN(ctx.previous.invalid.getTime()),
25+
};
26+
})
27+
.build();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* TypedArray Serialization Test Scenario
3+
*/
4+
import { scenario } from "jsr:@probitas/probitas@^0";
5+
6+
export default scenario("TypedArray Serialization")
7+
.step("Return TypedArray values", () => {
8+
// TypedArray is serialized as object with numeric keys by JSON.stringify
9+
// ArrayBuffer cannot be directly serialized
10+
return {
11+
uint8: new Uint8Array([1, 2, 3, 4]),
12+
int32: new Int32Array([100, 200, -300]),
13+
float64: new Float64Array([1.5, 2.5, 3.5]),
14+
buffer: new ArrayBuffer(8),
15+
dataView: new DataView(new ArrayBuffer(4)),
16+
};
17+
})
18+
.step("This step should also run", (ctx) => {
19+
// This step accesses the TypedArray values
20+
return {
21+
uint8IsTypedArray: ctx.previous.uint8 instanceof Uint8Array,
22+
uint8Length: ctx.previous.uint8.length,
23+
uint8FirstValue: ctx.previous.uint8[0],
24+
int32IsTypedArray: ctx.previous.int32 instanceof Int32Array,
25+
float64Sum: ctx.previous.float64[0] + ctx.previous.float64[1],
26+
bufferIsArrayBuffer: ctx.previous.buffer instanceof ArrayBuffer,
27+
bufferByteLength: ctx.previous.buffer.byteLength,
28+
};
29+
})
30+
.build();

0 commit comments

Comments
 (0)