UNPKG

@firfi/quint-connect

Version:

Model-based testing framework connecting Quint specifications to TypeScript implementations

50 lines 3.08 kB
import { Effect, Predicate, Schema } from "effect"; import { buildEffectPicksDecoder } from "../itf/picks.js"; import { MbtMeta as MbtMetaSchema } from "../itf/schema.js"; import { actionContext, traceReplayError } from "./replay-errors.js"; import { resolveNestedValue, stripMetadata } from "./trace-state.js"; const extractMbtMeta = (state, context) => Effect.mapError(Schema.decodeUnknown(MbtMetaSchema)(state), (cause) => traceReplayError(actionContext(context, "unknown"), `Failed to extract MBT metadata: ${cause}`)); const isItfOption = (value) => Predicate.isRecord(value) && (value["tag"] === "Some" || value["tag"] === "None"); const normalizeNondetPathPick = (value) => isItfOption(value) ? value : { tag: "Some", value }; const normalizeNondetPathPicks = (value) => Predicate.isRecord(value) ? new Map(Object.entries(value).map(([key, pick]) => [key, normalizeNondetPathPick(pick)])) : new Map(); const extractFromNondetPath = (state, nondetPath, context) => { const raw = resolveNestedValue(state, nondetPath); if (!Predicate.isRecord(raw) || typeof raw["tag"] !== "string") { return Effect.fail(traceReplayError(actionContext(context, "unknown"), `Expected sum type {tag, value} at path ${nondetPath.join(".")}, got: ${JSON.stringify(raw)}`)); } const action = raw["tag"]; const value = raw["value"]; return Effect.succeed({ action, nondetPicks: normalizeNondetPathPicks(value) }); }; const decodeReplayAction = (state, nondetPath, context) => nondetPath.length > 0 ? extractFromNondetPath(state, nondetPath, context) : Effect.map(extractMbtMeta(state, context), (meta) => ({ action: meta["mbt::actionTaken"], nondetPicks: new Map(Object.entries(meta["mbt::nondetPicks"])) })); const projectSpecState = (state, statePath, action, context) => { if (statePath.length === 0) { return Effect.succeed(stripMetadata(state)); } const projected = resolveNestedValue(state, statePath); return projected === undefined ? Effect.fail(traceReplayError(actionContext(context, action), `Expected state at path ${statePath.join(".")}, got: undefined`)) : Effect.succeed(projected); }; /** Decode action and picks without projecting unused specification state. */ export const extractReplayAction = decodeReplayAction; /** Decode all data needed to dispatch and check one trace state. */ export const decodeReplayStep = (state, config, context) => Effect.gen(function* () { const replayAction = yield* decodeReplayAction(state, config.nondetPath ?? [], context); const specState = replayAction.action !== "" ? yield* projectSpecState(state, config.statePath ?? [], replayAction.action, context) : undefined; return { ...replayAction, specState }; }); export const buildPicksDecoder = (buildEffectPicksDecoder); export const buildPicksDecoders = (actions) => new Map(Object.entries(actions) .filter((entry) => entry[1] !== undefined) .map(([name, def]) => [name, buildPicksDecoder(def.picks)])); //# sourceMappingURL=replay-actions.js.map