Files
rose-ash/spec/tests/test-io-registry.sx
giles 5f72801901 Step 3: IO registry — spec-level defio + io contract dispatch
Promotes defio from native OCaml special form to spec-level CEK
evaluator feature. The IO registry is now the contract layer between
evaluator and platform.

Evaluator additions (spec/evaluator.sx):
- *io-registry* mutable dict global (like *library-registry*)
- io-register!, io-registered?, io-lookup, io-names accessors
- defio-parse-kwargs! recursive keyword parser
- sf-defio processes (defio "name" :category :data :params (...) ...)
- "defio" dispatch in step-eval-list
- step-sf-io: the contract function — validates against registry,
  then delegates to perform for IO suspension
- "io" dispatch in step-eval-list

Native OCaml defio handlers removed from:
- sx_server.ml (~20 lines)
- sx_browser.ml (~20 lines)
- run_tests.ml (~18 lines)
All replaced with __io-registry alias to spec's *io-registry*.

IO accessor functions bound in run_tests.ml env so tests can
call io-registered?, io-lookup, io-names.

10 new tests (spec/tests/test-io-registry.sx):
- defio populates registry
- io-lookup returns spec with name/category/returns/doc
- io-registered?/io-names work correctly
- kwargs parsing (batchable, cacheable, params)
- io contract rejects unregistered ops
- io contract passes validation for registered ops

2608/2608 tests passing (+10 new).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:18:04 +00:00

102 lines
2.7 KiB
Plaintext

;; IO registry tests — defio, *io-registry*, accessor functions, io contract
(defsuite
"io-registry-basic"
(deftest
"defio registers an IO operation"
(defio
"test-io-basic"
:category :data
:params ()
:returns "string"
:doc "Basic test op.")
(assert (io-registered? "test-io-basic")))
(deftest
"io-lookup returns spec dict"
(defio
"test-io-lookup"
:category :effect
:params (x)
:returns "nil"
:doc "Test effect.")
(let
((spec (io-lookup "test-io-lookup")))
(assert= (get spec "name") "test-io-lookup")
(assert= (keyword-name (get spec "category")) "effect")
(assert= (get spec "returns") "nil")
(assert= (get spec "doc") "Test effect.")))
(deftest
"io-registered? returns false for unknown"
(assert (not (io-registered? "nonexistent-io-op"))))
(deftest
"io-names includes registered ops"
(defio
"test-io-names"
:category :data
:params ()
:returns "any"
:doc "Names test.")
(assert (contains? (io-names) "test-io-names")))
(deftest
"defio returns the spec dict"
(let
((result (defio "test-io-ret" :category :code :params (a b) :returns "string" :doc "Return test.")))
(assert= (get result "name") "test-io-ret")
(assert= (keyword-name (get result "category")) "code"))))
(defsuite
"io-registry-kwargs"
(deftest
"defio parses batchable flag"
(defio
"test-io-batch"
:category :code
:params (code lang)
:returns "string"
:batchable true
:doc "Batchable op.")
(assert= (get (io-lookup "test-io-batch") "batchable") true))
(deftest
"defio parses cacheable flag"
(defio
"test-io-cache"
:category :data
:params ()
:returns "list"
:cacheable true
:doc "Cacheable op.")
(assert= (get (io-lookup "test-io-cache") "cacheable") true))
(deftest
"defio parses params list"
(defio
"test-io-params"
:category :data
:params (a b c)
:returns "list"
:doc "Multi param.")
(assert= (len (get (io-lookup "test-io-params") "params")) 3)))
(defsuite
"io-contract"
(deftest
"io rejects unregistered operations"
(let
((caught false))
(try-catch
(fn () (io "totally-unknown-op-xyz"))
(fn (err) (set! caught true)))
(assert caught)))
(deftest
"io suspends for registered operations"
(defio
"test-io-contract"
:category :data
:params ()
:returns "string"
:doc "Contract test.")
(let
((caught-msg ""))
(try-catch
(fn () (io "test-io-contract"))
(fn (err) (set! caught-msg err)))
(assert (not (string-contains? caught-msg "unknown operation"))))))