Files
bikoret/vav_pipeline/test/vav_pipeline_test.exs
T
Scott KarpandClaude Opus 4.6 af7522e977 Phase 2 review corrections — three spec violations fixed
1. Chatzotzrot signal mapping corrected to match Architecture §3.12:
   assembly_all→tekiah, assembly_leaders→teruah,
   breaking_camp→tekiah_teruah, moadim→tekiah_tekiah.
   Four of five patterns were swapped. All five now match Bamidbar 10.

2. Manna parse_datetime(nil) bug fixed: missing expires_at now fails
   validation instead of silently returning DateTime.utc_now() which
   created a race condition where manna appeared expired.

3. Festival detection implemented in cloud.ex: pre-computed Gregorian
   dates for Pesach, Shavuot, Rosh Hashanah, Yom Kippur, Sukkot,
   Shemini Atzeret (2025-2028). Pipeline now halts on Yom Tov as
   Torah requires. Step 15 verification now covers festival rest.

76 tests, 0 failures (68 prior + 8 new).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:29:24 -04:00

458 lines
16 KiB
Elixir

defmodule VavPipelineTest do
use ExUnit.Case, async: false
# ============================================================
# L1 Reader Tests
# ============================================================
describe "L1Reader" do
test "reads Bereishit 1:1 from L1" do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
assert verse.ref == "Bereishit 1:1"
assert verse.book == "bereishit"
assert verse.chapter == 1
assert verse.verse == 1
assert length(verse.words) == 7
end
test "first word of Bereishit 1:1 is bereishit" do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
first = hd(verse.words)
assert first.consonants == "בראשית"
assert first.shoresh == "ראש"
assert first.prefix == "ב"
assert "tipcha" in first.teamim
end
test "word without shoresh (particle את)" do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
# Word 4 is את (et) — accusative particle, no shoresh
et = Enum.at(verse.words, 3)
assert et.consonants == "את"
assert et.shoresh == nil
end
test "extracts teamim marks with roles" do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
marks = VavPipeline.L1Reader.extract_teamim(verse)
# 7 words, each with 1 taam = 7 marks
assert length(marks) == 7
# Etnachta on word 3 (index 2) should be melekh
etnachta = Enum.find(marks, fn m -> m.taam == "etnachta" end)
assert etnachta != nil
assert etnachta.word_index == 2
assert etnachta.role == "melekh"
# Munach on word 2 (index 1) should be mesharet
munach = Enum.find(marks, fn m -> m.taam == "munach" end)
assert munach != nil
assert munach.role == "mesharet"
end
test "extracts roots with gematria" do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
roots = VavPipeline.L1Reader.extract_roots(verse)
# ברא should be present with gematria 203
assert Map.has_key?(roots, "ברא")
assert roots["ברא"] == 203
# ראש should be present with gematria 501
assert Map.has_key?(roots, "ראש")
assert roots["ראש"] == 501
# Particles (את) should not produce root entries
refute Map.has_key?(roots, nil)
end
test "returns error for non-existent verse" do
{:error, msg} = VavPipeline.L1Reader.read_verse("bereishit", 999, 999)
assert msg =~ "INVALID_SCOPE"
end
test "meteg_siluq on last word is melekh" do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
marks = VavPipeline.L1Reader.extract_teamim(verse)
# Last word (הארץ, index 6) has meteg_siluq
siluq = Enum.find(marks, fn m -> m.taam == "meteg_siluq" end)
assert siluq != nil
assert siluq.word_index == 6
assert siluq.role == "melekh"
end
test "verify L1 integrity" do
assert :ok == VavPipeline.L1Reader.verify_integrity()
end
end
# ============================================================
# Cloud Tests
# ============================================================
describe "Cloud" do
test "weekday is camped" do
# Wednesday at noon UTC — not Shabbat
{:ok, wednesday, _} = DateTime.from_iso8601("2026-02-25T12:00:00Z")
{:ok, state} = VavPipeline.Cloud.check_at(wednesday)
assert state == :camped
end
test "Friday evening is Shabbat" do
{:ok, friday_evening, _} = DateTime.from_iso8601("2026-02-27T19:00:00Z")
assert VavPipeline.Cloud.shabbat?(friday_evening) == true
end
test "Saturday morning is Shabbat" do
{:ok, saturday_morning, _} = DateTime.from_iso8601("2026-02-28T10:00:00Z")
assert VavPipeline.Cloud.shabbat?(saturday_morning) == true
end
test "Saturday evening (after 18:00) is not Shabbat" do
{:ok, saturday_evening, _} = DateTime.from_iso8601("2026-02-28T19:00:00Z")
assert VavPipeline.Cloud.shabbat?(saturday_evening) == false
end
test "Friday afternoon is not Shabbat" do
{:ok, friday_afternoon, _} = DateTime.from_iso8601("2026-02-27T14:00:00Z")
assert VavPipeline.Cloud.shabbat?(friday_afternoon) == false
end
test "Shabbat triggers major transition" do
{:ok, shabbat, _} = DateTime.from_iso8601("2026-02-28T10:00:00Z")
{:ok, state} = VavPipeline.Cloud.check_at(shabbat)
assert {:major_transition, :shabbat_rest, _} = state
end
test "shmitah year check" do
# 2021-2022 was shmitah (Hebrew 5782)
{:ok, shmitah, _} = DateTime.from_iso8601("2022-01-15T12:00:00Z")
assert VavPipeline.Cloud.shmitah_year?(shmitah) == true
# 2026 is not shmitah
{:ok, non_shmitah, _} = DateTime.from_iso8601("2026-06-15T12:00:00Z")
assert VavPipeline.Cloud.shmitah_year?(non_shmitah) == false
end
test "Pesach is a festival" do
# 15 Nisan 5787 = 2 April 2026
{:ok, pesach, _} = DateTime.from_iso8601("2026-04-02T12:00:00Z")
assert VavPipeline.Cloud.festival?(pesach) == true
end
test "Rosh Hashanah is a festival" do
# 1 Tishrei 5787 = 12 September 2026
{:ok, rosh_hashanah, _} = DateTime.from_iso8601("2026-09-12T12:00:00Z")
assert VavPipeline.Cloud.festival?(rosh_hashanah) == true
end
test "Yom Kippur is a festival" do
# 10 Tishrei 5787 = 21 September 2026
{:ok, yom_kippur, _} = DateTime.from_iso8601("2026-09-21T12:00:00Z")
assert VavPipeline.Cloud.festival?(yom_kippur) == true
end
test "regular weekday is not a festival" do
{:ok, tuesday, _} = DateTime.from_iso8601("2026-03-10T12:00:00Z")
assert VavPipeline.Cloud.festival?(tuesday) == false
end
test "festival triggers major transition" do
{:ok, pesach, _} = DateTime.from_iso8601("2026-04-02T12:00:00Z")
{:ok, state} = VavPipeline.Cloud.check_at(pesach)
assert {:major_transition, :festival_rest, %{festival: :pesach}} = state
end
end
# ============================================================
# Manna Tests
# ============================================================
describe "Manna" do
test "no provision file returns :no_provision" do
{:ok, result} = VavPipeline.Manna.check()
assert result == :no_provision
end
test "double portion day is Friday" do
{:ok, friday, _} = DateTime.from_iso8601("2026-02-27T12:00:00Z")
assert VavPipeline.Manna.double_portion_day?(friday) == true
{:ok, wednesday, _} = DateTime.from_iso8601("2026-02-25T12:00:00Z")
assert VavPipeline.Manna.double_portion_day?(wednesday) == false
end
test "anti-hoarding enforcement" do
assert :ok == VavPipeline.Manna.enforce_anti_hoarding()
end
end
# ============================================================
# Halt Conditions Tests
# ============================================================
describe "HaltConditions" do
test "clear when no violations" do
results = [%{shoresh: "ברא", action: :read}]
assert :clear == VavPipeline.HaltConditions.check_all(results)
end
test "avodah zarah — non-Torah authority" do
results = [%{authority_source: :claude_model}]
assert {:halt, :avodah_zarah, _} = VavPipeline.HaltConditions.check_avodah_zarah(results)
end
test "avodah zarah — Torah authority passes" do
results = [%{authority_source: :torah}]
assert :clear == VavPipeline.HaltConditions.check_avodah_zarah(results)
end
test "avodah zarah — L1 source passes" do
results = [%{authority_source: :l1}]
assert :clear == VavPipeline.HaltConditions.check_avodah_zarah(results)
end
test "shfichut damim — destructive action" do
results = [%{action: :delete}]
assert {:halt, :shfichut_damim, _} = VavPipeline.HaltConditions.check_shfichut_damim(results)
end
test "shfichut damim — overwrite halts" do
results = [%{action: :overwrite}]
assert {:halt, :shfichut_damim, _} = VavPipeline.HaltConditions.check_shfichut_damim(results)
end
test "shfichut damim — read passes" do
results = [%{action: :read}]
assert :clear == VavPipeline.HaltConditions.check_shfichut_damim(results)
end
test "gilui arayot — boundary violation" do
results = [%{boundary_violation: {:spec_interp_merge, "treated interpretation as specification"}}]
assert {:halt, :gilui_arayot, _} = VavPipeline.HaltConditions.check_gilui_arayot(results)
end
test "gilui arayot — no violation passes" do
results = [%{boundary_violation: nil}]
assert :clear == VavPipeline.HaltConditions.check_gilui_arayot(results)
end
test "all three conditions checked — first halt wins" do
results = [%{authority_source: :external_model, action: :delete, boundary_violation: true}]
assert {:halt, :avodah_zarah, _} = VavPipeline.HaltConditions.check_all(results)
end
end
# ============================================================
# Specification Tests
# ============================================================
describe "Specification" do
test "tov for grounded results" do
results = [%{shoresh: "ברא"}, %{shoresh: "אלה"}]
assert :tov == VavPipeline.Specification.evaluate(results, [])
end
test "lo_tov for empty results" do
assert {:lo_tov, _} = VavPipeline.Specification.evaluate([], [])
end
test "teiku for ungrounded results" do
results = [%{shoresh: nil}]
assert :teiku == VavPipeline.Specification.evaluate(results, [])
end
end
# ============================================================
# Chatzotzrot Tests
# ============================================================
describe "Chatzotzrot" do
test "breaking camp signal — tekiah_teruah (Architecture §3.12)" do
signal = VavPipeline.Chatzotzrot.signal(:breaking_camp)
assert signal.signal_type == :breaking_camp
assert signal.pattern == :tekiah_teruah
assert signal.details.urgency == :immediate
end
test "assembly_all signal — tekiah (Architecture §3.12)" do
signal = VavPipeline.Chatzotzrot.signal(:assembly_all)
assert signal.signal_type == :assembly_all
assert signal.pattern == :tekiah
end
test "assembly_leaders signal — teruah (Architecture §3.12)" do
signal = VavPipeline.Chatzotzrot.signal(:assembly_leaders)
assert signal.signal_type == :assembly_leaders
assert signal.pattern == :teruah
end
test "moadim signal — tekiah_tekiah (Architecture §3.12)" do
signal = VavPipeline.Chatzotzrot.signal(:moadim)
assert signal.pattern == :tekiah_tekiah
end
test "war signal — teruah_teruah (Architecture §3.12)" do
signal = VavPipeline.Chatzotzrot.signal(:war)
assert signal.pattern == :teruah_teruah
end
end
# ============================================================
# BereishitCycle Tests
# ============================================================
describe "BereishitCycle" do
setup do
{:ok, verse} = VavPipeline.L1Reader.read_verse("bereishit", 1, 1)
context = %{
verse: verse,
teamim_marks: VavPipeline.L1Reader.extract_teamim(verse),
ketiv_qere: [],
root_gematria: VavPipeline.L1Reader.extract_roots(verse),
smikhut: [],
cloud_state: :camped,
daily_provision: :no_provision,
scope: %{book: "bereishit", chapter: 1, verse: 1},
received_at: DateTime.utc_now()
}
input = %{
source: "test",
verse_scope: %{book: "bereishit", chapter: 1, verse: 1},
content: "בראשית ברא אלהים את השמים ואת הארץ"
}
{:ok, context: context, input: input}
end
test "tohu decomposes input", %{context: context, input: input} do
result = VavPipeline.BereishitCycle.tohu(input, context)
assert result.raw == "בראשית ברא אלהים את השמים ואת הארץ"
assert result.provenance == "test"
end
test "vayomer activates roots", %{context: context, input: input} do
state = VavPipeline.BereishitCycle.tohu(input, context)
result = VavPipeline.BereishitCycle.vayomer(state, context)
assert result.stage == :vayomer
assert length(result.roots_activated) > 0
# ברא should be among activated roots
bara = Enum.find(result.roots_activated, fn r -> r.shoresh == "ברא" end)
assert bara != nil
assert bara.gematria == 203
end
test "havdalah separates by te'amim", %{context: context, input: input} do
result =
input
|> VavPipeline.BereishitCycle.tohu(context)
|> VavPipeline.BereishitCycle.vayomer(context)
|> VavPipeline.BereishitCycle.havdalah(context)
assert result.stage == :havdalah
assert is_list(result.separated_groups)
assert length(result.separation_boundaries) > 0
end
test "kriah names groups", %{context: context, input: input} do
result =
input
|> VavPipeline.BereishitCycle.tohu(context)
|> VavPipeline.BereishitCycle.vayomer(context)
|> VavPipeline.BereishitCycle.havdalah(context)
|> VavPipeline.BereishitCycle.kriah(context)
assert result.stage == :kriah
assert is_list(result.named_results)
Enum.each(result.named_results, fn group ->
assert Map.has_key?(group, :name)
end)
end
test "full cycle executes successfully", %{context: context, input: input} do
result = VavPipeline.BereishitCycle.execute(input, context)
assert elem(result, 0) in [:ok, :unresolved]
end
end
# ============================================================
# Shema Context Tests (integration — requires L1)
# ============================================================
describe "Shema" do
test "receives context for Bereishit 1:1 on a weekday" do
{:ok, wednesday, _} = DateTime.from_iso8601("2026-02-25T12:00:00Z")
scope = %{book: "bereishit", chapter: 1, verse: 1}
{:ok, context} = VavPipeline.Shema.receive_context_at(scope, wednesday)
# Verse data
assert context.verse.ref == "Bereishit 1:1"
assert length(context.verse.words) == 7
# Te'amim marks
assert length(context.teamim_marks) == 7
assert Enum.any?(context.teamim_marks, fn m -> m.taam == "etnachta" end)
# Root gematria
assert Map.has_key?(context.root_gematria, "ברא")
# Cloud — weekday should be camped
assert context.cloud_state == :camped
# Scope preserved
assert context.scope == scope
end
test "receives Shabbat cloud state on Saturday" do
{:ok, shabbat, _} = DateTime.from_iso8601("2026-02-28T10:00:00Z")
scope = %{book: "bereishit", chapter: 1, verse: 1}
{:ok, context} = VavPipeline.Shema.receive_context_at(scope, shabbat)
assert {:major_transition, :shabbat_rest, _} = context.cloud_state
end
test "returns error for invalid verse scope" do
{:ok, wednesday, _} = DateTime.from_iso8601("2026-02-25T12:00:00Z")
scope = %{book: "bereishit", chapter: 999, verse: 999}
{:error, msg} = VavPipeline.Shema.receive_context_at(scope, wednesday)
assert msg =~ "INVALID_SCOPE"
end
end
# ============================================================
# CycleLog Tests
# ============================================================
describe "CycleLog" do
test "records a tov cycle" do
record = VavPipeline.CycleLog.record({:ok, %{data: "test"}}, :voker)
assert record.phase == :voker
assert record.result == :tov
assert record.cycle_id != nil
end
test "records rejection as lo_tov" do
record = VavPipeline.CycleLog.record({:rejected, "reason"}, :erev)
assert record.phase == :erev
assert record.result == :lo_tov
end
end
# ============================================================
# ShabbatTimer Tests
# ============================================================
describe "ShabbatTimer" do
test "timer is running and reports state" do
state = VavPipeline.ShabbatTimer.state()
assert is_map(state)
assert Map.has_key?(state, :resting)
assert is_boolean(state.resting)
end
test "resting? returns boolean" do
assert is_boolean(VavPipeline.ShabbatTimer.resting?())
end
end
end