defmodule VavPipeline.BigdeiKodeshTest do @moduledoc """ Tests for the Bigdei Kodesh — the HTTP API layer. Shemot 28:2 — V'asita vigdei kodesh l'Aharon achicha, l'khavod ul'tifaret. Four garments for four access tiers: /peah/* — public, no auth /courtyard/* — authenticated /chatzer/* — full access /kodesh/* — operator """ use ExUnit.Case, async: false import Plug.Test import Plug.Conn alias VavPipeline.BigdeiKodesh.Router @opts Router.init([]) # ============================================================ # Pe'ah — Public Endpoint (Machaneh Yisrael) # ============================================================ describe "Pe'ah endpoint" do test "GET /peah/ask without question returns usage" do conn = conn(:get, "/peah/ask") |> Router.call(@opts) assert conn.status == 200 body = Jason.decode!(conn.resp_body) assert body["message"] =~ "Shorashim" assert body["usage"] =~ "/peah/ask" end test "GET /peah/ask with question parameter processes it" do conn = conn(:get, "/peah/ask?question=What+is+bereishit") |> Router.call(@opts) # Should return 200 or 503 (if waiting for provision) assert conn.status in [200, 503] end test "POST /peah/ask with valid question" do conn = conn(:post, "/peah/ask", Jason.encode!(%{question: "What is bereishit?"})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) assert conn.status in [200, 503] body = Jason.decode!(conn.resp_body) if conn.status == 200 do assert Map.has_key?(body, "answer") or Map.has_key?(body, "processing") end end test "POST /peah/ask without question returns 422" do conn = conn(:post, "/peah/ask", Jason.encode!(%{not_question: "wrong field"})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) assert conn.status == 422 body = Jason.decode!(conn.resp_body) assert body["error"]["code"] == "KIYOR_VALIDATION_FAILED" assert body["error"]["type"] == "purification_needed" end test "POST /peah/ask with empty question returns 422" do conn = conn(:post, "/peah/ask", Jason.encode!(%{question: ""})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) assert conn.status == 422 end end # ============================================================ # Courtyard — Authenticated Endpoint # ============================================================ describe "Courtyard endpoint" do test "POST /courtyard/offering without auth returns 401" do conn = conn(:post, "/courtyard/offering", Jason.encode!(%{content: "test"})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) assert conn.status == 401 body = Jason.decode!(conn.resp_body) assert body["error"]["code"] == "UNAUTHORIZED" end test "POST /courtyard/offering with auth but no content returns 422" do conn = conn(:post, "/courtyard/offering", Jason.encode!(%{offering_type: "shelamim"})) |> put_req_header("content-type", "application/json") |> put_req_header("authorization", "Bearer test-token") |> Router.call(@opts) assert conn.status == 422 body = Jason.decode!(conn.resp_body) assert body["error"]["code"] == "KIYOR_VALIDATION_FAILED" end test "POST /courtyard/offering with auth and content processes" do conn = conn(:post, "/courtyard/offering", Jason.encode!(%{ content: "bereishit bara elohim", offering_type: "shelamim" })) |> put_req_header("content-type", "application/json") |> put_req_header("authorization", "Bearer test-token") |> Router.call(@opts) assert conn.status in [200, 503] if conn.status == 200 do body = Jason.decode!(conn.resp_body) assert Map.has_key?(body, "offering_id") assert Map.has_key?(body, "offering_receipt") assert body["offering_receipt"]["type"] == "shelamim" end end end # ============================================================ # Chatzer — Sanhedrin Endpoint # ============================================================ describe "Chatzer endpoint" do test "POST /chatzer/sanhedrin without auth returns 401" do conn = conn(:post, "/chatzer/sanhedrin", Jason.encode!(%{case: "test case"})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) assert conn.status == 401 end test "POST /chatzer/sanhedrin without case returns 422" do conn = conn(:post, "/chatzer/sanhedrin", Jason.encode!(%{not_case: "wrong field"})) |> put_req_header("content-type", "application/json") |> put_req_header("authorization", "Bearer test-token") |> Router.call(@opts) assert conn.status == 422 end test "POST /chatzer/sanhedrin with valid case deliberates" do conn = conn(:post, "/chatzer/sanhedrin", Jason.encode!(%{ case: "Is cooking on Shabbat permitted for pikuach nefesh?" })) |> put_req_header("content-type", "application/json") |> put_req_header("authorization", "Bearer test-token") |> Router.call(@opts) assert conn.status == 200 body = Jason.decode!(conn.resp_body) assert Map.has_key?(body, "case_ref") assert Map.has_key?(body, "deliberation") assert body["deliberation"]["outcome"] in ["accepted", "rejected", "teiku"] assert body["note"] =~ "not a binding ruling" end end # ============================================================ # Kodesh — Operator Endpoint # ============================================================ describe "Kodesh endpoint" do test "GET /kodesh/health returns health check" do conn = conn(:get, "/kodesh/health") |> Router.call(@opts) assert conn.status in [200, 503] body = Jason.decode!(conn.resp_body) assert Map.has_key?(body, "overall") assert Map.has_key?(body, "checks") assert body["overall"] in ["healthy", "degraded", "critical", "shabbat"] # Required health checks assert Map.has_key?(body["checks"], "l1_hash") assert Map.has_key?(body["checks"], "halt_monitor_alive") assert Map.has_key?(body["checks"], "esh_tamid") end test "GET /kodesh/status returns system status" do conn = conn(:get, "/kodesh/status") |> Router.call(@opts) assert conn.status == 200 body = Jason.decode!(conn.resp_body) assert Map.has_key?(body, "cloud_state") assert Map.has_key?(body, "components") assert Map.has_key?(body, "halt_status") assert Map.has_key?(body, "signal_count") end end # ============================================================ # Error handling — Chatat protocol # ============================================================ describe "error handling" do test "unknown endpoint returns 404" do conn = conn(:get, "/unknown/path") |> Router.call(@opts) assert conn.status == 404 body = Jason.decode!(conn.resp_body) assert body["error"]["code"] == "NOT_FOUND" end test "all errors follow chatat format" do conn = conn(:post, "/peah/ask", Jason.encode!(%{})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) assert conn.status == 422 body = Jason.decode!(conn.resp_body) # Chatat format: error.code, error.type, error.message, error.guidance assert Map.has_key?(body["error"], "code") assert Map.has_key?(body["error"], "type") assert Map.has_key?(body["error"], "message") assert Map.has_key?(body["error"], "guidance") end test "all responses are JSON" do conn = conn(:get, "/kodesh/health") |> Router.call(@opts) content_type = get_resp_header(conn, "content-type") assert Enum.any?(content_type, &String.contains?(&1, "application/json")) end end end