From 85f6c5999733d242f46864fcc66808633a6e5d65 Mon Sep 17 00:00:00 2001 From: Scott Prutton Date: Mon, 23 Dec 2024 16:39:00 -0500 Subject: [PATCH] working tests! --- bin/lang-js/BUCK | 6 +- bin/lang-js/src/debug.ts | 7 +- bin/lang-js/src/function.ts | 42 ++---- bin/lang-js/src/function_kinds/action_run.ts | 2 + bin/lang-js/src/function_kinds/before.ts | 2 + .../src/function_kinds/joi_validation.ts | 33 +++- bin/lang-js/src/function_kinds/management.ts | 2 + .../src/function_kinds/resolver_function.ts | 2 + .../schema_variant_definition.ts | 2 + bin/lang-js/src/sandbox/console.ts | 1 - bin/lang-js/src/vm.ts | 21 --- bin/lang-js/src/worker.js | 57 ++++--- bin/lang-js/tests/exec.spec.ts | 31 ++-- bin/lang-js/tests/executeFunction.spec.ts | 142 ++++++++---------- bin/lang-js/tests/functions/actionRun.ts | 1 + bin/lang-js/tests/functions/beforeFuncs.ts | 24 +-- bin/lang-js/tests/functions/schema-socket.ts | 1 + .../tests/functions/schema-validation.ts | 1 + bin/lang-js/tests/functions/validation.ts | 1 + bin/lang-js/tests/functions/willTimeout.ts | 1 + bin/lang-js/tests/requestStorage.spec.ts | 47 +++--- bin/lang-js/tests/sandbox.spec.ts | 17 ++- bin/lang-js/tests/vm.spec.ts | 31 ---- deno.lock | 42 +++++- prelude-si/deno.bzl | 7 + 25 files changed, 261 insertions(+), 262 deletions(-) delete mode 100644 bin/lang-js/src/vm.ts delete mode 100644 bin/lang-js/tests/vm.spec.ts diff --git a/bin/lang-js/BUCK b/bin/lang-js/BUCK index a21f486c0d..52af991a42 100644 --- a/bin/lang-js/BUCK +++ b/bin/lang-js/BUCK @@ -37,6 +37,10 @@ deno_compile( name = "bin", main = "src/index.ts", out = "lang-js", + srcs = glob([ + "src/**/*.ts", + "src/**/*.js", + ]), permissions = [ "allow-all", ], @@ -59,7 +63,7 @@ deno_format( ) deno_test( - name = "test", + name = "test-unit", srcs = glob(["**/*_test.ts", "**/*.test.ts"]), ignore = ["node_modules"], ) diff --git a/bin/lang-js/src/debug.ts b/bin/lang-js/src/debug.ts index 83d7c89322..988bf03d73 100644 --- a/bin/lang-js/src/debug.ts +++ b/bin/lang-js/src/debug.ts @@ -1,13 +1,13 @@ import process from "node:process"; -export type Debugger = (msg: any) => void; +export type Debugger = (msg: unknown) => void; export function Debug(namespace: string): Debugger { const debugActive = process.env.SI_LANG_JS_LOG || process.env.SI_LOG; - return (msg: any) => { + return (msg: unknown) => { if (debugActive) { try { - const safeStringify = (obj: any): string => { + const safeStringify = (obj: unknown): string => { const seen = new WeakSet(); return JSON.stringify(obj, (_, value) => { // Handle functions @@ -41,7 +41,6 @@ export function Debug(namespace: string): Debugger { process.stderr.write(`${namespace} ${line}\n`); } } catch { - // Fallback if stringify fails completely process.stderr.write( `${namespace} [Debug Error: Unable to stringify message]\n`, ); diff --git a/bin/lang-js/src/function.ts b/bin/lang-js/src/function.ts index cca82d183a..40d83a1065 100644 --- a/bin/lang-js/src/function.ts +++ b/bin/lang-js/src/function.ts @@ -1,5 +1,4 @@ import * as _ from "lodash-es"; -import process from "node:process"; import { base64Decode } from "./base64.ts"; import { ctxFromRequest, Request, RequestCtx } from "./request.ts"; import joi_validation, { @@ -15,10 +14,10 @@ import schema_variant_definition, { import management_run, { ManagementFunc } from "./function_kinds/management.ts"; import action_run, { ActionRunFunc } from "./function_kinds/action_run.ts"; import before from "./function_kinds/before.ts"; -import { rawStorage, rawStorageRequest } from "./sandbox/requestStorage.ts"; -import { Debug, Debugger } from "./debug.ts"; +import { rawStorage } from "./sandbox/requestStorage.ts"; +import { Debugger } from "./debug.ts"; import { transpile } from "jsr:@deno/emit"; -import * as w from "./worker.js"; +import * as _worker from "./worker.js"; export enum FunctionKind { ActionRun = "actionRun", @@ -36,7 +35,6 @@ export function functionKinds(): Array { export type Parameters = Record; export interface Func { - handler: string; codeBase64: string; } @@ -102,13 +100,6 @@ export async function executeFunction( for (const beforeFunction of request.before || []) { await executor(ctx, beforeFunction, timeout, before); - // Set process environment variables, set from requestStorage - { - const requestStorageEnv = rawStorageRequest().env(); - for (const key in requestStorageEnv) { - process.env[key] = requestStorageEnv[key]; - } - } } // TODO Create Func types instead of casting request objs @@ -184,20 +175,6 @@ export async function executeFunction( console.log(JSON.stringify(result)); } -class TimeoutError extends Error { - constructor(seconds: number) { - super(`function timed out after ${seconds} seconds`); - this.name = "TimeoutError"; - } -} - -function timer(seconds: number): Promise { - const ms = seconds * 1000; - return new Promise((_, reject) => { - setTimeout(() => reject(new TimeoutError(seconds)), ms); - }); -} - export async function executor( ctx: RequestCtx, func: F, @@ -213,6 +190,7 @@ export async function executor( ctx: RequestCtx, func: F, code: string, + timeout: number, ) => Promise; }, ) { @@ -224,10 +202,7 @@ export async function executor( const code = wrapCode(originalCode); // Following section throws on timeout or execution error - const result = await Promise.race([ - execute(ctx, func, code), - timer(timeout), - ]); + const result = await execute(ctx, func, code, timeout); debug({ result }); return result; } @@ -236,9 +211,9 @@ export async function runCode( code: string, func_kind: FunctionKind, execution_id: string, + timeout: number, with_arg?: Record, ): Promise> { - const debug = Debug("runCode"); code = await bundleCode(code); const currentEnv = rawStorage().env; @@ -263,13 +238,14 @@ export async function runCode( func_kind, execution_id, with_arg, - env: currentEnv ?? {} + env: currentEnv ?? {}, + timeout, }); worker.onmessage = (event) => { const { result, env } = event.data; if (env) { - Object.assign(rawStorage().env, env); + rawStorage().env = { ...env }; } resolve(result); worker.terminate(); diff --git a/bin/lang-js/src/function_kinds/action_run.ts b/bin/lang-js/src/function_kinds/action_run.ts index 1293257ef5..c5c4b5fa6b 100644 --- a/bin/lang-js/src/function_kinds/action_run.ts +++ b/bin/lang-js/src/function_kinds/action_run.ts @@ -31,12 +31,14 @@ async function execute( { executionId }: RequestCtx, { args }: ActionRunFunc, code: string, + timeout: number, ): Promise { try { const actionRunResult = await runCode( code, FunctionKind.ActionRun, executionId, + timeout, args as Record, ); diff --git a/bin/lang-js/src/function_kinds/before.ts b/bin/lang-js/src/function_kinds/before.ts index c455f794ff..2b79a7aaf2 100644 --- a/bin/lang-js/src/function_kinds/before.ts +++ b/bin/lang-js/src/function_kinds/before.ts @@ -26,12 +26,14 @@ async function execute( { executionId }: RequestCtx, { arg }: BeforeFunc, code: string, + timeout: number, ): Promise { try { await runCode( code, FunctionKind.Before, executionId, + timeout, arg as Record, ); } catch (err) { diff --git a/bin/lang-js/src/function_kinds/joi_validation.ts b/bin/lang-js/src/function_kinds/joi_validation.ts index 6fffa62d06..06649e97fe 100644 --- a/bin/lang-js/src/function_kinds/joi_validation.ts +++ b/bin/lang-js/src/function_kinds/joi_validation.ts @@ -33,6 +33,7 @@ async function execute( { executionId }: RequestCtx, args: JoiValidationFunc, code: string, + timeout: number, ): Promise { try { // NOTE(victor): Joi treats null as a value, so even if .required() @@ -46,8 +47,26 @@ async function execute( code, FunctionKind.Validation, executionId, + timeout, parsedArgs, ); + + if ( + result.err && typeof result.err === "object" && "name" in result.err && + "message" in result.err + ) { + return { + protocol: "result", + status: "failure", + executionId, + error: { + kind: { + UserCodeException: result.err.name as string, + }, + message: result.err.message as string, + }, + }; + } debug({ result }); return { protocol: "result", @@ -63,25 +82,25 @@ async function execute( const wrapCode = (_: string) => ` async function run({ value, validationFormat }) { let definition; - let message; try { definition = JSON.parse(validationFormat); } catch (e) { - e.name = "JoiValidationJsonParsingError"; - message = e; + const error = new Error('Invalid JSON format'); + error.name = 'JoiValidationJsonParsingError'; + throw error; } let schema; try { schema = Joi.build(definition); } catch (e) { - e.name = "JoiValidationFormatError"; - e.message = e.message.replace("\\"value\\"", "validationFormat"); - message = e; + const error = new Error('validationFormat must be of type object'); + error.name = 'JoiValidationFormatError'; + throw error; } const { error } = schema.validate(value); - return { "err": error }; + return { err: error ? error.message : undefined }; }`; export default { diff --git a/bin/lang-js/src/function_kinds/management.ts b/bin/lang-js/src/function_kinds/management.ts index eb18ee3857..13cc97e9a1 100644 --- a/bin/lang-js/src/function_kinds/management.ts +++ b/bin/lang-js/src/function_kinds/management.ts @@ -73,6 +73,7 @@ async function execute( { executionId }: RequestCtx, { thisComponent, components, currentView }: ManagementFunc, code: string, + timeout: number, ): Promise { let managementResult: Record | undefined | null; try { @@ -80,6 +81,7 @@ async function execute( code, FunctionKind.Management, executionId, + timeout, { thisComponent, components, currentView }, ); } catch (err) { diff --git a/bin/lang-js/src/function_kinds/resolver_function.ts b/bin/lang-js/src/function_kinds/resolver_function.ts index e273d7144f..e723e1635d 100644 --- a/bin/lang-js/src/function_kinds/resolver_function.ts +++ b/bin/lang-js/src/function_kinds/resolver_function.ts @@ -193,6 +193,7 @@ async function execute( { executionId }: RequestCtx, { component, responseType }: ResolverFunc, code: string, + timeout: number, ): Promise { let resolverFunctionResult: Record; try { @@ -200,6 +201,7 @@ async function execute( code, FunctionKind.ResolverFunction, executionId, + timeout, component.data.properties, ); } catch (err) { diff --git a/bin/lang-js/src/function_kinds/schema_variant_definition.ts b/bin/lang-js/src/function_kinds/schema_variant_definition.ts index 5bb9fa0a78..44956e2db3 100644 --- a/bin/lang-js/src/function_kinds/schema_variant_definition.ts +++ b/bin/lang-js/src/function_kinds/schema_variant_definition.ts @@ -27,6 +27,7 @@ async function execute( { executionId }: RequestCtx, _: SchemaVariantDefinitionFunc, code: string, + timeout: number, ): Promise { let result: Record; try { @@ -34,6 +35,7 @@ async function execute( code, FunctionKind.SchemaVariantDefinition, executionId, + timeout, {}, ); debug({ result: JSON.stringify(result) }); diff --git a/bin/lang-js/src/sandbox/console.ts b/bin/lang-js/src/sandbox/console.ts index 2edf8549ca..c4f0f8ebba 100644 --- a/bin/lang-js/src/sandbox/console.ts +++ b/bin/lang-js/src/sandbox/console.ts @@ -1,5 +1,4 @@ import { OutputLine } from "../function.ts"; -import { Debug } from "../debug.ts"; const normalizeMessage = (msg: unknown[]): string => { return msg diff --git a/bin/lang-js/src/vm.ts b/bin/lang-js/src/vm.ts deleted file mode 100644 index 07bf9755d7..0000000000 --- a/bin/lang-js/src/vm.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { VM, VMScript } from "vm2"; - -import { Sandbox } from "./sandbox.ts"; - -export function createVm(sandbox: Sandbox): VM { - const timeout = 10000; - const fixAsync = true; - return new VM({ - timeout, - sandbox, - eval: false, - wasm: false, - fixAsync, - }); -} - -export function compileCode(source: string): VMScript { - const code = new VMScript(source); - code.compile(); - return code; -} diff --git a/bin/lang-js/src/worker.js b/bin/lang-js/src/worker.js index 4b54b7721e..9d1cc45a81 100644 --- a/bin/lang-js/src/worker.js +++ b/bin/lang-js/src/worker.js @@ -1,41 +1,62 @@ import { createSandbox } from "./sandbox.ts"; import { rawStorage } from "./sandbox/requestStorage.ts"; +class TimeoutError extends Error { + constructor(seconds) { + super(`function timed out after ${seconds} seconds`); + this.name = "TimeoutError"; + } +} + self.onmessage = async (event) => { - const { bundledCode, func_kind, execution_id, with_arg, env } = event.data || {}; + const { bundledCode, func_kind, execution_id, with_arg, env, timeout } = + event.data || + {}; const sandbox = createSandbox(func_kind, execution_id); const keys = Object.keys(sandbox); const values = Object.values(sandbox); - try { - if (env) { - Object.assign(rawStorage().env, env); - for (const [key, value] of Object.entries(rawStorage().env || {})) { - Deno.env.set(key, value); - } + if (env) { + Object.assign(rawStorage().env, env); + for (const [key, value] of Object.entries(rawStorage().env || {})) { + Deno.env.set(key, value); } + } - const func = new Function( - ...keys, - "with_arg", - ` + const func = new Function( + ...keys, + "with_arg", + ` return (async () => { ${bundledCode} - return await run(with_arg); + try { + return await run(with_arg); + } catch (e) { + return { + err: { + name: e.name, + message: e.message + } + }; + } })() `, - ); + ); + const timeoutId = setTimeout(() => { + throw new TimeoutError(timeout); + }, timeout * 1000); + + try { const result = await func(...values, with_arg); + clearTimeout(timeoutId); self.postMessage({ result, env: rawStorage().env, }); - } catch (err) { - self.postMessage({ - error: err.message, - stack: err.stack, - }); + } catch (e) { + clearTimeout(timeoutId); + throw e; } }; diff --git a/bin/lang-js/tests/exec.spec.ts b/bin/lang-js/tests/exec.spec.ts index 6b73079eb8..8ee1b42a3d 100644 --- a/bin/lang-js/tests/exec.spec.ts +++ b/bin/lang-js/tests/exec.spec.ts @@ -1,15 +1,16 @@ -import { describe, expect, test } from "vitest"; -import { makeExec } from "../src/sandbox/exec"; +import { assertEquals } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { makeExec } from "../src/sandbox/exec.ts"; -describe("exec", () => { - test("exec a command", async () => { +Deno.test("exec", async (t) => { + await t.step("exec a command", async () => { const e = makeExec("p"); const r = await e.waitUntilEnd("echo", ["poop"]); - expect(r.all).toBe("poop"); + console.log(r); + assertEquals(r.all, "poop"); }); - describe("watch a command", () => { - test("until it succeeds", async () => { + await t.step("watch a command", async (t) => { + await t.step("until it succeeds", async () => { const e = makeExec("p"); const getCurrentTime = await e.waitUntilEnd("date", ["+%s"]); const startSeconds = getCurrentTime.all || "3"; @@ -23,10 +24,10 @@ describe("exec", () => { return elapsed > waitUntil; }, }); - expect(r.result.exitCode).toBe(0); + assertEquals(r.result.exitCode, 0); }); - test("fail immediately if the command fails", async () => { + await t.step("fail immediately if the command fails", async () => { const e = makeExec("p"); let didIt = "nope"; const r = await e.watch({ @@ -39,12 +40,12 @@ describe("exec", () => { return true; }, }); - expect(r.result.failed).toBe(true); - expect(r.failed).toBe("commandFailed"); - expect(didIt).toBe("nope"); + assertEquals(r.result.failed, true); + assertEquals(r.failed, "commandFailed"); + assertEquals(didIt, "nope"); }); - test("fail if the deadline is exceeded", async () => { + await t.step("fail if the deadline is exceeded", async () => { const e = makeExec("p"); const getCurrentTime = await e.waitUntilEnd("date", ["+%s"]); const startSeconds = getCurrentTime.all || "3"; @@ -60,8 +61,8 @@ describe("exec", () => { }, }); - expect(r.result.exitCode).toBe(0); - expect(r.failed).toBe("deadlineExceeded"); + assertEquals(r.result.exitCode, 0); + assertEquals(r.failed, "deadlineExceeded"); }); }); }); diff --git a/bin/lang-js/tests/executeFunction.spec.ts b/bin/lang-js/tests/executeFunction.spec.ts index cce8a83583..a4a4575c3d 100644 --- a/bin/lang-js/tests/executeFunction.spec.ts +++ b/bin/lang-js/tests/executeFunction.spec.ts @@ -1,14 +1,18 @@ -import * as fs from "fs/promises"; -import Joi from "joi"; -import { describe, expect, fail, test, vi } from "vitest"; -import { executeFunction, FunctionKind } from "../src/function"; -import { AnyFunction, RequestCtx } from "../src/request"; +import { + assertObjectMatch, + assertRejects, +} from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { join } from "https://deno.land/std@0.224.0/path/mod.ts"; +import { describe, it } from "https://deno.land/std@0.224.0/testing/bdd.ts"; +import Joi from "npm:joi"; +import { executeFunction, FunctionKind } from "../src/function.ts"; +import { AnyFunction, RequestCtx } from "../src/request.ts"; let lastLog = ""; -const consoleSpy = vi.spyOn(console, "log").mockImplementation((msg) => { +console.log = (msg: string) => { console.dir(msg); lastLog = msg; -}); +}; const FUNCS_FOLDER = "./tests/functions/"; @@ -20,11 +24,10 @@ interface FuncScenario { funcSpec: AnyFunction; func?: FuncOrFuncLocation; before?: { - handler: string; - func: FuncOrFuncLocation; - arg: Record; + arg: Record; + codeBase64: string; }[]; - result?: any; + result?: unknown; timeout?: number; } @@ -34,8 +37,7 @@ const scenarios: FuncScenario[] = [ kind: FunctionKind.SchemaVariantDefinition, funcSpec: { value: {}, - handler: "main", - codeBase64: "", // We rewrite this later + codeBase64: "", }, func: "schema-socket.ts", }, @@ -44,7 +46,6 @@ const scenarios: FuncScenario[] = [ kind: FunctionKind.SchemaVariantDefinition, funcSpec: { value: {}, - handler: "main", codeBase64: "", // We rewrite this later }, func: "schema-validation.ts", @@ -54,7 +55,6 @@ const scenarios: FuncScenario[] = [ kind: FunctionKind.ActionRun, funcSpec: { value: {}, - handler: "workit", codeBase64: "", // We rewrite this later }, func: "actionRun.ts", @@ -64,20 +64,31 @@ const scenarios: FuncScenario[] = [ kind: FunctionKind.ActionRun, funcSpec: { value: {}, - handler: "main", codeBase64: "", // We rewrite this later }, func: "beforeFuncs.ts", before: [ { - handler: "before1", - func: "beforeFuncs.ts", arg: { username: "name" }, + codeBase64: btoa(` + function main(arg) { + console.log("Running Before 1"); + console.log(\`My arg is \${JSON.stringify(arg)}\`); + requestStorage.setEnv("b1", true); + requestStorage.setEnv("b2", true); + } + `), }, { - handler: "before2", - func: "beforeFuncs.ts", arg: {}, + codeBase64: btoa(` + function main(arg) { + console.log("Running Before 2"); + console.log(\`My arg is \${JSON.stringify(arg)}\`); + requestStorage.deleteEnv("b2"); + requestStorage.setEnv("b3", "I'm a string"); + } + `), }, ], }, @@ -87,7 +98,6 @@ const scenarios: FuncScenario[] = [ funcSpec: { value: 1, validationFormat: JSON.stringify(Joi.number().describe()), - handler: "", codeBase64: "", }, }, @@ -97,7 +107,6 @@ const scenarios: FuncScenario[] = [ funcSpec: { value: "foobar", validationFormat: JSON.stringify(Joi.number().describe()), - handler: "", codeBase64: "", }, result: { @@ -112,7 +121,6 @@ const scenarios: FuncScenario[] = [ funcSpec: { value: "foobar", validationFormat: JSON.stringify(Joi.string().describe()), - handler: "", codeBase64: "", }, }, @@ -122,7 +130,6 @@ const scenarios: FuncScenario[] = [ funcSpec: { value: 1, validationFormat: JSON.stringify(Joi.string().describe()), - handler: "", codeBase64: "", }, result: { @@ -137,7 +144,6 @@ const scenarios: FuncScenario[] = [ funcSpec: { value: 1, validationFormat: "''", - handler: "", codeBase64: "", }, result: { @@ -156,7 +162,6 @@ const scenarios: FuncScenario[] = [ funcSpec: { value: 1, validationFormat: JSON.stringify("test"), - handler: "", codeBase64: "", }, result: { @@ -175,7 +180,6 @@ const scenarios: FuncScenario[] = [ kind: FunctionKind.ActionRun, funcSpec: { value: {}, - handler: "main", codeBase64: "", // We rewrite this later }, func: "willTimeout.ts", @@ -186,36 +190,28 @@ const scenarios: FuncScenario[] = [ // This is the test suite timeout in seconds. const testSuiteTimeout = 30; +async function base64FromFile(path: string) { + const buffer = await Deno.readFile(join(Deno.cwd(), path)); + return btoa(new TextDecoder().decode(buffer)); +} + describe("executeFunction", () => { - test("Name", () => { - const format = Joi.number().integer().min(0).max(2) - .required(); + it("Name", () => { + const format = Joi.number().integer().min(0).max(2).required(); const string = JSON.stringify(format.describe()); console.log(string); }); - test.each(scenarios)( - "$name", - async (scenario) => { - consoleSpy.mockClear(); + for (const scenario of scenarios) { + it(scenario.name, async () => { lastLog = ""; let codeBase64: string; if (scenario.func) { if (typeof scenario.func === "function") { - // If we get a function from the scenario object we need to get its - // string representation and make it a valid function definition - // function.toString() is a wild thing :) const rawCode = scenario.func.toString(); - - let code: string; - if (rawCode.startsWith("func()")) { - code = `function ${rawCode}`; - } else { - code = `const ${scenario.funcSpec.handler} = ${rawCode}`; - } - - codeBase64 = Buffer.from(code).toString("base64"); + const code = `function ${rawCode}`; + codeBase64 = btoa(code); } else { codeBase64 = await base64FromFile(FUNCS_FOLDER + scenario.func); } @@ -232,48 +228,34 @@ describe("executeFunction", () => { codeBase64, }; - const before = []; - - for (const { func, handler, arg } of scenario.before ?? []) { - before.push({ - handler, - codeBase64: await base64FromFile(FUNCS_FOLDER + func), - arg, - }); - } - if (scenario.timeout) { - try { - await executeFunction(scenario.kind, { - ...ctx, - ...funcObj, - before, - }, scenario.timeout); - fail("expected function to hit timeout, but no error was thrown"); - } catch (error) { - expect(error.message).toBe( - `function timed out after ${scenario.timeout} seconds`, - ); - } + assertRejects( + async () => { + await executeFunction(scenario.kind, { + ...funcObj, + ...ctx, + before: scenario.before, + }, scenario.timeout!); + }, + Error, + `function timed out after ${scenario.timeout} seconds`, + ); } else { await executeFunction(scenario.kind, { ...ctx, ...funcObj, - before, + before: scenario.before, }, testSuiteTimeout * 1000); + const parsedLog = JSON.parse(lastLog); - expect(parsedLog).toMatchObject( - scenario.result ?? { + assertObjectMatch( + parsedLog, + (scenario.result ?? { protocol: "result", status: "success", - }, + }) as Record, ); } - }, - ); -}, testSuiteTimeout * 1000); - -async function base64FromFile(path: string) { - const buffer = await fs.readFile(path); - return buffer.toString("base64"); -} + }); + } +}); diff --git a/bin/lang-js/tests/functions/actionRun.ts b/bin/lang-js/tests/functions/actionRun.ts index c386479b87..06f206bcc9 100644 --- a/bin/lang-js/tests/functions/actionRun.ts +++ b/bin/lang-js/tests/functions/actionRun.ts @@ -1,3 +1,4 @@ +// deno-lint-ignore-file async function main() { console.log("first"); return { status: "ok" }; diff --git a/bin/lang-js/tests/functions/beforeFuncs.ts b/bin/lang-js/tests/functions/beforeFuncs.ts index 704da96eb2..63d8cfd9c9 100644 --- a/bin/lang-js/tests/functions/beforeFuncs.ts +++ b/bin/lang-js/tests/functions/beforeFuncs.ts @@ -1,12 +1,12 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// deno-lint-ignore-file // @ts-nocheck function main() { console.log("Running Main"); - const b1 = requestStorage.getItem("b1"); - const b2 = requestStorage.getItem("b2"); - const b3 = requestStorage.getItem("b3"); + const b1 = requestStorage.getEnv("b1"); + const b2 = requestStorage.getEnv("b2"); + const b3 = requestStorage.getEnv("b3"); - const keys = requestStorage.getKeys(); + const keys = requestStorage.getEnvKeys(); console.log( `Before function 1 set b1="${b1}", Before function 2 said "${b3}", keys are ${keys}`, @@ -16,17 +16,3 @@ function main() { status: b1 && b2 === undefined && typeof b3 === "string" ? "ok" : "error", }; } - -function before1(arg) { - console.log("Running Before 1"); - console.log(`My arg is ${arg}`); - requestStorage.setItem("b1", true); - requestStorage.setItem("b2", true); -} - -function before2(arg) { - console.log("Running Before 2"); - console.log(`My arg is ${arg}`); - requestStorage.deleteItem("b2"); - requestStorage.setItem("b3", "I'm a string"); -} diff --git a/bin/lang-js/tests/functions/schema-socket.ts b/bin/lang-js/tests/functions/schema-socket.ts index 71b02c3b32..c092f6bceb 100644 --- a/bin/lang-js/tests/functions/schema-socket.ts +++ b/bin/lang-js/tests/functions/schema-socket.ts @@ -1,3 +1,4 @@ +// deno-lint-ignore-file // @ts-nocheck function main() { diff --git a/bin/lang-js/tests/functions/schema-validation.ts b/bin/lang-js/tests/functions/schema-validation.ts index b6fbec3d1d..23aedd8c7c 100644 --- a/bin/lang-js/tests/functions/schema-validation.ts +++ b/bin/lang-js/tests/functions/schema-validation.ts @@ -1,3 +1,4 @@ +// deno-lint-ignore-file // @ts-nocheck function main() { diff --git a/bin/lang-js/tests/functions/validation.ts b/bin/lang-js/tests/functions/validation.ts index b4581aa3f0..023d927ba8 100644 --- a/bin/lang-js/tests/functions/validation.ts +++ b/bin/lang-js/tests/functions/validation.ts @@ -1,3 +1,4 @@ +// deno-lint-ignore-file function main() { return { valid: true, diff --git a/bin/lang-js/tests/functions/willTimeout.ts b/bin/lang-js/tests/functions/willTimeout.ts index a44b3bde0c..5a32e7d274 100644 --- a/bin/lang-js/tests/functions/willTimeout.ts +++ b/bin/lang-js/tests/functions/willTimeout.ts @@ -1,3 +1,4 @@ +// deno-lint-ignore-file // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck async function main() { diff --git a/bin/lang-js/tests/requestStorage.spec.ts b/bin/lang-js/tests/requestStorage.spec.ts index 1515e16eea..827d9dabde 100644 --- a/bin/lang-js/tests/requestStorage.spec.ts +++ b/bin/lang-js/tests/requestStorage.spec.ts @@ -1,32 +1,35 @@ -import { beforeAll, describe, expect, test } from "vitest"; -import { makeBeforeRequestStorage } from "../src/sandbox/requestStorage"; - -describe("requestStorage", () => { - const KEY = "a"; - const VALUE = "this is a test value"; - const EXEC_ID = "execId"; - - beforeAll(() => { - makeBeforeRequestStorage(EXEC_ID).setItem(KEY, VALUE); - }); - - test("Retrieve a value", () => { +import { + assertEquals, + assertExists, +} from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { makeBeforeRequestStorage } from "../src/sandbox/requestStorage.ts"; + +const KEY = "a"; +const VALUE = "this is a test value"; +const EXEC_ID = "execId"; + +function setupStorage() { + makeBeforeRequestStorage(EXEC_ID).setItem(KEY, VALUE); +} + +Deno.test("requestStorage", async (t) => { + await t.step("Retrieve a value", () => { + setupStorage(); const value = makeBeforeRequestStorage(EXEC_ID).getItem(KEY); - - expect(value).toBe(VALUE); + assertEquals(value, VALUE); }); - test("Retrieve keys", () => { + await t.step("Retrieve keys", () => { + setupStorage(); const keys = makeBeforeRequestStorage(EXEC_ID).getKeys(); - - expect(keys).toHaveLength(1); - expect(keys).toContain(KEY); + assertEquals(keys.length, 1); + assertEquals(keys.includes(KEY), true); }); - test("Delete a value by key", () => { + await t.step("Delete a value by key", () => { + setupStorage(); makeBeforeRequestStorage(EXEC_ID).deleteItem(KEY); - const value = makeBeforeRequestStorage(EXEC_ID).getItem(KEY); - expect(value).toBeUndefined(); + assertExists(!value); }); }); diff --git a/bin/lang-js/tests/sandbox.spec.ts b/bin/lang-js/tests/sandbox.spec.ts index 3f7cdf4861..6a86248367 100644 --- a/bin/lang-js/tests/sandbox.spec.ts +++ b/bin/lang-js/tests/sandbox.spec.ts @@ -1,11 +1,12 @@ -import { describe, expect, test } from "vitest"; -import { FunctionKind } from "../src/function"; -import { createSandbox } from "../src/sandbox"; +import { assertObjectMatch } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { FunctionKind } from "../src/function.ts"; +import { createSandbox } from "../src/sandbox.ts"; -describe("createSandbox", () => { - test("creates a new sandbox environment for execution", () => { - const sandbox = createSandbox(FunctionKind.ResolverFunction, "poop"); - expect(sandbox).toHaveProperty("console"); - expect(sandbox).toHaveProperty("_"); +Deno.test("createSandbox", () => { + const sandbox = createSandbox(FunctionKind.ResolverFunction, "poop"); + + assertObjectMatch(sandbox, { + console: sandbox.console, + _: sandbox._, }); }); diff --git a/bin/lang-js/tests/vm.spec.ts b/bin/lang-js/tests/vm.spec.ts deleted file mode 100644 index 227e400787..0000000000 --- a/bin/lang-js/tests/vm.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { VM, VMScript } from "vm2"; -import { describe, expect, test } from "vitest"; -import { compileCode, createVm } from "../src/vm"; -import { createSandbox } from "../src/sandbox"; - -import { FunctionKind } from "../src/function"; - -describe("createVm", () => { - test("creates a new vm for execution", () => { - const sandbox = createSandbox(FunctionKind.ResolverFunction, "poop"); - const vm = createVm(sandbox); - expect(vm).toBeInstanceOf(VM); - }); -}); - -describe("compileCode", () => { - test("compiles code for execution", () => { - const code = compileCode("'foo'"); - expect(code).toBeInstanceOf(VMScript); - }); -}); - -describe("runCode", () => { - test("runs code on a vm, returning the result", () => { - const sandbox = createSandbox(FunctionKind.ResolverFunction, "poop"); - const vm = createVm(sandbox); - const code = compileCode("'foo'"); - const result = vm.run(code); - expect(result).toBe("foo"); - }); -}); diff --git a/deno.lock b/deno.lock index 9291f03843..dc530075f1 100644 --- a/deno.lock +++ b/deno.lock @@ -2,6 +2,7 @@ "version": "4", "specifiers": { "jsr:@deno/cache-dir@0.13.2": "0.13.2", + "jsr:@deno/emit@*": "0.46.0", "jsr:@deno/emit@0.46": "0.46.0", "jsr:@deno/graph@~0.73.1": "0.73.1", "jsr:@std/assert@0.223": "0.223.0", @@ -15,11 +16,13 @@ "npm:@types/js-yaml@^4.0.5": "4.0.9", "npm:@types/lodash-es@^4.17.12": "4.17.12", "npm:@types/node-fetch@^2.6.1": "2.6.12", + "npm:@types/node@*": "22.5.4", "npm:@types/node@^18.19.59": "18.19.67", "npm:@typescript/vfs@^1.4.0": "1.6.0_typescript@4.9.5", "npm:commander@^9.2.0": "9.5.0", "npm:eslint@^8.57.1": "8.57.1", "npm:execa@^5.1.1": "5.1.1", + "npm:joi@*": "17.13.3", "npm:joi@^17.11.0": "17.13.3", "npm:js-yaml@^4.1.0": "4.1.0", "npm:lodash-es@^4.17.21": "4.17.21", @@ -2049,11 +2052,44 @@ } }, "redirects": { - "https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts" + "https://deno.land/std/assert/mod.ts": "https://deno.land/std@0.224.0/assert/mod.ts", + "https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts", + "https://deno.land/std/testing/bdd.ts": "https://deno.land/std@0.224.0/testing/bdd.ts" }, "remote": { + "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", + "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", + "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", + "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", + "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", + "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", + "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", + "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", + "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", + "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", + "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", + "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", + "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", + "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", + "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", + "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", + "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", + "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", + "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", + "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", + "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", + "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", + "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", + "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", + "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", + "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", + "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", + "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", + "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", + "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", + "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e", "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8", "https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2", "https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c", @@ -2130,7 +2166,9 @@ "https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7", "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972", "https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e", - "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c" + "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", + "https://deno.land/std@0.224.0/testing/_test_suite.ts": "f10a8a6338b60c403f07a76f3f46bdc9f1e1a820c0a1decddeb2949f7a8a0546", + "https://deno.land/std@0.224.0/testing/bdd.ts": "3e4de4ff6d8f348b5574661cef9501b442046a59079e201b849d0e74120d476b" }, "workspace": { "dependencies": [ diff --git a/prelude-si/deno.bzl b/prelude-si/deno.bzl index 4f7d09f8fb..88033c8159 100644 --- a/prelude-si/deno.bzl +++ b/prelude-si/deno.bzl @@ -25,6 +25,8 @@ def deno_compile_impl(ctx: AnalysisContext) -> list[Provider]: out.as_output(), ) + cmd.hidden(ctx.attrs.srcs) + if ctx.attrs.permissions: cmd.add("--permissions") cmd.add(ctx.attrs.permissions) @@ -46,6 +48,11 @@ deno_compile = rule( "main": attrs.source( doc = "The entry point TypeScript/JavaScript file", ), + "srcs": attrs.list( + attrs.source(), + default = [], + doc = "All source files that are part of the compilation", + ), "out": attrs.string( doc = "The name of the output binary", ),