Probatio Diabolica
A Ruby DSL-based testing framework with classic matchers and LLM-powered matchers (text and image).
This project is experimental and not production-ready.
What this project does
probatio_diabolica runs *_spec.rb files through a custom runtime (PrD::Runtime) with an RSpec-like syntax:
-
describe,context,it,pending,let,subject -
expect(...).to(...)andexpect(...).not_to(...) - standard matchers (
eq,be,includes,have,all) - LLM matcher
satisfy(...)to validate natural-language conditions
Tests are evaluated with instance_eval (not through RSpec).
Installation
In the Gemfile
gem 'probatio_diabolica'Then:
bundle installConfiguration LLM
The runtime automatically loads prd_helper.rb if present (or a file passed with -c).
Minimal example:
# prd_helper.rb
RubyLLM.configure do |config|
config.openrouter_api_key = ENV['OPENROUTER_API_KEY']
endWithout valid configuration, tests using satisfy(...) will fail.
Running tests
CLI command:
prd <file_or_directory> [options]From source checkout (without gem install), this is always valid:
bundle exec ruby bin/prd <file_or_directory> [options]MCP server (run_specs)
A minimal MCP server is available through:
bundle exec ruby bin/prd_mcpIt exposes one tool: run_specs.
Input:
-
path(required): file or directory containing specs -
config(optional): same as-c -
out(optional): same as-o -
formatters(optional): array ofsimple|html|json|pdf(default:["simple"]) -
mode(optional):verbose|synthetic(default:synthetic)
Output (structuredContent):
-
ok,exit_code -
summary(passed,failed,pending) -
artifacts(base_out,reports,annex_dir) -
logs(stdout,stderr)
Options:
-
-c, --config FILERuby config file to require -
-t, --type TYPEformatter type(s), default:simple- supported:
simple,html,json,pdf - can be repeated (
-t html -t json) or comma-separated (-t html,json,pdf)
- supported:
-
-o, --out PATHoutput base path (directory or file-like base name) -
-m, --mode MODEoutput mode, default:verbose- supported:
verbose,synthetic
- supported:
Output rules (--out):
- No
--out:- one formatter (
simple,html, orjson): output goes tostdout -
pdf: fails (PDF formatter requires --out) - multiple formatters: fails (
Multiple formatter types require --out)
- one formatter (
- With
--out PATH:- if
PATHexists as a directory, or ends with/, reports are written asPATH/report.<ext> - otherwise,
PATHis treated as a base name and reports are written asPATH.<ext> - if
PATHends with one known extension (.txt,.html,.json,.pdf), that extension is stripped before generating outputs
- if
Formatter/file extension mapping:
-
simple->.txt -
html->.html -
json->.json -
pdf->.pdf
Examples:
# single file
prd examples/basics_spec.rb
# all *_spec.rb files in a directory
prd examples
# HTML report in an existing directory (creates ./tmp/report.html)
prd examples/image_spec.rb -t html -o ./tmp/
# multiple reports from one run with shared base name
prd examples/basics_spec.rb -t html,json,pdf -o ./tmp/my_report
# compact synthetic output on console
prd examples/basics_spec.rb --mode syntheticAvailable DSL
It is inspired by RSpec but with a custom runtime and additional features.
Structure
describe 'My domain' do
context 'my context' do
let(:two) { 2 }
let(:three) { 3 }
subject { two + three }
it 'runs an assertion' do
expect.to eq(5)
end
pending 'test to implement later'
end
endAssertions
expect(actual).to matcherexpect(actual).not_to matcherexpect { |subject| ... }.to matcher-
expect.to matcher(usessubject)
Matchers
-
eq(expected)equality with== -
be(expected)object identity (equal?) -
includes(expected)inclusion forString,Array,File,PDF::Reader -
have(expected)alias inclusion viainclude? -
all(proc)checks all elements against a block -
satisfy(natural_language_condition)LLM-based validation
Browser helpers (Ferrum)
PrD::Runtime exposes helpers to test content loaded in Chrome:
-
screen(at:, width:, height:, warmup_time:)captures a PNG and returns aFile -
text(at:, css:, warmup_time:)extracts a CSS node into a.txtfile and returns aFile -
network(at:, warmup_time:)returns Ferrum network traffic -
network_urls(at:, warmup_time:)returns traffic URLs -
pdf(at:, warmup_time:)generates a PDF and returns aPDF::Reader -
html(at:, warmup_time:)returns HTML (browser.body)
Prerequisites:
- Chrome/Chromium must be installed.
- The
ferrumgem is optional and only required for these helpers.- Add
gem 'ferrum'to your Gemfile, or install it withgem install ferrum. - If it is missing, an explicit
LoadErroris raised on the first browser helper call.
- Add
Example:
it 'checks dynamic content loaded in browser' do
page_text = text(at: 'https://example.com', css: 'main')
expect(page_text).to(includes('Example Domain'))
endSource code helper (Prism)
source_code(...) uses the prism gem to parse Ruby source and extract class/method code.
It returns a PrD::Code object:
-
source(String) -
language(String, default:ruby)
Example:
let(:code) { source_code(PrD::Matchers::AllMatcher) }
it 'uses raw source text' do
expect(code.source).to(includes('class AllMatcher'))
endPrerequisites:
- The
prismgem is optional and only required forsource_code(...).- Add
gem 'prism'to your Gemfile, or install it withgem install prism. - If it is missing, an explicit
LoadErroris raised on the firstsource_code(...)call.
- Add
LLM models
You can set a model at context or it level:
context 'SQL checks', model: 'qwen/qwen-2.5-72b-instruct:free' do
it 'accepts a valid query' do
expect('SELECT * FROM users').to satisfy('This statement is valid SQL.')
end
endThe runtime keeps a model stack (it can temporarily override the parent context model).
Formatters
-
PrD::Formatters::SimpleFormatter(text console output) -
PrD::Formatters::HtmlFormatter(simple HTML output) -
PrD::Formatters::JsonFormatter(structured JSON output) -
PrD::Formatters::PdfFormatter(PDF report output)
In CLI usage:
- selecting one formatter writes one output stream/file
- selecting multiple formatters runs tests once and writes one file per formatter
Subject rendering policy (best effort)
When you define a subject, each formatter tries to render it in the most useful way for its medium:
-
SimpleFormatter:- renders readable text in terminal
- for files, prints a textual representation (for example path, file preview for
.txt)
-
HtmlFormatter:- renders text values directly
- for
PrD::Code, renders syntax-highlighted code blocks (Rouge) - for image files (
.png,.jpg,.jpeg), embeds the image in the report - for PDF subjects (
File.pdforPDF::Reader), embeds the PDF with adata:application/pdf;base64,...URI
-
PdfFormatter:- renders text values as report lines
- for
PrD::Code, renders language + code block (plain, without syntax colors) - for image files (
.png,.jpg,.jpeg), inserts the image directly in the PDF report
-
JsonFormatter:- keeps a structured representation for machine processing
- for
PrD::Code, emits a structured payload (type: "code",language,source) -
Filevalues (images, PDFs, text files, etc.) are embedded as base64 payloads -
PDF::Readervalues are also embedded as base64 (application/pdf)
The goal is to preserve readability and report size while surfacing the richest representation each formatter can reasonably support.
Useful references in this repository
- Basic example:
examples/basics_spec.rb - Code analysis example:
examples/code_example_spec.rb - Image example:
examples/image_spec.rb - Browser example:
examples/browser_spec.rb - CLI entrypoint:
bin/prd - DSL runtime:
lib/pr_d.rb
Current limitations
- Work in progress, API may change.
-
satisfy(...)requires a configured LLM provider and network access. - PDF and multi-format output require
--out.
