defmodule Aether.ManifestTest do use ExUnit.Case, async: true @manifest_path Path.join(__DIR__, "../../manifest.json") |> Path.expand() setup_all do content = File.read!(@manifest_path) manifest = Jason.decode!(content) %{manifest: manifest} end describe "manifest.json" do test "is valid JSON", %{manifest: manifest} do assert is_map(manifest) end test "has required fields", %{manifest: manifest} do required = [ "name", "version", "entry_module", "host_api", "otp_app", "provides", "requires" ] for field <- required do assert Map.has_key?(manifest, field), "manifest.json must contain #{inspect(field)}" end end test "name matches OTP app name", %{manifest: manifest} do assert manifest["name"] == "aether" assert manifest["otp_app"] == manifest["name"] end test "entry_module uses Tribes.Plugins namespace and Plugin suffix", %{manifest: manifest} do module_name = manifest["entry_module"] assert is_binary(module_name) assert Regex.match?( ~r/^Tribes\.Plugins\.[A-Z][A-Za-z0-9_]*(\.[A-Z][A-Za-z0-9_]*)*\.Plugin$/, module_name ) end test "provides contains valid capability identifiers", %{manifest: manifest} do for cap <- manifest["provides"] do assert Regex.match?(~r/^[a-z][a-z0-9_]*(@\d+)?$/, cap), "invalid capability identifier: #{inspect(cap)}" end end test "requires contains valid capability identifiers", %{manifest: manifest} do for cap <- manifest["requires"] do assert Regex.match?(~r/^[a-z][a-z0-9_]*(@\d+)?$/, cap), "invalid capability identifier: #{inspect(cap)}" end end test "host_api is a string version", %{manifest: manifest} do assert is_binary(manifest["host_api"]) end test "entry_module matches actual plugin module", %{manifest: manifest} do module = String.to_atom("Elixir.#{manifest["entry_module"]}") assert Code.ensure_loaded?(module), "entry_module #{manifest["entry_module"]} must be loadable" end end end