Skip to content

Commit 5a63e73

Browse files
hyperpolymathclaude
andcommitted
test: add registry + runtime test suite expansion
New tests for hyperpolymath_forge, language_adapters, runtime manager, source_builder, and url_handler. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 68ccac1 commit 5a63e73

5 files changed

Lines changed: 706 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
defmodule Opsm.Registries.HyperpPolymathForgeTest do
4+
use ExUnit.Case, async: false
5+
6+
alias Opsm.Registries.HyperpPolymathForge
7+
8+
# ---------------------------------------------------------------------------
9+
# Module interface contract
10+
# ---------------------------------------------------------------------------
11+
12+
describe "module exports" do
13+
test "ensure_cache/0 is defined" do
14+
assert function_exported?(HyperpPolymathForge, :ensure_cache, 0)
15+
end
16+
17+
test "search/2 is defined" do
18+
assert function_exported?(HyperpPolymathForge, :search, 2)
19+
end
20+
21+
test "fetch_package/2 is defined" do
22+
assert function_exported?(HyperpPolymathForge, :fetch_package, 2)
23+
end
24+
25+
test "versions/1 is defined" do
26+
assert function_exported?(HyperpPolymathForge, :versions, 1)
27+
end
28+
29+
test "exists?/1 is defined" do
30+
assert function_exported?(HyperpPolymathForge, :exists?, 1)
31+
end
32+
end
33+
34+
# ---------------------------------------------------------------------------
35+
# search/2 — always returns a list (even on network failure)
36+
# ---------------------------------------------------------------------------
37+
38+
describe "search/2" do
39+
test "returns a list for any query" do
40+
result = HyperpPolymathForge.search("opsm", [])
41+
assert is_list(result)
42+
end
43+
44+
test "returns a list for empty string query" do
45+
result = HyperpPolymathForge.search("", [])
46+
assert is_list(result)
47+
end
48+
end
49+
50+
# ---------------------------------------------------------------------------
51+
# versions/1
52+
# ---------------------------------------------------------------------------
53+
54+
describe "versions/1" do
55+
test "returns ok tuple with list for any package name" do
56+
assert {:ok, versions} = HyperpPolymathForge.versions("opsm")
57+
assert is_list(versions)
58+
end
59+
60+
test "returns ok tuple with list for non-existent package" do
61+
assert {:ok, versions} = HyperpPolymathForge.versions("xyz-definitely-not-a-package-999")
62+
assert is_list(versions)
63+
end
64+
end
65+
66+
# ---------------------------------------------------------------------------
67+
# exists?/1
68+
# ---------------------------------------------------------------------------
69+
70+
describe "exists?/1" do
71+
test "returns boolean for any query" do
72+
result = HyperpPolymathForge.exists?("opsm")
73+
assert is_boolean(result)
74+
end
75+
76+
test "returns false for obviously non-existent package" do
77+
result = HyperpPolymathForge.exists?("xyz-definitely-not-a-hp-package-abc-999")
78+
assert result == false
79+
end
80+
end
81+
82+
# ---------------------------------------------------------------------------
83+
# fetch_package/2
84+
# ---------------------------------------------------------------------------
85+
86+
describe "fetch_package/2" do
87+
test "returns error tuple for non-existent package" do
88+
result = HyperpPolymathForge.fetch_package("xyz-definitely-not-a-hp-package-abc-999", "latest")
89+
assert match?({:error, _}, result)
90+
end
91+
92+
@tag :external_api
93+
test "returns ok or not_found for opsm itself" do
94+
case HyperpPolymathForge.fetch_package("opsm", "latest") do
95+
{:ok, pkg} ->
96+
assert is_map(pkg)
97+
assert pkg.forth == :hf
98+
{:error, :not_found} ->
99+
# Acceptable if GitHub API unavailable in CI or cache miss
100+
:ok
101+
{:error, _} ->
102+
:ok
103+
end
104+
end
105+
end
106+
107+
# ---------------------------------------------------------------------------
108+
# ensure_cache/0 — idempotent, non-crashing
109+
# ---------------------------------------------------------------------------
110+
111+
describe "ensure_cache/0" do
112+
test "does not raise when called multiple times" do
113+
# First call may hit the network (skippable in CI); subsequent calls use ETS
114+
HyperpPolymathForge.ensure_cache()
115+
HyperpPolymathForge.ensure_cache()
116+
HyperpPolymathForge.ensure_cache()
117+
# If we get here, idempotency is satisfied
118+
assert true
119+
end
120+
end
121+
end
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
#
4+
# Consolidated tests for all new language/database registry adapters.
5+
# Each adapter follows the same contract (search/2, fetch_package/2,
6+
# versions/1, exists?/1) so we test them with a shared suite.
7+
8+
defmodule Opsm.Registries.LanguageAdaptersTest do
9+
use ExUnit.Case, async: true
10+
11+
# All new adapters introduced in the first-class system sprint (2026-04-12)
12+
@adapters [
13+
{Opsm.Registries.Betlang, :betlang, "betlang-rt"},
14+
{Opsm.Registries.Ephapax, :ephapax, "ephapax-std"},
15+
{Opsm.Registries.Phronesis, :phronesis, "phronesis-core"},
16+
{Opsm.Registries.Tangle, :tangle, "tangle-std"},
17+
{Opsm.Registries.Wokelang, :wokelang, "wokelang-std"},
18+
{Opsm.Registries.Lithoglyph, :lithoglyph, "lithoglyph-core"},
19+
{Opsm.Registries.Quandledb, :quandledb, "quandledb-core"},
20+
{Opsm.Registries.Nqc, :nqc, "nqc-core"},
21+
]
22+
23+
# ---------------------------------------------------------------------------
24+
# Module interface — every adapter must export the standard 4 functions
25+
# ---------------------------------------------------------------------------
26+
27+
for {mod, _forth, _known_pkg} <- @adapters do
28+
mod_name = mod |> Module.split() |> List.last()
29+
30+
describe "#{mod_name} — module interface" do
31+
test "exports search/2" do
32+
assert function_exported?(unquote(mod), :search, 2)
33+
end
34+
35+
test "exports fetch_package/2" do
36+
assert function_exported?(unquote(mod), :fetch_package, 2)
37+
end
38+
39+
test "exports versions/1" do
40+
assert function_exported?(unquote(mod), :versions, 1)
41+
end
42+
43+
test "exports exists?/1" do
44+
assert function_exported?(unquote(mod), :exists?, 1)
45+
end
46+
end
47+
end
48+
49+
# ---------------------------------------------------------------------------
50+
# search/2 — must return a list, never crash
51+
# ---------------------------------------------------------------------------
52+
53+
for {mod, _forth, _known_pkg} <- @adapters do
54+
mod_name = mod |> Module.split() |> List.last()
55+
56+
describe "#{mod_name}.search/2" do
57+
test "returns list for a relevant query" do
58+
result = unquote(mod).search(unquote(mod_name |> String.downcase()), [])
59+
assert is_list(result)
60+
end
61+
62+
test "returns list for empty string" do
63+
result = unquote(mod).search("", [])
64+
assert is_list(result)
65+
end
66+
67+
test "returns list for nonsense query" do
68+
result = unquote(mod).search("xyzzy-no-match-999", [])
69+
assert is_list(result)
70+
end
71+
end
72+
end
73+
74+
# ---------------------------------------------------------------------------
75+
# exists?/1 — must return a boolean, never crash
76+
# ---------------------------------------------------------------------------
77+
78+
for {mod, _forth, known_pkg} <- @adapters do
79+
mod_name = mod |> Module.split() |> List.last()
80+
81+
describe "#{mod_name}.exists?/1" do
82+
test "returns boolean for known package name" do
83+
result = unquote(mod).exists?(unquote(known_pkg))
84+
assert is_boolean(result)
85+
end
86+
87+
test "returns false for obviously non-existent package" do
88+
result = unquote(mod).exists?("xyz-definitely-not-real-abc-999")
89+
assert result == false
90+
end
91+
end
92+
end
93+
94+
# ---------------------------------------------------------------------------
95+
# versions/1 — must return {:ok, list}
96+
# ---------------------------------------------------------------------------
97+
98+
for {mod, _forth, known_pkg} <- @adapters do
99+
mod_name = mod |> Module.split() |> List.last()
100+
101+
describe "#{mod_name}.versions/1" do
102+
test "returns ok tuple with list for known package" do
103+
assert {:ok, versions} = unquote(mod).versions(unquote(known_pkg))
104+
assert is_list(versions)
105+
end
106+
107+
test "returns ok tuple with list for unknown package" do
108+
assert {:ok, versions} = unquote(mod).versions("xyz-definitely-not-real-abc-999")
109+
assert is_list(versions)
110+
end
111+
end
112+
end
113+
114+
# ---------------------------------------------------------------------------
115+
# fetch_package/2 — non-existent packages return {:error, :not_found}
116+
# ---------------------------------------------------------------------------
117+
118+
for {mod, _forth, _known_pkg} <- @adapters do
119+
mod_name = mod |> Module.split() |> List.last()
120+
121+
describe "#{mod_name}.fetch_package/2" do
122+
test "returns error for non-existent package" do
123+
result = unquote(mod).fetch_package("xyz-definitely-not-real-abc-999", "latest")
124+
assert match?({:error, _}, result)
125+
end
126+
127+
@tag :external_api
128+
test "returns ok or not_found for a known package name" do
129+
case unquote(mod).fetch_package(unquote(mod_name |> String.downcase() |> Kernel.<>("-core")), "latest") do
130+
{:ok, pkg} ->
131+
assert is_map(pkg)
132+
assert Map.has_key?(pkg, :forth)
133+
{:error, :not_found} -> :ok
134+
{:error, _} -> :ok
135+
end
136+
end
137+
end
138+
end
139+
end

0 commit comments

Comments
 (0)