chore: generate
This commit is contained in:
parent
cb93114424
commit
83c638eaac
20 changed files with 2137 additions and 1207 deletions
|
|
@ -40,7 +40,11 @@ describe("Date", () => {
|
|||
})
|
||||
|
||||
test("UTC getters read calendar components", async () => {
|
||||
expect(await value(`const d = new Date("2024-03-05T06:07:08.009Z"); return [d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds()]`)).toEqual([2024, 2, 5, 6, 7, 8, 9])
|
||||
expect(
|
||||
await value(
|
||||
`const d = new Date("2024-03-05T06:07:08.009Z"); return [d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds()]`,
|
||||
),
|
||||
).toEqual([2024, 2, 5, 6, 7, 8, 9])
|
||||
})
|
||||
|
||||
test("invalid dates yield NaN times, guardable in-sandbox", async () => {
|
||||
|
|
@ -49,7 +53,9 @@ describe("Date", () => {
|
|||
})
|
||||
|
||||
test("toISOString on an invalid date is a catchable error", async () => {
|
||||
expect(await value(`try { new Date("garbage").toISOString(); return "no" } catch { return "caught" }`)).toBe("caught")
|
||||
expect(await value(`try { new Date("garbage").toISOString(); return "no" } catch { return "caught" }`)).toBe(
|
||||
"caught",
|
||||
)
|
||||
})
|
||||
|
||||
test("template interpolation renders the ISO form", async () => {
|
||||
|
|
@ -72,14 +78,18 @@ describe("Date", () => {
|
|||
})
|
||||
|
||||
test("sorting dates with a numeric comparator", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const dates = [new Date(3000), new Date(1000), new Date(2000)]
|
||||
return dates.sort((a, b) => a - b).map((d) => d.getTime())
|
||||
`)).toEqual([1000, 2000, 3000])
|
||||
`),
|
||||
).toEqual([1000, 2000, 3000])
|
||||
})
|
||||
|
||||
test("new Date(year, month, day) accepts component form", async () => {
|
||||
expect(await value(`const d = new Date(2024, 0, 2); return [d.getFullYear(), d.getMonth(), d.getDate()]`)).toEqual([2024, 0, 2])
|
||||
expect(await value(`const d = new Date(2024, 0, 2); return [d.getFullYear(), d.getMonth(), d.getDate()]`)).toEqual([
|
||||
2024, 0, 2,
|
||||
])
|
||||
})
|
||||
|
||||
test("typeof and unknown properties are forgiving", async () => {
|
||||
|
|
@ -95,25 +105,31 @@ describe("RegExp", () => {
|
|||
})
|
||||
|
||||
test("exec exposes captures and index", async () => {
|
||||
expect(await value(`const m = /a(b+)/.exec("xxabbc"); return { full: m[0], group: m[1], index: m.index }`)).toEqual({
|
||||
full: "abb",
|
||||
group: "bb",
|
||||
index: 2,
|
||||
})
|
||||
expect(await value(`const m = /a(b+)/.exec("xxabbc"); return { full: m[0], group: m[1], index: m.index }`)).toEqual(
|
||||
{
|
||||
full: "abb",
|
||||
group: "bb",
|
||||
index: 2,
|
||||
},
|
||||
)
|
||||
expect(await value(`return /a/.exec("zzz")`)).toBeNull()
|
||||
})
|
||||
|
||||
test("named groups read through", async () => {
|
||||
expect(await value(`const m = /(?<word>[a-z]+)-(?<num>\\d+)/.exec("id ab-42"); return m.groups.word + m.groups.num`)).toBe("ab42")
|
||||
expect(
|
||||
await value(`const m = /(?<word>[a-z]+)-(?<num>\\d+)/.exec("id ab-42"); return m.groups.word + m.groups.num`),
|
||||
).toBe("ab42")
|
||||
})
|
||||
|
||||
test("global exec advances lastIndex across calls", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const r = /\\d+/g
|
||||
const first = r.exec("a1b22c")
|
||||
const second = r.exec("a1b22c")
|
||||
return [first[0], second[0]]
|
||||
`)).toEqual(["1", "22"])
|
||||
`),
|
||||
).toEqual(["1", "22"])
|
||||
})
|
||||
|
||||
test("string match: non-global carries index, global lists all matches", async () => {
|
||||
|
|
@ -194,34 +210,51 @@ describe("RegExp", () => {
|
|||
|
||||
describe("Map", () => {
|
||||
test("get/set/has/size with chaining", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const m = new Map()
|
||||
m.set("a", 1).set("b", 2)
|
||||
return { a: m.get("a"), b: m.get("b"), has: m.has("a"), miss: m.get("zz") === undefined, size: m.size }
|
||||
`)).toEqual({ a: 1, b: 2, has: true, miss: true, size: 5 - 3 })
|
||||
`),
|
||||
).toEqual({ a: 1, b: 2, has: true, miss: true, size: 5 - 3 })
|
||||
})
|
||||
|
||||
test("object keys use identity", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const key = { id: 1 }
|
||||
const m = new Map()
|
||||
m.set(key, "hit")
|
||||
return [m.get(key), m.get({ id: 1 }) === undefined]
|
||||
`)).toEqual(["hit", true])
|
||||
`),
|
||||
).toEqual(["hit", true])
|
||||
})
|
||||
|
||||
test("construction from entry pairs and another Map", async () => {
|
||||
expect(await value(`const m = new Map([["a", 1], ["b", 2]]); return m.get("b")`)).toBe(2)
|
||||
expect(await value(`const m = new Map([["a", 1]]); const n = new Map(m); n.set("b", 2); return [n.get("a"), n.get("b"), m.has("b")]`)).toEqual([1, 2, false])
|
||||
expect(
|
||||
await value(
|
||||
`const m = new Map([["a", 1]]); const n = new Map(m); n.set("b", 2); return [n.get("a"), n.get("b"), m.has("b")]`,
|
||||
),
|
||||
).toEqual([1, 2, false])
|
||||
expect((await error(`return new Map("nope")`)).message).toMatch(/\[key, value\] pairs/)
|
||||
expect((await error(`return new Map(["flat"])`)).message).toMatch(/\[key, value\] pairs/)
|
||||
})
|
||||
|
||||
test("keys/values/entries return arrays", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const m = new Map([["a", 1], ["b", 2]])
|
||||
return { keys: m.keys(), values: m.values(), entries: m.entries() }
|
||||
`)).toEqual({ keys: ["a", "b"], values: [1, 2], entries: [["a", 1], ["b", 2]] })
|
||||
`),
|
||||
).toEqual({
|
||||
keys: ["a", "b"],
|
||||
values: [1, 2],
|
||||
entries: [
|
||||
["a", 1],
|
||||
["b", 2],
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
test("Object.fromEntries(map) and Array.from(map)", async () => {
|
||||
|
|
@ -230,13 +263,15 @@ describe("Map", () => {
|
|||
})
|
||||
|
||||
test("for...of iterates [key, value] pairs with destructuring", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const m = new Map([["a", 1], ["b", 2]])
|
||||
let total = 0
|
||||
let names = ""
|
||||
for (const [key, count] of m) { names += key; total += count }
|
||||
return names + total
|
||||
`)).toBe("ab3")
|
||||
`),
|
||||
).toBe("ab3")
|
||||
})
|
||||
|
||||
test("spread produces entry pairs", async () => {
|
||||
|
|
@ -244,32 +279,38 @@ describe("Map", () => {
|
|||
})
|
||||
|
||||
test("forEach passes (value, key)", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const m = new Map([["a", 1], ["b", 2]])
|
||||
const seen = []
|
||||
m.forEach((count, key) => seen.push(key + count))
|
||||
return seen
|
||||
`)).toEqual(["a1", "b2"])
|
||||
`),
|
||||
).toEqual(["a1", "b2"])
|
||||
})
|
||||
|
||||
test("delete and clear", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const m = new Map([["a", 1], ["b", 2]])
|
||||
const removed = m.delete("a")
|
||||
const missed = m.delete("zz")
|
||||
const sizeAfterDelete = m.size
|
||||
m.clear()
|
||||
return [removed, missed, sizeAfterDelete, m.size]
|
||||
`)).toEqual([true, false, 1, 0])
|
||||
`),
|
||||
).toEqual([true, false, 1, 0])
|
||||
})
|
||||
|
||||
test("counting idiom: grouped tallies", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const words = ["a", "b", "a", "c", "a"]
|
||||
const counts = new Map()
|
||||
for (const word of words) counts.set(word, (counts.get(word) ?? 0) + 1)
|
||||
return Object.fromEntries(counts)
|
||||
`)).toEqual({ a: 3, b: 1, c: 1 })
|
||||
`),
|
||||
).toEqual({ a: 3, b: 1, c: 1 })
|
||||
})
|
||||
|
||||
test("maps serialize to {} at the boundary, like JSON", async () => {
|
||||
|
|
@ -286,12 +327,14 @@ describe("Map", () => {
|
|||
|
||||
describe("Set", () => {
|
||||
test("add/has/delete/size with chaining", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const s = new Set()
|
||||
s.add(1).add(2).add(1)
|
||||
const removed = s.delete(2)
|
||||
return [s.size, s.has(1), s.has(2), removed]
|
||||
`)).toEqual([1, true, false, true])
|
||||
`),
|
||||
).toEqual([1, true, false, true])
|
||||
})
|
||||
|
||||
test("dedupe idiom: [...new Set(items)]", async () => {
|
||||
|
|
@ -308,11 +351,13 @@ describe("Set", () => {
|
|||
})
|
||||
|
||||
test("for...of iterates values", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
let total = 0
|
||||
for (const n of new Set([1, 2, 3])) total += n
|
||||
return total
|
||||
`)).toBe(6)
|
||||
`),
|
||||
).toBe(6)
|
||||
})
|
||||
|
||||
test("sets serialize to {} at the boundary, like JSON", async () => {
|
||||
|
|
@ -338,21 +383,32 @@ describe("stdlib integration", () => {
|
|||
})
|
||||
|
||||
test("dates inside Map values survive in-sandbox reads", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const m = new Map([["start", new Date(1000)]])
|
||||
return m.get("start").getTime()
|
||||
`)).toBe(1000)
|
||||
`),
|
||||
).toBe(1000)
|
||||
})
|
||||
|
||||
test("instanceof recognizes the stdlib value types", async () => {
|
||||
expect(await value(`return [new Date(0) instanceof Date, /a/ instanceof RegExp, new Map() instanceof Map, new Set() instanceof Set]`)).toEqual([true, true, true, true])
|
||||
expect(await value(`return [[1] instanceof Array, [1] instanceof Object, ({}) instanceof Object, 5 instanceof Object]`)).toEqual([true, true, true, false])
|
||||
expect(
|
||||
await value(
|
||||
`return [new Date(0) instanceof Date, /a/ instanceof RegExp, new Map() instanceof Map, new Set() instanceof Set]`,
|
||||
),
|
||||
).toEqual([true, true, true, true])
|
||||
expect(
|
||||
await value(`return [[1] instanceof Array, [1] instanceof Object, ({}) instanceof Object, 5 instanceof Object]`),
|
||||
).toEqual([true, true, true, false])
|
||||
expect(await value(`return [new Map() instanceof Set, "s" instanceof Date]`)).toEqual([false, false])
|
||||
expect(await value(`const p = Promise.resolve(1); const isPromise = p instanceof Promise; await p; return isPromise`)).toBe(true)
|
||||
expect(
|
||||
await value(`const p = Promise.resolve(1); const isPromise = p instanceof Promise; await p; return isPromise`),
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
test("realistic pipeline: parse, extract with regex, dedupe, count by day", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const raw = '[{"at":"2024-01-01T05:00:00Z","tag":"a b"},{"at":"2024-01-01T09:00:00Z","tag":"b c"},{"at":"2024-01-02T01:00:00Z","tag":"a"}]'
|
||||
const rows = JSON.parse(raw)
|
||||
const tags = new Set()
|
||||
|
|
@ -363,27 +419,34 @@ describe("stdlib integration", () => {
|
|||
byDay.set(day, (byDay.get(day) ?? 0) + 1)
|
||||
}
|
||||
return { tags: [...tags].sort((a, b) => (a < b ? -1 : 1)), byDay: Object.fromEntries(byDay) }
|
||||
`)).toEqual({ tags: ["a", "b", "c"], byDay: { "2024-01-01": 2, "2024-01-02": 1 } })
|
||||
`),
|
||||
).toEqual({ tags: ["a", "b", "c"], byDay: { "2024-01-01": 2, "2024-01-02": 1 } })
|
||||
})
|
||||
})
|
||||
|
||||
describe("sandbox values at intra-sandbox checkpoints", () => {
|
||||
test("Object.values/entries keep Dates usable", async () => {
|
||||
expect(await value(`return Object.values({ d: new Date(0) })[0].getTime()`)).toBe(0)
|
||||
expect(await value(`const [key, d] = Object.entries({ d: new Date(0) })[0]; return key + ":" + d.getTime()`)).toBe("d:0")
|
||||
expect(await value(`const [key, d] = Object.entries({ d: new Date(0) })[0]; return key + ":" + d.getTime()`)).toBe(
|
||||
"d:0",
|
||||
)
|
||||
})
|
||||
|
||||
test("Object.assign keeps Maps usable", async () => {
|
||||
expect(await value(`const merged = Object.assign({}, { m: new Map([["a", 1]]) }); return merged.m.get("a")`)).toBe(1)
|
||||
expect(await value(`const merged = Object.assign({}, { m: new Map([["a", 1]]) }); return merged.m.get("a")`)).toBe(
|
||||
1,
|
||||
)
|
||||
})
|
||||
|
||||
test("object and array spread keep sandbox values usable", async () => {
|
||||
expect(await value(`
|
||||
expect(
|
||||
await value(`
|
||||
const src = { m: new Map([["a", 1]]) }
|
||||
const copy = { ...src }
|
||||
copy.m.set("b", 2)
|
||||
return [copy.m.get("a"), src.m.get("b")]
|
||||
`)).toEqual([1, 2])
|
||||
`),
|
||||
).toEqual([1, 2])
|
||||
expect(await value(`const list = [new Date(1000)]; const copy = [...list]; return copy[0].getTime()`)).toBe(1000)
|
||||
})
|
||||
|
||||
|
|
@ -404,7 +467,10 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
|
|||
})
|
||||
|
||||
test("the host boundary still serializes JSON forms: results, JSON.stringify, and tool arguments", async () => {
|
||||
expect(await value(`return { d: new Date(0), m: new Map([["a", 1]]) }`)).toEqual({ d: "1970-01-01T00:00:00.000Z", m: {} })
|
||||
expect(await value(`return { d: new Date(0), m: new Map([["a", 1]]) }`)).toEqual({
|
||||
d: "1970-01-01T00:00:00.000Z",
|
||||
m: {},
|
||||
})
|
||||
expect(await value(`return JSON.stringify({ d: new Date(0) })`)).toBe('{"d":"1970-01-01T00:00:00.000Z"}')
|
||||
|
||||
const observed: Array<unknown> = []
|
||||
|
|
@ -417,10 +483,12 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
|
|||
return "ok"
|
||||
}),
|
||||
})
|
||||
const result = await Effect.runPromise(CodeMode.execute({
|
||||
tools: { host: { capture } },
|
||||
code: `return await tools.host.capture({ when: new Date(0), tags: new Map([["a", 1]]) })`,
|
||||
}))
|
||||
const result = await Effect.runPromise(
|
||||
CodeMode.execute({
|
||||
tools: { host: { capture } },
|
||||
code: `return await tools.host.capture({ when: new Date(0), tags: new Map([["a", 1]]) })`,
|
||||
}),
|
||||
)
|
||||
expect(result.ok).toBe(true)
|
||||
expect(observed).toStrictEqual([{ when: "1970-01-01T00:00:00.000Z", tags: {} }])
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue