Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions ffi/connectors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"_spdx": "MPL-2.0",
"_note": "Golden source-of-truth for the hexadeca-connector wire contract. The Zig enum (ffi/zig/src/hexadeca.zig), the Idris2 ABI (src/abi/Types.idr), and the Rust client (clients/rust/hypatia-client/src/connector.rs) MUST all agree with this list, in this exact order; id is the C ABI wire id. Drift is guarded by test/hexadeca_contract_test.exs. Wire ordering is load-bearing -- do not renumber.",
"connectors": [
{ "id": 0, "name": "grpc" },
{ "id": 1, "name": "graphql" },
{ "id": 2, "name": "rest" },
{ "id": 3, "name": "flatbuffers" },
{ "id": 4, "name": "bebop" },
{ "id": 5, "name": "jsonrpc" },
{ "id": 6, "name": "websocket" },
{ "id": 7, "name": "mqtt" },
{ "id": 8, "name": "trpc" },
{ "id": 9, "name": "capnproto" },
{ "id": 10, "name": "soap" },
{ "id": 11, "name": "verisimdb-rest" },
{ "id": 12, "name": "bsp" },
{ "id": 13, "name": "scip" },
{ "id": 14, "name": "ipfs" },
{ "id": 15, "name": "arrow-flight" }
]
}
53 changes: 53 additions & 0 deletions test/hexadeca_contract_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# SPDX-License-Identifier: MPL-2.0
defmodule Hypatia.HexadecaContractTest do
@moduledoc false
use ExUnit.Case, async: true

# Single-oracle drift guard for the hexadeca-connector wire contract.
#
# `ffi/connectors.json` is the golden source-of-truth for the 16-connector
# surface. The Zig enum, the Idris2 ABI, and the Rust client each mirror it;
# this test reads the golden plus all three source files and fails if any
# mirror drifts in name or order. Wire ordering is load-bearing
# (see ffi/zig/src/hexadeca.zig).

@root Path.expand("..", __DIR__)
@golden Path.join(@root, "ffi/connectors.json")

@sources [
{"Zig (hexadeca.zig)", Path.join(@root, "ffi/zig/src/hexadeca.zig"),
~r/\.\w+\s*=>\s*"([a-z0-9-]+)"/},
{"Idris2 (Types.idr)", Path.join(@root, "src/abi/Types.idr"),
~r/connectorName\s+\w+\s*=\s*"([a-z0-9-]+)"/},
{"Rust (connector.rs)", Path.join(@root, "clients/rust/hypatia-client/src/connector.rs"),
~r/Connector::\w+\s*=>\s*"([a-z0-9-]+)"/}
]

defp golden_connectors do
@golden |> File.read!() |> Jason.decode!() |> Map.fetch!("connectors")
end

defp names_in(path, regex) do
regex |> Regex.scan(File.read!(path)) |> Enum.map(fn [_, name] -> name end)
end

test "golden fixture lists the 16 connectors with sequential wire ids" do
conns = golden_connectors()
assert length(conns) == 16
assert Enum.map(conns, & &1["id"]) == Enum.to_list(0..15)
end

test "every language mirror matches the golden in name and order" do
golden_names = Enum.map(golden_connectors(), & &1["name"])

for {label, path, regex} <- @sources do
names = names_in(path, regex)

assert length(names) == 16,
"#{label}: expected 16 connector names, found #{length(names)}"

assert names == golden_names,
"#{label} drifted from ffi/connectors.json\n golden: #{inspect(golden_names)}\n found: #{inspect(names)}"
end
end
end
Loading