PropertyDamage.Export (PropertyDamage v0.2.0)

View Source

Export failure reports to various portable formats.

The Export module converts PropertyDamage failure reports into artifacts that can be shared, executed, and used for regression testing without requiring the full framework.

Export Formats

  • ExUnit (:exunit) - Regression tests using PropertyDamage
  • Scripts - Standalone scripts in multiple languages:
    • :elixir - Elixir with Req HTTP client
    • :curl - Bash with curl commands
    • :python - Python with requests library
  • LiveBook (:livebook) - Interactive notebooks for exploration

Usage

Export to String

# Generate ExUnit regression test
test_code = PropertyDamage.Export.to_exunit(failure,
  model: MyModel,
  adapter: MyHTTPAdapter
)

# Generate standalone scripts
curl_script = PropertyDamage.Export.to_script(failure, :curl,
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter
)

python_script = PropertyDamage.Export.to_script(failure, :python,
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter
)

# Generate LiveBook notebook
notebook = PropertyDamage.Export.to_livebook(failure,
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter
)

Export to File

# Save individual format
{:ok, path} = PropertyDamage.Export.save(failure, "exports/", :exunit)
{:ok, path} = PropertyDamage.Export.save(failure, "exports/", {:script, :curl})
{:ok, path} = PropertyDamage.Export.save(failure, "exports/", :livebook)

# Save all formats at once
{:ok, paths} = PropertyDamage.Export.save_all(failure, "exports/",
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter
)

HTTPSpec for Script Generation

For standalone scripts to work, the adapter should implement the optional http_spec/2 callback that describes how commands map to HTTP calls. See PropertyDamage.Export.HTTPSpec for details.

Summary

Functions

Saves an export to a file.

Saves exports in all formats to a directory.

Generates an ExUnit regression test from a failure report.

Generates a LiveBook notebook for interactive failure exploration.

Generates a standalone script in the specified language.

Types

format()

@type format() ::
  :exunit | :livebook | {:script, PropertyDamage.Export.Script.language()}

Functions

save(report, directory, format, opts \\ [])

@spec save(PropertyDamage.FailureReport.t(), Path.t(), format(), keyword()) ::
  {:ok, Path.t()} | {:error, term()}

Saves an export to a file.

The filename is automatically generated based on the seed and format.

Parameters

  • report - The failure report to export
  • directory - Directory to save the file in
  • format - Export format (:exunit, {:script, :curl}, :livebook, etc.)
  • opts - Format-specific options

Example

{:ok, path} = PropertyDamage.Export.save(failure, "exports/", :exunit)
# => {:ok, "exports/reproduce_512902757.exs"}

{:ok, path} = PropertyDamage.Export.save(failure, "scripts/", {:script, :curl},
  base_url: "http://localhost:4000"
)
# => {:ok, "scripts/reproduce_512902757.sh"}

save_all(report, directory, opts \\ [])

@spec save_all(PropertyDamage.FailureReport.t(), Path.t(), keyword()) ::
  {:ok, map()} | {:error, term()}

Saves exports in all formats to a directory.

Options

  • :base_url - Base URL for HTTP calls (required for scripts/livebook)
  • :adapter - Adapter module for HTTPSpec
  • :script_languages - Languages for scripts (default: [:elixir, :curl])
  • All other options are passed to individual generators

Example

{:ok, paths} = PropertyDamage.Export.save_all(failure, "exports/",
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter,
  script_languages: [:elixir, :curl, :python]
)

# => {:ok, %{
#   exunit: "exports/reproduce_512902757.exs",
#   livebook: "exports/reproduce_512902757.livemd",
#   script_elixir: "exports/reproduce_512902757.exs",
#   script_curl: "exports/reproduce_512902757.sh",
#   script_python: "exports/reproduce_512902757.py"
# }}

to_exunit(report, opts \\ [])

@spec to_exunit(
  PropertyDamage.FailureReport.t(),
  keyword()
) :: String.t()

Generates an ExUnit regression test from a failure report.

Options

  • :model - Model module (defaults to report.model)
  • :adapter - Adapter module (defaults to report.adapter)
  • :module_name - Module name for the test
  • :test_name - Custom test name
  • :adapter_config - Adapter configuration map
  • :expect_fixed - If true, expect the test to pass (default: false)

Example

test_code = PropertyDamage.Export.to_exunit(failure,
  model: MyModel,
  adapter: MyHTTPAdapter
)

File.write!("test/regressions/seed_123_test.exs", test_code)

to_livebook(report, opts \\ [])

@spec to_livebook(
  PropertyDamage.FailureReport.t(),
  keyword()
) :: String.t()

Generates a LiveBook notebook for interactive failure exploration.

The notebook includes:

  • Setup with Mix.install for dependencies
  • Step-by-step command execution with state tracking
  • Exploration section for "what-if" experiments

Options

  • :base_url - Base URL for HTTP calls (required)
  • :adapter - Adapter module for HTTPSpec mapping (recommended)
  • :title - Custom notebook title
  • :include_exploration - Include exploration section (default: true)
  • :include_state_tracking - Track model state (default: true)

Example

notebook = PropertyDamage.Export.to_livebook(failure,
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter,
  title: "Investigating Balance Bug"
)

File.write!("investigation.livemd", notebook)

to_script(report, language, opts \\ [])

Generates a standalone script in the specified language.

Supported Languages

  • :elixir - Elixir script with Req (runnable with elixir script.exs)
  • :curl - Bash script with curl (runnable with bash script.sh)
  • :python - Python script with requests (runnable with python script.py)

Options

  • :base_url - Base URL for HTTP calls (required)
  • :adapter - Adapter module for HTTPSpec mapping (recommended)
  • :env_var - Environment variable name for base URL (default: "BASE_URL")
  • :verbose - Include extra comments (default: true)

Example

script = PropertyDamage.Export.to_script(failure, :curl,
  base_url: "http://localhost:4000",
  adapter: MyHTTPAdapter
)

File.write!("reproduce.sh", script)