mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
- Deleted package.json and tsconfig.json for IsolatedVM. - Removed isolated-vm upstream configuration from Nginx default.conf.template. - Removed ISOLATED_VM_PORT and telemetry settings from config.example.env. - Cleaned up docker-compose.base.yml by removing isolated-vm service and its environment variables. - Updated docker-compose.dev.yml to eliminate isolated-vm service and its associated volumes. - Removed isolated-vm service from docker-compose.yml.
300 lines
8.8 KiB
TypeScript
300 lines
8.8 KiB
TypeScript
// Mock all heavy dependencies so the test focuses on template logic only
|
|
jest.mock("../../../../Server/Utils/VM/VMRunner", () => {
|
|
return {
|
|
__esModule: true,
|
|
default: {
|
|
runCodeInSandbox: jest.fn(),
|
|
},
|
|
};
|
|
});
|
|
|
|
jest.mock("../../../../Server/Utils/Logger", () => {
|
|
return {
|
|
__esModule: true,
|
|
default: {
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
},
|
|
};
|
|
});
|
|
|
|
jest.mock("../../../../Server/Utils/Telemetry/CaptureSpan", () => {
|
|
return {
|
|
__esModule: true,
|
|
default: () => {
|
|
return (
|
|
_target: any,
|
|
_propertyKey: string,
|
|
descriptor: PropertyDescriptor,
|
|
) => {
|
|
return descriptor;
|
|
};
|
|
},
|
|
};
|
|
});
|
|
|
|
import VMUtil from "../../../../Server/Utils/VM/VMAPI";
|
|
import { describe, expect, it } from "@jest/globals";
|
|
import { JSONObject } from "../../../../Types/JSON";
|
|
|
|
describe("VMUtil", () => {
|
|
describe("deepFind", () => {
|
|
it("should find top-level keys", () => {
|
|
const obj: JSONObject = { status: "firing", receiver: "test" };
|
|
expect(VMUtil.deepFind(obj, "status")).toBe("firing");
|
|
});
|
|
|
|
it("should find nested keys with dot notation", () => {
|
|
const obj: JSONObject = { data: { nested: { value: 42 } } };
|
|
expect(VMUtil.deepFind(obj, "data.nested.value")).toBe(42);
|
|
});
|
|
|
|
it("should find array elements with bracket notation", () => {
|
|
const obj: JSONObject = { items: ["a", "b", "c"] };
|
|
expect(VMUtil.deepFind(obj, "items[0]")).toBe("a");
|
|
expect(VMUtil.deepFind(obj, "items[2]")).toBe("c");
|
|
});
|
|
|
|
it("should find last array element with [last]", () => {
|
|
const obj: JSONObject = { items: [1, 2, 3] };
|
|
expect(VMUtil.deepFind(obj, "items[last]")).toBe(3);
|
|
});
|
|
|
|
it("should return undefined for missing paths", () => {
|
|
const obj: JSONObject = { a: { b: 1 } };
|
|
expect(VMUtil.deepFind(obj, "a.c")).toBeUndefined();
|
|
expect(VMUtil.deepFind(obj, "x.y.z")).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("replaceValueInPlace", () => {
|
|
it("should replace simple variables", () => {
|
|
const storageMap: JSONObject = { name: "test", status: "firing" };
|
|
const result: string = VMUtil.replaceValueInPlace(
|
|
storageMap,
|
|
"Alert: {{name}} is {{status}}",
|
|
false,
|
|
);
|
|
expect(result).toBe("Alert: test is firing");
|
|
});
|
|
|
|
it("should replace nested dotted path variables", () => {
|
|
const storageMap: JSONObject = {
|
|
requestBody: { title: "My Alert", data: { severity: "high" } },
|
|
};
|
|
const result: string = VMUtil.replaceValueInPlace(
|
|
storageMap,
|
|
"Title: {{requestBody.title}}, Severity: {{requestBody.data.severity}}",
|
|
false,
|
|
);
|
|
expect(result).toBe("Title: My Alert, Severity: high");
|
|
});
|
|
|
|
it("should leave unresolved variables as-is", () => {
|
|
const storageMap: JSONObject = { name: "test" };
|
|
const result: string = VMUtil.replaceValueInPlace(
|
|
storageMap,
|
|
"{{name}} {{unknown}}",
|
|
false,
|
|
);
|
|
expect(result).toBe("test {{unknown}}");
|
|
});
|
|
});
|
|
|
|
describe("expandEachLoops", () => {
|
|
it("should expand a simple each loop over an array of objects", () => {
|
|
const storageMap: JSONObject = {
|
|
requestBody: {
|
|
alerts: [
|
|
{ labels: { label: "Coralpay" }, status: "firing" },
|
|
{ labels: { label: "capitecpay" }, status: "resolved" },
|
|
],
|
|
},
|
|
};
|
|
|
|
const template: string =
|
|
"Alerts:{{#each requestBody.alerts}} {{labels.label}}({{status}}){{/each}}";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("Alerts: Coralpay(firing) capitecpay(resolved)");
|
|
});
|
|
|
|
it("should support {{@index}} in loops", () => {
|
|
const storageMap: JSONObject = {
|
|
items: [{ name: "a" }, { name: "b" }, { name: "c" }],
|
|
};
|
|
|
|
const template: string = "{{#each items}}{{@index}}: {{name}} {{/each}}";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("0: a 1: b 2: c ");
|
|
});
|
|
|
|
it("should support {{this}} for primitive arrays", () => {
|
|
const storageMap: JSONObject = {
|
|
tags: ["critical", "production", "api"],
|
|
};
|
|
|
|
const template: string = "Tags:{{#each tags}} {{this}}{{/each}}";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("Tags: critical production api");
|
|
});
|
|
|
|
it("should remove the block if the path is not an array", () => {
|
|
const storageMap: JSONObject = { notAnArray: "hello" };
|
|
const template: string = "Before {{#each notAnArray}}body{{/each}} After";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("Before After");
|
|
});
|
|
|
|
it("should remove the block if the path does not exist", () => {
|
|
const storageMap: JSONObject = {};
|
|
const template: string =
|
|
"Before {{#each missing.path}}body{{/each}} After";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("Before After");
|
|
});
|
|
|
|
it("should handle empty arrays", () => {
|
|
const storageMap: JSONObject = { items: [] };
|
|
const template: string = "Before {{#each items}}item{{/each}} After";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("Before After");
|
|
});
|
|
|
|
it("should support nested each loops", () => {
|
|
const storageMap: JSONObject = {
|
|
groups: [
|
|
{ name: "G1", members: [{ id: 1 }, { id: 2 }] },
|
|
{ name: "G2", members: [{ id: 3 }] },
|
|
],
|
|
};
|
|
|
|
const template: string =
|
|
"{{#each groups}}Group {{name}}: {{#each members}}{{id}} {{/each}}| {{/each}}";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("Group G1: 1 2 | Group G2: 3 | ");
|
|
});
|
|
|
|
it("should allow fallback to parent variables inside loops", () => {
|
|
const storageMap: JSONObject = {
|
|
globalTitle: "My Dashboard",
|
|
items: [{ name: "item1" }, { name: "item2" }],
|
|
};
|
|
|
|
const template: string =
|
|
"{{#each items}}{{name}} in {{globalTitle}} {{/each}}";
|
|
const result: string = VMUtil.expandEachLoops(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe("item1 in My Dashboard item2 in My Dashboard ");
|
|
});
|
|
});
|
|
|
|
describe("replaceValueInPlace with each loops (end-to-end)", () => {
|
|
it("should expand loops and then replace remaining variables", () => {
|
|
const storageMap: JSONObject = {
|
|
requestBody: {
|
|
receiver: "Fundsflow",
|
|
alerts: [
|
|
{
|
|
status: "firing",
|
|
labels: { label: "Coralpay", alertname: "File Drop" },
|
|
},
|
|
{
|
|
status: "firing",
|
|
labels: { label: "capitecpay", alertname: "File Drop" },
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const template: string =
|
|
"Receiver: {{requestBody.receiver}}\n{{#each requestBody.alerts}}- {{labels.label}}: {{status}}\n{{/each}}";
|
|
const result: string = VMUtil.replaceValueInPlace(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe(
|
|
"Receiver: Fundsflow\n- Coralpay: firing\n- capitecpay: firing\n",
|
|
);
|
|
});
|
|
|
|
it("should handle the Grafana alerts use case", () => {
|
|
const storageMap: JSONObject = {
|
|
requestBody: {
|
|
status: "firing",
|
|
alerts: [
|
|
{
|
|
status: "firing",
|
|
labels: {
|
|
alertname: "Fundsflow File Drop Update",
|
|
label: "Coralpay",
|
|
},
|
|
valueString: "A=0, C=1",
|
|
},
|
|
{
|
|
status: "firing",
|
|
labels: {
|
|
alertname: "Fundsflow File Drop Update",
|
|
label: "capitecpay",
|
|
},
|
|
valueString: "A=0, C=1",
|
|
},
|
|
{
|
|
status: "firing",
|
|
labels: {
|
|
alertname: "Fundsflow File Drop Update",
|
|
label: "capricorn",
|
|
},
|
|
valueString: "A=0, C=1",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const template: string =
|
|
"Alert Labels:\n{{#each requestBody.alerts}}- {{labels.label}}\n{{/each}}";
|
|
const result: string = VMUtil.replaceValueInPlace(
|
|
storageMap,
|
|
template,
|
|
false,
|
|
);
|
|
expect(result).toBe(
|
|
"Alert Labels:\n- Coralpay\n- capitecpay\n- capricorn\n",
|
|
);
|
|
});
|
|
});
|
|
});
|