Skip to content

Commit f226715

Browse files
committed
test environment
1 parent b434ed2 commit f226715

File tree

2 files changed

+172
-2
lines changed

2 files changed

+172
-2
lines changed

src/client/environment.test.ts

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe("environment patching units", () => {
2424
const patchedMath = patchMath(originalMath);
2525

2626
expect(() => patchedMath.random()).toThrow(
27-
"Math.random() isn't currently supported within workflows",
27+
"Math.random() isn't yet supported within workflows",
2828
);
2929
});
3030

@@ -110,7 +110,10 @@ describe("environment patching units", () => {
110110

111111
expect(DeterministicDate.parse).toBe(originalDate.parse);
112112
expect(DeterministicDate.UTC).toBe(originalDate.UTC);
113+
114+
// Prototype should be the same as original (no patching currently)
113115
expect(DeterministicDate.prototype).toBe(originalDate.prototype);
116+
expect(DeterministicDate.prototype.constructor).toBe(DeterministicDate);
114117
});
115118

116119
it("should not affect the original Date constructor", () => {
@@ -121,6 +124,167 @@ describe("environment patching units", () => {
121124
// Original Date should be unchanged
122125
expect(Date.now).toBe(originalNow);
123126
});
127+
128+
describe("behavior validation vs original Date", () => {
129+
it("should produce identical outputs for specific dates", () => {
130+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
131+
132+
// Test with specific timestamps
133+
const timestamps = [
134+
0, // Unix epoch
135+
946684800000, // Y2K
136+
1640995200000, // 2022-01-01
137+
2023, 0, 15, 10, 30, 45, 123 // Feb 15, 2023 10:30:45.123
138+
];
139+
140+
for (const ts of [timestamps[0], timestamps[1], timestamps[2]]) {
141+
const original = new Date(ts);
142+
const deterministic = new DeterministicDate(ts);
143+
144+
expect(deterministic.getTime()).toBe(original.getTime());
145+
expect(deterministic.getFullYear()).toBe(original.getFullYear());
146+
expect(deterministic.getMonth()).toBe(original.getMonth());
147+
expect(deterministic.getDate()).toBe(original.getDate());
148+
expect(deterministic.getHours()).toBe(original.getHours());
149+
expect(deterministic.getMinutes()).toBe(original.getMinutes());
150+
expect(deterministic.getSeconds()).toBe(original.getSeconds());
151+
expect(deterministic.getMilliseconds()).toBe(original.getMilliseconds());
152+
}
153+
154+
// Test with constructor args
155+
const original = new Date(2023, 0, 15, 10, 30, 45, 123);
156+
const deterministic = new DeterministicDate(2023, 0, 15, 10, 30, 45, 123);
157+
158+
expect(deterministic.getFullYear()).toBe(original.getFullYear());
159+
expect(deterministic.getMonth()).toBe(original.getMonth());
160+
expect(deterministic.getDate()).toBe(original.getDate());
161+
expect(deterministic.getHours()).toBe(original.getHours());
162+
expect(deterministic.getMinutes()).toBe(original.getMinutes());
163+
expect(deterministic.getSeconds()).toBe(original.getSeconds());
164+
expect(deterministic.getMilliseconds()).toBe(original.getMilliseconds());
165+
});
166+
167+
it("should produce identical string representations for deterministic dates", () => {
168+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
169+
170+
const timestamp = 1640995200000; // 2022-01-01T00:00:00.000Z
171+
const original = new Date(timestamp);
172+
const deterministic = new DeterministicDate(timestamp);
173+
174+
expect(deterministic.toISOString()).toBe(original.toISOString());
175+
expect(deterministic.toUTCString()).toBe(original.toUTCString());
176+
expect(deterministic.toDateString()).toBe(original.toDateString());
177+
expect(deterministic.toTimeString()).toBe(original.toTimeString());
178+
expect(deterministic.toJSON()).toBe(original.toJSON());
179+
expect(deterministic.valueOf()).toBe(original.valueOf());
180+
expect(deterministic.getTime()).toBe(original.getTime());
181+
});
182+
183+
it("should handle UTC methods identically", () => {
184+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
185+
186+
const timestamp = 1640995200123; // 2022-01-01T00:00:00.123Z
187+
const original = new Date(timestamp);
188+
const deterministic = new DeterministicDate(timestamp);
189+
190+
expect(deterministic.getUTCFullYear()).toBe(original.getUTCFullYear());
191+
expect(deterministic.getUTCMonth()).toBe(original.getUTCMonth());
192+
expect(deterministic.getUTCDate()).toBe(original.getUTCDate());
193+
expect(deterministic.getUTCHours()).toBe(original.getUTCHours());
194+
expect(deterministic.getUTCMinutes()).toBe(original.getUTCMinutes());
195+
expect(deterministic.getUTCSeconds()).toBe(original.getUTCSeconds());
196+
expect(deterministic.getUTCMilliseconds()).toBe(original.getUTCMilliseconds());
197+
expect(deterministic.getUTCDay()).toBe(original.getUTCDay());
198+
});
199+
200+
it("should handle static methods identically", () => {
201+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
202+
203+
const dateString = "2023-01-15T10:30:45.123Z";
204+
const year = 2023;
205+
const month = 0; // January
206+
const day = 15;
207+
const hour = 10;
208+
const minute = 30;
209+
const second = 45;
210+
const ms = 123;
211+
212+
expect(DeterministicDate.parse(dateString)).toBe(Date.parse(dateString));
213+
expect(DeterministicDate.UTC(year, month, day, hour, minute, second, ms))
214+
.toBe(Date.UTC(year, month, day, hour, minute, second, ms));
215+
});
216+
217+
it("should maintain Date compatibility", () => {
218+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
219+
220+
const date = new DeterministicDate(2023, 0, 1);
221+
222+
// Should be an instance of Date (important for type compatibility)
223+
expect(date instanceof Date).toBe(true);
224+
225+
// Should have all expected Date methods
226+
expect(typeof date.getTime).toBe("function");
227+
expect(typeof date.getFullYear).toBe("function");
228+
expect(typeof date.toISOString).toBe("function");
229+
expect(typeof date.getTimezoneOffset).toBe("function");
230+
expect(typeof date.toLocaleString).toBe("function");
231+
});
232+
233+
it("should handle Date modification methods correctly", () => {
234+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
235+
236+
const timestamp = 1640995200000; // 2022-01-01
237+
const original = new Date(timestamp);
238+
const deterministic = new DeterministicDate(timestamp);
239+
240+
// Test setters
241+
const newTime = 1641081600000; // 2022-01-02
242+
original.setTime(newTime);
243+
deterministic.setTime(newTime);
244+
245+
expect(deterministic.getTime()).toBe(original.getTime());
246+
247+
// Test setFullYear
248+
original.setFullYear(2024);
249+
deterministic.setFullYear(2024);
250+
251+
expect(deterministic.getFullYear()).toBe(original.getFullYear());
252+
expect(deterministic.getTime()).toBe(original.getTime());
253+
});
254+
255+
it("should have timezone and locale methods available for future patching", () => {
256+
const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState);
257+
const date = new DeterministicDate(1640995200000); // 2022-01-01T00:00:00.000Z
258+
259+
// These methods exist but are not yet fully patched for determinism
260+
// They currently still use system timezone/locale settings
261+
expect(typeof date.getTimezoneOffset).toBe("function");
262+
expect(typeof date.toLocaleString).toBe("function");
263+
expect(typeof date.toLocaleDateString).toBe("function");
264+
expect(typeof date.toLocaleTimeString).toBe("function");
265+
266+
// The methods work but results depend on system settings
267+
const timezoneOffset = date.getTimezoneOffset();
268+
const localeString = date.toLocaleString();
269+
const localeDateString = date.toLocaleDateString();
270+
const localeTimeString = date.toLocaleTimeString();
271+
272+
expect(typeof timezoneOffset).toBe("number");
273+
expect(typeof localeString).toBe("string");
274+
expect(typeof localeDateString).toBe("string");
275+
expect(typeof localeTimeString).toBe("string");
276+
277+
// Should be consistent when called multiple times
278+
expect(date.getTimezoneOffset()).toBe(timezoneOffset);
279+
expect(date.toLocaleString()).toBe(localeString);
280+
expect(date.toLocaleDateString()).toBe(localeDateString);
281+
expect(date.toLocaleTimeString()).toBe(localeTimeString);
282+
283+
// TODO: These methods should be patched for full determinism:
284+
// - getTimezoneOffset() should return 0 (UTC)
285+
// - locale methods should use fixed locale (en-US) and UTC timezone
286+
});
287+
});
124288
});
125289

126290
describe("createConsole", () => {

src/client/environment.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export function patchMath(math: typeof Math): typeof Math {
1616

1717
// Override random to throw
1818
patchedMath.random = () => {
19-
console.trace("calling random");
2019
throw new Error("Math.random() isn't yet supported within workflows");
2120
};
2221

@@ -52,6 +51,13 @@ export function createDeterministicDate(
5251
DeterministicDate.prototype = originalDate.prototype;
5352
DeterministicDate.prototype.constructor = DeterministicDate as typeof Date;
5453

54+
// TODO: Additional methods that should be patched for full determinism:
55+
// - getTimezoneOffset() - should return 0 (UTC)
56+
// - toLocaleString() - should use fixed locale (en-US) and UTC timezone
57+
// - toLocaleDateString() - should use fixed locale (en-US) and UTC timezone
58+
// - toLocaleTimeString() - should use fixed locale (en-US) and UTC timezone
59+
// These would require more complex prototype manipulation to work correctly.
60+
5561
return DeterministicDate as typeof Date;
5662
}
5763

0 commit comments

Comments
 (0)