ReefVM/tests/functions-parameter.test.ts

233 lines
4.8 KiB
TypeScript

import { test, expect, describe } from "bun:test"
import { run, VM } from "#index"
import { toBytecode } from "#bytecode"
describe("functions parameter", () => {
test("pass functions to run()", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE add
PUSH 5
PUSH 3
PUSH 2
PUSH 0
CALL
HALT
`)
const result = await run(bytecode, {
add: (a: number, b: number) => a + b
})
expect(result).toEqual({ type: 'number', value: 8 })
})
test("pass functions to VM constructor", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE multiply
PUSH 10
PUSH 2
PUSH 2
PUSH 0
CALL
HALT
`)
const vm = new VM(bytecode, {
multiply: (a: number, b: number) => a * b
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 20 })
})
test("pass multiple functions", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE add
PUSH 10
PUSH 5
PUSH 2
PUSH 0
CALL
STORE sum
LOAD_NATIVE multiply
LOAD sum
PUSH 3
PUSH 2
PUSH 0
CALL
HALT
`)
const result = await run(bytecode, {
add: (a: number, b: number) => a + b,
multiply: (a: number, b: number) => a * b
})
expect(result).toEqual({ type: 'number', value: 45 })
})
test("auto-wraps native functions", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE concat
PUSH "hello"
PUSH "world"
PUSH 2
PUSH 0
CALL
HALT
`)
const result = await run(bytecode, {
concat: (a: string, b: string) => a + " " + b
})
expect(result).toEqual({ type: 'string', value: 'hello world' })
})
test("works with async functions", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE delay
PUSH 100
PUSH 1
PUSH 0
CALL
HALT
`)
const result = await run(bytecode, {
delay: async (n: number) => {
await new Promise(resolve => setTimeout(resolve, 1))
return n * 2
}
})
expect(result).toEqual({ type: 'number', value: 200 })
})
test("can combine with manual registerFunction", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE add
PUSH 5
PUSH 3
PUSH 2
PUSH 0
CALL
STORE sum
LOAD_NATIVE subtract
LOAD sum
PUSH 2
PUSH 2
PUSH 0
CALL
HALT
`)
const vm = new VM(bytecode, {
add: (a: number, b: number) => a + b
})
// Register another function manually
vm.registerFunction('subtract', (a: number, b: number) => a - b)
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 6 })
})
test("no functions parameter (undefined)", async () => {
const bytecode = toBytecode(`
PUSH 42
HALT
`)
const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 42 })
})
test("empty functions object", async () => {
const bytecode = toBytecode(`
PUSH 99
HALT
`)
const result = await run(bytecode, {})
expect(result).toEqual({ type: 'number', value: 99 })
})
test("function throws error", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE divide
PUSH 0
PUSH 1
PUSH 0
CALL
HALT
`)
try {
await run(bytecode, {
divide: (n: number) => {
if (n === 0) throw new Error("Cannot divide by zero")
return 100 / n
}
})
expect(true).toBe(false) // Should not reach here
} catch (e: any) {
expect(e.message).toContain("Cannot divide by zero")
}
})
test("complex workflow with multiple function calls", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE add
PUSH 5
PUSH 3
PUSH 2
PUSH 0
CALL
STORE result
LOAD_NATIVE multiply
LOAD result
PUSH 2
PUSH 2
PUSH 0
CALL
STORE final
LOAD_NATIVE format
LOAD final
PUSH 1
PUSH 0
CALL
HALT
`)
const result = await run(bytecode, {
add: (a: number, b: number) => a + b,
multiply: (a: number, b: number) => a * b,
format: (n: number) => `Result: ${n}`
})
expect(result).toEqual({ type: 'string', value: 'Result: 16' })
})
test("function overriding - later registration wins", async () => {
const bytecode = toBytecode(`
LOAD_NATIVE getValue
PUSH 5
PUSH 1
PUSH 0
CALL
HALT
`)
const vm = new VM(bytecode, {
getValue: () => 100
})
// Override with manual registration
vm.registerFunction('getValue', () => 200)
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 200 })
})
})