"""Auto-generated from test.sx — SX spec self-tests.

DO NOT EDIT. Regenerate with:
    python shared/sx/ref/bootstrap_test.py --output shared/sx/tests/test_sx_spec.py
"""
from __future__ import annotations

import pytest
from shared.sx.parser import parse_all
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline


_PREAMBLE = '''(define assert-equal (fn (expected actual) (assert (equal? expected actual) (str "Expected " (str expected) " but got " (str actual)))))
(define assert-not-equal (fn (a b) (assert (not (equal? a b)) (str "Expected values to differ but both are " (str a)))))
(define assert-true (fn (val) (assert val (str "Expected truthy but got " (str val)))))
(define assert-false (fn (val) (assert (not val) (str "Expected falsy but got " (str val)))))
(define assert-nil (fn (val) (assert (nil? val) (str "Expected nil but got " (str val)))))
(define assert-type (fn (expected-type val) (let ((actual-type (if (nil? val) "nil" (if (boolean? val) "boolean" (if (number? val) "number" (if (string? val) "string" (if (list? val) "list" (if (dict? val) "dict" "unknown")))))))) (assert (= expected-type actual-type) (str "Expected type " expected-type " but got " actual-type)))))
(define assert-length (fn (expected-len col) (assert (= (len col) expected-len) (str "Expected length " expected-len " but got " (len col)))))
(define assert-contains (fn (item col) (assert (some (fn (x) (equal? x item)) col) (str "Expected collection to contain " (str item)))))
(define assert-throws (fn (thunk) (let ((result (try-call thunk))) (assert (not (get result "ok")) "Expected an error to be thrown but none was"))))'''


def _make_env() -> dict:
    """Create a fresh env with assertion helpers loaded."""
    env = {}
    for expr in parse_all(_PREAMBLE):
        _trampoline(_eval(expr, env))
    return env


def _run(sx_source: str, env: dict | None = None) -> object:
    """Evaluate SX source and return the result."""
    if env is None:
        env = _make_env()
    exprs = parse_all(sx_source)
    result = None
    for expr in exprs:
        result = _trampoline(_eval(expr, env))
    return result

class TestSpecLiterals:
    """test.sx suite: literals"""

    def test_numbers_are_numbers(self):
        _run('(do (assert-type "number" 42) (assert-type "number" 3.14) (assert-type "number" -1))')

    def test_strings_are_strings(self):
        _run('(do (assert-type "string" "hello") (assert-type "string" ""))')

    def test_booleans_are_booleans(self):
        _run('(do (assert-type "boolean" true) (assert-type "boolean" false))')

    def test_nil_is_nil(self):
        _run('(do (assert-type "nil" nil) (assert-nil nil))')

    def test_lists_are_lists(self):
        _run('(do (assert-type "list" (list 1 2 3)) (assert-type "list" (list)))')

    def test_dicts_are_dicts(self):
        _run('(assert-type "dict" {:a 1 :b 2})')


class TestSpecArithmetic:
    """test.sx suite: arithmetic"""

    def test_addition(self):
        _run('(do (assert-equal 3 (+ 1 2)) (assert-equal 0 (+ 0 0)) (assert-equal -1 (+ 1 -2)) (assert-equal 10 (+ 1 2 3 4)))')

    def test_subtraction(self):
        _run('(do (assert-equal 1 (- 3 2)) (assert-equal -1 (- 2 3)))')

    def test_multiplication(self):
        _run('(do (assert-equal 6 (* 2 3)) (assert-equal 0 (* 0 100)) (assert-equal 24 (* 1 2 3 4)))')

    def test_division(self):
        _run('(do (assert-equal 2 (/ 6 3)) (assert-equal 2.5 (/ 5 2)))')

    def test_modulo(self):
        _run('(do (assert-equal 1 (mod 7 3)) (assert-equal 0 (mod 6 3)))')


class TestSpecComparison:
    """test.sx suite: comparison"""

    def test_equality(self):
        _run('(do (assert-true (= 1 1)) (assert-false (= 1 2)) (assert-true (= "a" "a")) (assert-false (= "a" "b")))')

    def test_deep_equality(self):
        _run('(do (assert-true (equal? (list 1 2 3) (list 1 2 3))) (assert-false (equal? (list 1 2) (list 1 3))) (assert-true (equal? {:a 1} {:a 1})) (assert-false (equal? {:a 1} {:a 2})))')

    def test_ordering(self):
        _run('(do (assert-true (< 1 2)) (assert-false (< 2 1)) (assert-true (> 2 1)) (assert-true (<= 1 1)) (assert-true (<= 1 2)) (assert-true (>= 2 2)) (assert-true (>= 3 2)))')


class TestSpecStrings:
    """test.sx suite: strings"""

    def test_str_concatenation(self):
        _run('(do (assert-equal "abc" (str "a" "b" "c")) (assert-equal "hello world" (str "hello" " " "world")) (assert-equal "42" (str 42)) (assert-equal "" (str)))')

    def test_string_length(self):
        _run('(do (assert-equal 5 (string-length "hello")) (assert-equal 0 (string-length "")))')

    def test_substring(self):
        _run('(do (assert-equal "ell" (substring "hello" 1 4)) (assert-equal "hello" (substring "hello" 0 5)))')

    def test_string_contains(self):
        _run('(do (assert-true (string-contains? "hello world" "world")) (assert-false (string-contains? "hello" "xyz")))')

    def test_upcase_and_downcase(self):
        _run('(do (assert-equal "HELLO" (upcase "hello")) (assert-equal "hello" (downcase "HELLO")))')

    def test_trim(self):
        _run('(do (assert-equal "hello" (trim "  hello  ")) (assert-equal "hello" (trim "hello")))')

    def test_split_and_join(self):
        _run('(do (assert-equal (list "a" "b" "c") (split "a,b,c" ",")) (assert-equal "a-b-c" (join "-" (list "a" "b" "c"))))')


class TestSpecLists:
    """test.sx suite: lists"""

    def test_constructors(self):
        _run('(do (assert-equal (list 1 2 3) (list 1 2 3)) (assert-equal (list) (list)) (assert-length 3 (list 1 2 3)))')

    def test_first_and_rest(self):
        _run('(do (assert-equal 1 (first (list 1 2 3))) (assert-equal (list 2 3) (rest (list 1 2 3))) (assert-nil (first (list))) (assert-equal (list) (rest (list))))')

    def test_nth(self):
        _run('(do (assert-equal 1 (nth (list 1 2 3) 0)) (assert-equal 2 (nth (list 1 2 3) 1)) (assert-equal 3 (nth (list 1 2 3) 2)))')

    def test_last(self):
        _run('(do (assert-equal 3 (last (list 1 2 3))) (assert-nil (last (list))))')

    def test_cons_and_append(self):
        _run('(do (assert-equal (list 0 1 2) (cons 0 (list 1 2))) (assert-equal (list 1 2 3 4) (append (list 1 2) (list 3 4))))')

    def test_reverse(self):
        _run('(do (assert-equal (list 3 2 1) (reverse (list 1 2 3))) (assert-equal (list) (reverse (list))))')

    def test_empty(self):
        _run('(do (assert-true (empty? (list))) (assert-false (empty? (list 1))))')

    def test_len(self):
        _run('(do (assert-equal 0 (len (list))) (assert-equal 3 (len (list 1 2 3))))')

    def test_contains(self):
        _run('(do (assert-true (contains? (list 1 2 3) 2)) (assert-false (contains? (list 1 2 3) 4)))')

    def test_flatten(self):
        _run('(assert-equal (list 1 2 3 4) (flatten (list (list 1 2) (list 3 4))))')


class TestSpecDicts:
    """test.sx suite: dicts"""

    def test_dict_literal(self):
        _run('(do (assert-type "dict" {:a 1 :b 2}) (assert-equal 1 (get {:a 1} "a")) (assert-equal 2 (get {:a 1 :b 2} "b")))')

    def test_assoc(self):
        _run('(do (assert-equal {:a 1 :b 2} (assoc {:a 1} "b" 2)) (assert-equal {:a 99} (assoc {:a 1} "a" 99)))')

    def test_dissoc(self):
        _run('(assert-equal {:b 2} (dissoc {:a 1 :b 2} "a"))')

    def test_keys_and_vals(self):
        _run('(let ((d {:a 1 :b 2})) (assert-length 2 (keys d)) (assert-length 2 (vals d)) (assert-contains "a" (keys d)) (assert-contains "b" (keys d)))')

    def test_has_key(self):
        _run('(do (assert-true (has-key? {:a 1} "a")) (assert-false (has-key? {:a 1} "b")))')

    def test_merge(self):
        _run('(do (assert-equal {:a 1 :b 2 :c 3} (merge {:a 1 :b 2} {:c 3})) (assert-equal {:a 99 :b 2} (merge {:a 1 :b 2} {:a 99})))')


class TestSpecPredicates:
    """test.sx suite: predicates"""

    def test_nil(self):
        _run('(do (assert-true (nil? nil)) (assert-false (nil? 0)) (assert-false (nil? false)) (assert-false (nil? "")))')

    def test_number(self):
        _run('(do (assert-true (number? 42)) (assert-true (number? 3.14)) (assert-false (number? "42")))')

    def test_string(self):
        _run('(do (assert-true (string? "hello")) (assert-false (string? 42)))')

    def test_list(self):
        _run('(do (assert-true (list? (list 1 2))) (assert-false (list? "not a list")))')

    def test_dict(self):
        _run('(do (assert-true (dict? {:a 1})) (assert-false (dict? (list 1))))')

    def test_boolean(self):
        _run('(do (assert-true (boolean? true)) (assert-true (boolean? false)) (assert-false (boolean? nil)) (assert-false (boolean? 0)))')

    def test_not(self):
        _run('(do (assert-true (not false)) (assert-true (not nil)) (assert-false (not true)) (assert-false (not 1)) (assert-false (not "x")))')


class TestSpecSpecialForms:
    """test.sx suite: special-forms"""

    def test_if(self):
        _run('(do (assert-equal "yes" (if true "yes" "no")) (assert-equal "no" (if false "yes" "no")) (assert-equal "no" (if nil "yes" "no")) (assert-nil (if false "yes")))')

    def test_when(self):
        _run('(do (assert-equal "yes" (when true "yes")) (assert-nil (when false "yes")))')

    def test_cond(self):
        _run('(do (assert-equal "a" (cond true "a" :else "b")) (assert-equal "b" (cond false "a" :else "b")) (assert-equal "c" (cond false "a" false "b" :else "c")))')

    def test_and(self):
        _run('(do (assert-true (and true true)) (assert-false (and true false)) (assert-false (and false true)) (assert-equal 3 (and 1 2 3)))')

    def test_or(self):
        _run('(do (assert-equal 1 (or 1 2)) (assert-equal 2 (or false 2)) (assert-equal "fallback" (or nil false "fallback")) (assert-false (or false false)))')

    def test_let(self):
        _run('(do (assert-equal 3 (let ((x 1) (y 2)) (+ x y))) (assert-equal "hello world" (let ((a "hello") (b " world")) (str a b))))')

    def test_let_clojure_style(self):
        _run('(assert-equal 3 (let (x 1 y 2) (+ x y)))')

    def test_do_begin(self):
        _run('(do (assert-equal 3 (do 1 2 3)) (assert-equal "last" (begin "first" "middle" "last")))')

    def test_define(self):
        _run('(do (define x 42) (assert-equal 42 x))')

    def test_set(self):
        _run('(do (define x 1) (set! x 2) (assert-equal 2 x))')


class TestSpecLambdas:
    """test.sx suite: lambdas"""

    def test_basic_lambda(self):
        _run('(let ((add (fn (a b) (+ a b)))) (assert-equal 3 (add 1 2)))')

    def test_closure_captures_env(self):
        _run('(let ((x 10)) (let ((add-x (fn (y) (+ x y)))) (assert-equal 15 (add-x 5))))')

    def test_lambda_as_argument(self):
        _run('(assert-equal (list 2 4 6) (map (fn (x) (* x 2)) (list 1 2 3)))')

    def test_recursive_lambda_via_define(self):
        _run('(do (define factorial (fn (n) (if (<= n 1) 1 (* n (factorial (- n 1)))))) (assert-equal 120 (factorial 5)))')

    def test_higher_order_returns_lambda(self):
        _run('(let ((make-adder (fn (n) (fn (x) (+ n x))))) (let ((add5 (make-adder 5))) (assert-equal 8 (add5 3))))')


class TestSpecHigherOrder:
    """test.sx suite: higher-order"""

    def test_map(self):
        _run('(do (assert-equal (list 2 4 6) (map (fn (x) (* x 2)) (list 1 2 3))) (assert-equal (list) (map (fn (x) x) (list))))')

    def test_filter(self):
        _run('(do (assert-equal (list 2 4) (filter (fn (x) (= (mod x 2) 0)) (list 1 2 3 4))) (assert-equal (list) (filter (fn (x) false) (list 1 2 3))))')

    def test_reduce(self):
        _run('(do (assert-equal 10 (reduce (fn (acc x) (+ acc x)) 0 (list 1 2 3 4))) (assert-equal 0 (reduce (fn (acc x) (+ acc x)) 0 (list))))')

    def test_some(self):
        _run('(do (assert-true (some (fn (x) (> x 3)) (list 1 2 3 4 5))) (assert-false (some (fn (x) (> x 10)) (list 1 2 3))))')

    def test_every(self):
        _run('(do (assert-true (every? (fn (x) (> x 0)) (list 1 2 3))) (assert-false (every? (fn (x) (> x 2)) (list 1 2 3))))')

    def test_map_indexed(self):
        _run('(assert-equal (list "0:a" "1:b" "2:c") (map-indexed (fn (i x) (str i ":" x)) (list "a" "b" "c")))')


class TestSpecComponents:
    """test.sx suite: components"""

    def test_defcomp_creates_component(self):
        _run('(do (defcomp ~test-comp (&key title) (div title)) (assert-true (not (nil? ~test-comp))))')

    def test_component_renders_with_keyword_args(self):
        _run('(do (defcomp ~greeting (&key name) (span (str "Hello, " name "!"))) (assert-true (not (nil? ~greeting))))')

    def test_component_with_children(self):
        _run('(do (defcomp ~box (&key &rest children) (div :class "box" children)) (assert-true (not (nil? ~box))))')

    def test_component_with_default_via_or(self):
        _run('(do (defcomp ~label (&key text) (span (or text "default"))) (assert-true (not (nil? ~label))))')


class TestSpecMacros:
    """test.sx suite: macros"""

    def test_defmacro_creates_macro(self):
        _run('(do (defmacro unless (cond &rest body) (quasiquote (if (not (unquote cond)) (do (splice-unquote body))))) (assert-equal "yes" (unless false "yes")) (assert-nil (unless true "no")))')

    def test_quasiquote_and_unquote(self):
        _run('(let ((x 42)) (assert-equal (list 1 42 3) (quasiquote (1 (unquote x) 3))))')

    def test_splice_unquote(self):
        _run('(let ((xs (list 2 3 4))) (assert-equal (list 1 2 3 4 5) (quasiquote (1 (splice-unquote xs) 5))))')


class TestSpecThreading:
    """test.sx suite: threading"""

    def test_thread_first(self):
        _run('(do (assert-equal 8 (-> 5 (+ 1) (+ 2))) (assert-equal "HELLO" (-> "hello" upcase)) (assert-equal "HELLO WORLD" (-> "hello" (str " world") upcase)))')


class TestSpecTruthiness:
    """test.sx suite: truthiness"""

    def test_truthy_values(self):
        _run('(do (assert-true (if 1 true false)) (assert-true (if "x" true false)) (assert-true (if (list 1) true false)) (assert-true (if true true false)))')

    def test_falsy_values(self):
        _run('(do (assert-false (if false true false)) (assert-false (if nil true false)))')


class TestSpecEdgeCases:
    """test.sx suite: edge-cases"""

    def test_nested_let_scoping(self):
        _run('(let ((x 1)) (let ((x 2)) (assert-equal 2 x)))')

    def test_recursive_map(self):
        _run('(assert-equal (list (list 2 4) (list 6 8)) (map (fn (sub) (map (fn (x) (* x 2)) sub)) (list (list 1 2) (list 3 4))))')

    def test_keyword_as_value(self):
        _run('(do (assert-equal "class" :class) (assert-equal "id" :id))')

    def test_dict_with_evaluated_values(self):
        _run('(let ((x 42)) (assert-equal 42 (get {:val x} "val")))')

    def test_nil_propagation(self):
        _run('(do (assert-nil (get {:a 1} "missing")) (assert-equal "default" (or (get {:a 1} "missing") "default")))')

    def test_empty_operations(self):
        _run('(do (assert-equal (list) (map (fn (x) x) (list))) (assert-equal (list) (filter (fn (x) true) (list))) (assert-equal 0 (reduce (fn (acc x) (+ acc x)) 0 (list))) (assert-equal 0 (len (list))) (assert-equal "" (str)))')

