defmodule Mix.Tasks.OpenResponses.Compliance do @moduledoc """ Run the Open Responses upstream compliance suite against a running server. ## Usage Start your server in one terminal: PHX_SERVER=true MIX_ENV=test mix phx.server Then in another terminal: mix open_responses.compliance By default, targets `http://localhost:4000/v1`. Pass `--base-url` to target a different host: mix open_responses.compliance --base-url http://staging.example.com/v1 ## Prerequisites Requires [Bun](https://bun.sh) and a clone of the compliance test runner. The task will clone the runner into `tmp/compliance` if it is not present. Alternatively, point the official interactive tester at your running server: https://www.openresponses.org/compliance ## Options - `--base-url` — base URL of the server under test (default: http://localhost:4000/v1) - `--api-key` — API key to pass in the Authorization header (default: empty) - `--filter` — comma-separated list of test case IDs to run """ use Mix.Task @repo_url "https://github.com/openresponses/openresponses.git" @clone_dir "tmp/compliance" @shortdoc "Run the Open Responses compliance suite against a server" @impl Mix.Task def run(args) do {opts, _rest, _invalid} = OptionParser.parse(args, strict: [base_url: :string, api_key: :string, filter: :string] ) base_url = Keyword.get(opts, :base_url, "http://localhost:4000/v1") api_key = Keyword.get(opts, :api_key, "") filter = Keyword.get(opts, :filter) ensure_bun!() ensure_clone!() cmd_args = build_args(base_url, api_key, filter) Mix.shell().info("Running compliance suite against #{base_url} ...") case System.cmd("bun", cmd_args, cd: @clone_dir, into: IO.stream(:stdio, :line), stderr_to_stdout: true ) do {_, 0} -> Mix.shell().info("Compliance suite passed.") {_, code} -> Mix.raise("Compliance suite exited with code #{code}.") end end defp ensure_bun! do case System.find_executable("bun") do nil -> Mix.raise("Bun is required. Install it from https://bun.sh") _ -> :ok end end defp ensure_clone! do unless File.dir?(@clone_dir) do Mix.shell().info("Cloning compliance runner into #{@clone_dir} ...") File.mkdir_p!("tmp") case System.cmd("git", ["clone", "--depth", "1", @repo_url, @clone_dir], stderr_to_stdout: true ) do {_, 0} -> System.cmd("bun", ["install"], cd: @clone_dir, stderr_to_stdout: true) {output, code} -> Mix.raise("git clone failed (exit #{code}):\n#{output}") end end end defp build_args(base_url, api_key, filter) do base = ["run", "bin/compliance-tests.ts", "--base-url", base_url, "--api-key", api_key] if filter, do: base ++ ["--filter", filter], else: base end end