Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  scenario-flow-runner.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import type { QaTransportState } from "./qa-transport.js";
import type { QaScenarioFlow, QaSeedScenarioWithSource } from "./scenario-catalog.js";

type QaSuiteStep = {
  name: string;
  run: () => Promise<string | void>;
};

type QaSuiteScenarioResult = {
  name: string;
  status: "pass" | "fail";
  steps: Array<{
    name: string;
    status: "pass" | "fail" | "skip";
    details?: string;
  }>;
  details?: string;
};

type QaFlowApi = Record<string, unknown> & {
  state: QaTransportState;
  scenario: QaSeedScenarioWithSource;
  config: Record<string, unknown>;
  runScenario: (name: string, steps: QaSuiteStep[]) => Promise<QaSuiteScenarioResult>;
};

type QaFlowVars = Record<string, unknown>;

const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor as new (
  ...args: string[]
) => (...fnArgs: unknown[]) => Promise<unknown>;

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function formatFlowDetails(details: unknown) {
  if (details === undefined) {
    return undefined;
  }
  if (typeof details === "string") {
    return details;
  }
  if (typeof details === "number" || typeof details === "boolean" || typeof details === "bigint") {
    return String(details);
  }
  return JSON.stringify(details, null, 2);
}

function getPathWithParent(
  root: Record<string, unknown>,
  ref: string,
): { parent: Record<string, unknown> | null; value: unknown } {
  const parts = ref.split(".").filter(Boolean);
  let current: unknown = root;
  let parent: Record<string, unknown> | null = null;
  for (const part of parts) {
    if (!isPlainObject(current)) {
      return { parent: null, value: undefined };
    }
    parent = current;
    current = current[part];
  }
  return { parent, value: current };
}

function createEvalContext(api: QaFlowApi, vars: QaFlowVars) {
  return {
    ...api,
    qaImport: (specifier: string) => import(specifier),
    vars,
    ...vars,
  };
}

async function evalExpr(expr: string, api: QaFlowApi, vars: QaFlowVars) {
  const context = createEvalContext(api, vars);
  const names = Object.keys(context);
  const values = Object.values(context);
  const fn = new AsyncFunction(...names, `return (${expr});`);
  return await fn(...values);
}

function buildLambda(
  spec: { params?: string[]; expr: string; async?: boolean },
  api: QaFlowApi,
  vars: QaFlowVars,
) {
  const context = createEvalContext(api, vars);
  const names = Object.keys(context);
  const values = Object.values(context);
  const params = spec.params ?? [];
  const Factory = spec.async ? AsyncFunction : Function;
  const fn = new Factory(...names, ...params, `return (${spec.expr});`) as (
    ...fnArgs: unknown[]
  ) => unknown;
  return (...lambdaArgs: unknown[]) => fn(...values, ...lambdaArgs);
}

async function resolveValue(node: unknown, api: QaFlowApi, vars: QaFlowVars): Promise<unknown> {
  if (Array.isArray(node)) {
    return await Promise.all(node.map((entry) => resolveValue(entry, api, vars)));
  }
  if (!isPlainObject(node)) {
    return node;
  }
  const keys = Object.keys(node);
  if (keys.length === 1 && typeof node.ref === "string") {
    return getPathWithParent(createEvalContext(api, vars), node.ref).value;
  }
  if (keys.length === 1 && typeof node.expr === "string") {
    return await evalExpr(node.expr, api, vars);
  }
  if (keys.length === 1 && isPlainObject(node.lambda) && typeof node.lambda.expr === "string") {
    return buildLambda(
      {
        expr: node.lambda.expr,
        params: Array.isArray(node.lambda.params)
          ? node.lambda.params.filter((entry): entry is string => typeof entry === "string")
          : [],
        async: node.lambda.async === true,
      },
      api,
      vars,
    );
  }
  const entries = await Promise.all(
    Object.entries(node).map(async ([key, value]) => [key, await resolveValue(value, api, vars)]),
  );
  return Object.fromEntries(entries);
}

function resolveCallable(path: string, api: QaFlowApi, vars: QaFlowVars) {
  const { parent, value } = getPathWithParent(createEvalContext(api, vars), path);
  if (typeof value !== "function") {
    throw new Error(`qa flow callable not found: ${path}`);
  }
  return parent ? value.bind(parent) : value;
}

async function runFlowAction(action: unknown, api: QaFlowApi, vars: QaFlowVars) {
  if (!isPlainObject(action)) {
    throw new Error(`invalid qa flow action: ${JSON.stringify(action)}`);
  }
  if (typeof action.call === "string") {
    const callable = resolveCallable(action.call, api, vars);
    const args = Array.isArray(action.args)
      ? await Promise.all(action.args.map((entry) => resolveValue(entry, api, vars)))
      : [];
    const result = await callable(...args);
    if (typeof action.saveAs === "string" && action.saveAs.trim()) {
      vars[action.saveAs.trim()] = result;
    }
    return;
  }
  if (typeof action.set === "string") {
    vars[action.set] = await resolveValue(action.value, api, vars);
    return;
  }
  if (typeof action.assert === "string" || isPlainObject(action.assert)) {
    const spec =
      typeof action.assert === "string"
        ? { expr: action.assert, message: undefined }
        : {
            expr: typeof action.assert.expr === "string" ? action.assert.expr : "",
            message: action.assert.message,
          };
    if (!spec.expr) {
      throw new Error(`invalid qa flow assertion: ${JSON.stringify(action.assert)}`);
    }
    const passed = Boolean(await evalExpr(spec.expr, api, vars));
    if (!passed) {
      const message =
        spec.message === undefined ? undefined : await resolveValue(spec.message, api, vars);
      throw new Error(
        typeof message === "string" && message.trim()
          ? message
          : `qa flow assertion failed: ${spec.expr}`,
      );
    }
    return;
  }
  if (typeof action.throw === "string" || isPlainObject(action.throw)) {
    const spec =
      typeof action.throw === "string"
        ? { expr: undefined, message: action.throw }
        : {
            expr: typeof action.throw.expr === "string" ? action.throw.expr : undefined,
            message: action.throw.message,
          };
    const evaluated = spec.expr ? await evalExpr(spec.expr, api, vars) : undefined;
    const message =
      spec.message === undefined ? undefined : await resolveValue(spec.message, api, vars);
    if (evaluated instanceof Error) {
      throw evaluated;
    }
    if (typeof evaluated === "string" && evaluated.trim()) {
      throw new Error(evaluated);
    }
    if (typeof message === "string" && message.trim()) {
      throw new Error(message);
    }
    throw new Error("qa flow throw");
  }
  if (isPlainObject(action.if)) {
    const ifAction = action.if as { expr: string; then: unknown[]; else?: unknown[] };
    const passed = Boolean(await evalExpr(ifAction.expr, api, vars));
    const branch = passed ? ifAction.then : (ifAction.else ?? []);
    for (const nested of branch) {
      await runFlowAction(nested, api, vars);
    }
    return;
  }
  if (isPlainObject(action.forEach)) {
    const forEachAction = action.forEach as {
      items: unknown;
      item: string;
      index?: string;
      actions: unknown[];
    };
    const items = await resolveValue(forEachAction.items, api, vars);
    if (!Array.isArray(items)) {
      throw new Error(`qa flow forEach items must resolve to array: ${JSON.stringify(items)}`);
    }
    for (const [index, item] of items.entries()) {
      vars[forEachAction.item] = item;
      if (forEachAction.index) {
        vars[forEachAction.index] = index;
      }
      for (const nested of forEachAction.actions) {
        await runFlowAction(nested, api, vars);
      }
    }
    return;
  }
  if (isPlainObject(action.try)) {
    const tryAction = action.try as {
      actions: unknown[];
      catchAs?: string;
      catch?: unknown[];
      finally?: unknown[];
    };
    try {
      for (const nested of tryAction.actions) {
        await runFlowAction(nested, api, vars);
      }
    } catch (error) {
      if (!tryAction.catch && !tryAction.finally) {
        throw error;
      }
      if (tryAction.catchAs) {
        vars[tryAction.catchAs] = error;
      }
      if (tryAction.catch) {
        for (const nested of tryAction.catch) {
          await runFlowAction(nested, api, vars);
        }
      } else {
        throw error;
      }
    } finally {
      if (tryAction.finally) {
        for (const nested of tryAction.finally) {
          await runFlowAction(nested, api, vars);
        }
      }
    }
    return;
  }
  throw new Error(`unknown qa flow action: ${JSON.stringify(action)}`);
}

export async function runScenarioFlow(params: {
  api: QaFlowApi;
  flow: QaScenarioFlow;
  scenarioTitle: string;
}) {
  const vars: QaFlowVars = {};
  const steps: QaSuiteStep[] = params.flow.steps.map((step) => ({
    name: step.name,
    run: async () => {
      for (const action of step.actions) {
        await runFlowAction(action, params.api, vars);
      }
      if (!step.detailsExpr) {
        return undefined;
      }
      const details = await evalExpr(step.detailsExpr, params.api, vars);
      return formatFlowDetails(details);
    },
  }));
  return await params.api.runScenario(params.scenarioTitle, steps);
}

export function describeScenarioFlowError(error: unknown) {
  return formatErrorMessage(error);
}

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge