Private
Public Access
1011 lines
60 KiB
Bash
Executable File
1011 lines
60 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
|
|
site_directory="${BEARER_TEST_SITE_DIRECTORY:-site}"
|
|
bin_directory="${BEARER_TEST_BIN_DIRECTORY:-/tmp/bearer/work}"
|
|
if [[ -r /etc/bearer/settings.cfg ]]; then
|
|
configured_site=$(awk -F= '/^[[:space:]]*HTTP_DOCUMENT_ROOT[[:space:]]*=/ {sub(/^[^=]*=/, ""); print; exit}' /etc/bearer/settings.cfg)
|
|
configured_bin=$(awk -F= '/^[[:space:]]*BIN_DIRECTORY[[:space:]]*=/ {sub(/^[^=]*=/, ""); print; exit}' /etc/bearer/settings.cfg)
|
|
[[ -n "${BEARER_TEST_SITE_DIRECTORY:-}" ]] || site_directory="${configured_site:-$site_directory}"
|
|
[[ -n "${BEARER_TEST_BIN_DIRECTORY:-}" ]] || bin_directory="${configured_bin:-$bin_directory}"
|
|
fi
|
|
site_directory=$(realpath "$site_directory")
|
|
|
|
expect_equal() {
|
|
local case_name=$1 expected=$2 actual=$3
|
|
if [[ "$actual" != "$expected" ]]; then
|
|
printf '%s mismatch\nexpected: %q\nactual: %q\n' "$case_name" "$expected" "$actual" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
native_compiler_id=$(sha256sum src/capy/*.cpp src/capy/*.h src/lib/compiler.cpp src/lib/markup-context.h | sha256sum | awk '{print $1}')
|
|
grep -aFq "$native_compiler_id" bin/bearer_fastcgi.linux.bin || {
|
|
echo "Bearer binary does not contain the current native Capy compiler identity" >&2
|
|
exit 1
|
|
}
|
|
stdlib_digest=$(sha256sum src/capy/stdlib.capy | awk '{print $1}')
|
|
grep -aFq "$stdlib_digest" bin/bearer_fastcgi.linux.bin || {
|
|
echo "Bearer binary does not contain the current embedded Capy standard library" >&2
|
|
exit 1
|
|
}
|
|
|
|
output=$(scripts/bearer-cli /tests/capy-phase1.capy)
|
|
[[ "$output" == "capy-direct-ok" ]] || { echo "Capy CLI output mismatch: $output" >&2; exit 1; }
|
|
expect_equal "math fixture" "capy-math-ok" "$(scripts/bearer-cli /tests/capy-math.capy)"
|
|
expect_equal "bits fixture" "capy-bits-ok" "$(scripts/bearer-cli /tests/capy-bits.capy)"
|
|
expect_equal "byte conversion fixture" "3|0|65|255|0|5" "$(scripts/bearer-cli /tests/capy-byte-conversion.capy)"
|
|
expect_equal "scalar format fixture" "capy-format-ok" "$(scripts/bearer-cli /tests/capy-format.capy)"
|
|
expect_equal "in-Wasm string fixture" "capy-string-hostpath-ok" "$(scripts/bearer-cli /tests/capy-string-hostpath.capy)"
|
|
expect_equal "identity string conversion preserves the source" "alivealive|0" "$(scripts/bearer-cli /tests/capy-string-identity.capy)"
|
|
expect_equal "flush_output ordering and reset" "first|true|second" "$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-flush-output.capy)"
|
|
|
|
language_output=$(scripts/bearer-cli /tests/capy-language.capy)
|
|
[[ "$language_output" == "sum=30;012;ok;01" ]] || {
|
|
echo "Capy functions/locals/control-flow output mismatch: $language_output" >&2
|
|
exit 1
|
|
}
|
|
operator_output=$(scripts/bearer-cli /tests/capy-operators.capy)
|
|
[[ "$operator_output" == "false|true|Xtrue|7|-5|true|false0|true0" ]] || {
|
|
echo "Capy logical/unary/inferred-declaration output mismatch: $operator_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "newline-independent expression parsing" "7|3|9" "$(scripts/bearer-cli /tests/capy-whitespace.capy)"
|
|
expect_equal "condition constructors and any bool extraction" "UuSsDdBbTTTTtttWC|true|false" "$(scripts/bearer-cli /tests/capy-condition-coercion.capy)"
|
|
(
|
|
condition_type_dir=$(mktemp -d "$site_directory/tests/capy-condition-type.XXXXXX")
|
|
trap 'rm -rf -- "$condition_type_dir"' EXIT
|
|
condition_type_name=${condition_type_dir##*/}
|
|
for case in \
|
|
'literal|function CLI(request : any) { if "true" {} }' \
|
|
'component|function CLI(request : any) { var name := "/tests/capy-request-context"; if component_resolve(name) {} }'; do
|
|
name=${case%%|*}; source=${case#*|}
|
|
printf '%s\n' "$source" >"$condition_type_dir/$name.capy"
|
|
set +e
|
|
condition_type_output=$(scripts/bearer-cli "/tests/$condition_type_name/$name.capy" 2>&1)
|
|
condition_type_status=$?
|
|
set -e
|
|
[[ $condition_type_status -ne 0 && "$condition_type_output" == *"$name.capy:1:"* ]] || {
|
|
echo "Capy $name condition type check did not fail: $condition_type_output" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
)
|
|
set +e
|
|
component_render_output=$(scripts/bearer-cli /tests/capy-component-render-result.capy 2>&1)
|
|
component_render_status=$?
|
|
set -e
|
|
[[ $component_render_status -ne 0 ]]
|
|
[[ "$component_render_output" == *"500"* ]]
|
|
[[ "$component_render_output" == *"rendered|true|false"* ]]
|
|
[[ "$component_render_output" != *'<div class="banner">'* ]]
|
|
consistency_expected="1|-2|3|4.5|x|10.5|3|4|left|right|early|late|0"
|
|
expect_equal "type aliases, value blocks, conditional values, and wide aggregates" \
|
|
"$consistency_expected" "$(scripts/bearer-cli /tests/capy-language-consistency.capy)"
|
|
(
|
|
value_trap_dir=$(mktemp -d "$site_directory/tests/capy-value-trap.XXXXXX")
|
|
trap 'rm -rf -- "$value_trap_dir"' EXIT
|
|
value_trap_name=${value_trap_dir##*/}
|
|
cat >"$value_trap_dir/then.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
var value := if true { -> [1][1] } else { -> [1][0] }
|
|
print(value)
|
|
}
|
|
EOF
|
|
cat >"$value_trap_dir/else.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
var value := if false { -> [1][0] } else { -> [1][1] }
|
|
print(value)
|
|
}
|
|
EOF
|
|
cat >"$value_trap_dir/block.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
var value := { var values := [1]; -> values[1] }
|
|
print(value)
|
|
}
|
|
EOF
|
|
for case in then:2:34 else:2:54 block:2:48; do
|
|
name=${case%%:*}; location=${case#*:}
|
|
set +e
|
|
value_trap_output=$(scripts/bearer-cli "/tests/$value_trap_name/$name.capy" 2>&1)
|
|
value_trap_status=$?
|
|
set -e
|
|
[[ $value_trap_status -ne 0 && "$value_trap_output" == *"$name.capy:$location"* ]] || {
|
|
echo "Capy $name value trap/source mapping mismatch: $value_trap_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "$name value trap recovery" "$consistency_expected" "$(scripts/bearer-cli /tests/capy-language-consistency.capy)"
|
|
done
|
|
)
|
|
coercion_expected="7|8|42|-9|10|4.25|true|false|12|false|1|2|103|9|4|0"
|
|
expect_equal "declared parameter coercion and constructors" "$coercion_expected" "$(scripts/bearer-cli /tests/capy-coercion.capy)"
|
|
(
|
|
dval_default_dir=$(mktemp -d "$site_directory/tests/capy-any-default.XXXXXX")
|
|
trap 'rm -rf -- "$dval_default_dir"' EXIT
|
|
cat >"$dval_default_dir/entry.capy" <<'EOF'
|
|
function fresh(opt : any = {nested: {items: [1, true, "x"]}}) any { -> opt }
|
|
function CLI(request : any) {
|
|
{
|
|
var first := fresh()
|
|
first.nested.items[0] = 2
|
|
first.nested.extra = "mutated"
|
|
var second := fresh()
|
|
print(s64(first.nested.items[0]), "|", s64(second.nested.items[0]), "|", bool(second.nested.items[1]), "|", second.nested.items[2], "|", second.nested.extra?)
|
|
}
|
|
print("|", arc_live())
|
|
}
|
|
EOF
|
|
expect_equal "DValue default identity" "2|1|true|x|false|0" "$(scripts/bearer-cli "/tests/${dval_default_dir##*/}/entry.capy")"
|
|
)
|
|
expect_equal "constructor options and precision" \
|
|
"7:7:7:7:0:7:7|7:7:7:7:0:7:7|7:7:7:7:0:7:7|7:7:7:7:0:7:7|7:7:7:7:true:7:7|true:false:true|true:true:true|11:12:13:-14:-15:-16:21:22:23:1.1000000000000001:1.2:1.3:absent:empty:true:true:false|5:5:255:255:42|1:-1:3:5:2:1:0:0|0:0:-9223372036854775808:255:0:127" \
|
|
"$(scripts/bearer-cli /tests/capy-constructors.capy)"
|
|
expect_equal "complete numeric grid" "127|-32768|255|65535|4294967295|1.5|7|8|4294967295|2.5|0" \
|
|
"$(scripts/bearer-cli /tests/capy-numeric-grid.capy)"
|
|
(
|
|
constructor_reject_dir=$(mktemp -d "$site_directory/tests/capy-constructor-reject.XXXXXX")
|
|
trap 'rm -rf -- "$constructor_reject_dir"' EXIT
|
|
printf '%s\n' 'function CLI(request : any) { print(u64("10", {base: 1.5})) }' >"$constructor_reject_dir/entry.capy"
|
|
set +e
|
|
constructor_base_output=$(scripts/bearer-cli "/tests/${constructor_reject_dir##*/}/entry.capy" 2>&1)
|
|
constructor_base_status=$?
|
|
set -e
|
|
[[ $constructor_base_status -ne 0 && "$constructor_base_output" == *"u64: base must be a whole number from 2 to 36"* ]] || {
|
|
echo "Capy did not reject a fractional constructor base: $constructor_base_output" >&2
|
|
exit 1
|
|
}
|
|
for source in \
|
|
'function CLI(request : any) { print(u64("10", {unknown: 1})) }' \
|
|
'function CLI(request : any) { print(f64("1", {base: 10})) }' \
|
|
'function CLI(request : any) { print(string("1", {base: 10})) }' \
|
|
'function CLI(request : any) { if "true" {} }'; do
|
|
printf '%s\n' "$source" >"$constructor_reject_dir/entry.capy"
|
|
if scripts/bearer-cli "/tests/${constructor_reject_dir##*/}/entry.capy" >/dev/null 2>&1; then
|
|
echo "Capy accepted an invalid constructor option or string condition" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
)
|
|
vector_expected="7|token|8|A|Ab|inside3|insideb|x|local|insidebz|0|8|2|1.5|c|12|9|p|a2true|b3|4c|4|4c|imir|function-print6|0"
|
|
expect_equal "constructors, shared vector arrays, variadics, splats, function values, and ARC" \
|
|
"$vector_expected" "$(scripts/bearer-cli /tests/capy-vector-variadic.capy)"
|
|
mutable_aggregate_expected="98335618|0|thirdfirstsecond1|aBaBctemporaryaB|2|12333|10213245789m0q|12967by|1291233abz0|89000000001parameter6new412|0"
|
|
expect_equal "stable mutable arrays and struct fields" \
|
|
"$mutable_aggregate_expected" "$(scripts/bearer-cli /tests/capy-mutable-array-struct.capy)"
|
|
expect_equal "managed parameter rebinding, nibble, and aggregate mutation" \
|
|
"go!|go|api:api/length|cc/dd?:cc/dd:zz|0|78|425|9|8" "$(scripts/bearer-cli /tests/capy-managed-parameter-rebind.capy)"
|
|
(
|
|
cycle_dir=$(mktemp -d "$site_directory/tests/capy-cycle.XXXXXX")
|
|
trap 'rm -rf -- "$cycle_dir"' EXIT
|
|
cat >"$cycle_dir/entry.capy" <<'EOF'
|
|
struct Cycle { children : [Cycle] }
|
|
function empty_cycles() [Cycle] { var values : [Cycle] = []; -> values }
|
|
function CLI(request : any) { var cycle := Cycle(empty_cycles()); cycle.children.push(cycle); print(arc_live() > 0) }
|
|
EOF
|
|
expect_equal "characterized strong aggregate cycle" "true" "$(scripts/bearer-cli "/tests/${cycle_dir##*/}/entry.capy")"
|
|
expect_equal "clean request after strong aggregate cycle" "$mutable_aggregate_expected" "$(scripts/bearer-cli /tests/capy-mutable-array-struct.capy)"
|
|
)
|
|
(
|
|
vector_trap_dir=$(mktemp -d "$site_directory/tests/capy-vector-traps.XXXXXX")
|
|
trap 'rm -rf -- "$vector_trap_dir"' EXIT
|
|
for case in \
|
|
'pop|function CLI(request : any) { var values : [s32] = []; values.pop() }' \
|
|
'insert|function CLI(request : any) { var values := [1]; values.insert(2, 2) }' \
|
|
'remove|function CLI(request : any) { var values := [1]; values.remove(-1) }' \
|
|
'reserve|function CLI(request : any) { var values := [1]; values.reserve(-1) }' \
|
|
'resize|function CLI(request : any) { var values := [1]; values.resize(-1, 0) }'; do
|
|
name=${case%%|*}; source=${case#*|}
|
|
printf '%s\n' "$source" >"$vector_trap_dir/$name.capy"
|
|
set +e
|
|
output=$(scripts/bearer-cli "/tests/${vector_trap_dir##*/}/$name.capy" 2>&1)
|
|
status=$?
|
|
set -e
|
|
[[ $status -ne 0 && "$output" == *"$name.capy:1:"* && "$output" != *"capy://stdlib.capy"* ]]
|
|
expect_equal "vector trap recovery after $name" "$vector_expected" "$(scripts/bearer-cli /tests/capy-vector-variadic.capy)"
|
|
done
|
|
)
|
|
(
|
|
coercion_trap_dir=$(mktemp -d "$site_directory/tests/capy-coercion-trap.XXXXXX")
|
|
trap 'rm -rf -- "$coercion_trap_dir"' EXIT
|
|
coercion_trap_name=${coercion_trap_dir##*/}
|
|
cat >"$coercion_trap_dir/entry.capy" <<'EOF'
|
|
function narrow(value : as s64) s64 { -> value }
|
|
function CLI(request : any) { print(narrow(1e300)) }
|
|
EOF
|
|
expect_equal "parameter conversion f64 formatting" "0" "$(scripts/bearer-cli "/tests/$coercion_trap_name/entry.capy")"
|
|
expect_equal "parameter conversion recovery" "$coercion_expected" "$(scripts/bearer-cli /tests/capy-coercion.capy)"
|
|
)
|
|
|
|
variable_expression_output=$(scripts/bearer-cli /tests/capy-variable-expressions.capy)
|
|
[[ "$variable_expression_output" == "var;true;typed;true;inferred;true;assigned;true;once;once-value;true;false;visible;equal;passed;call;and;or;2;2;0;2;0;returned;implicit;0" ]] || {
|
|
echo "Capy value-producing declaration output/ARC mismatch: $variable_expression_output" >&2
|
|
exit 1
|
|
}
|
|
(
|
|
variable_trap_dir=$(mktemp -d "$site_directory/tests/capy-variable-trap.XXXXXX")
|
|
trap 'rm -rf -- "$variable_trap_dir"' EXIT
|
|
variable_trap_name=${variable_trap_dir##*/}
|
|
cat >"$variable_trap_dir/entry.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
var owned := clone("owned")
|
|
while var again := true { var value := [1][1] }
|
|
}
|
|
EOF
|
|
set +e
|
|
variable_trap_output=$(scripts/bearer-cli "/tests/$variable_trap_name/entry.capy" 2>&1)
|
|
variable_trap_status=$?
|
|
set -e
|
|
[[ $variable_trap_status -ne 0 && "$variable_trap_output" == *"entry.capy:3:47"* ]] || {
|
|
echo "Capy declaration initializer trap/source mapping mismatch: $variable_trap_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "declaration initializer trap recovery" "$variable_expression_output" "$(scripts/bearer-cli /tests/capy-variable-expressions.capy)"
|
|
)
|
|
backtrace_output=$(scripts/bearer-cli /tests/capy-backtrace.capy)
|
|
expected_backtrace=$'#0 __lambda_0 at tests/capy-backtrace.capy:7:17\n#1 inner at tests/capy-backtrace.capy:5:1\n#2 outer at tests/capy-backtrace.capy:1:1\n#3 CLI at tests/capy-backtrace.capy:32:1\n==\n#0 inner at tests/capy-backtrace.capy:5:1\n#1 outer at tests/capy-backtrace.capy:1:1\n--\n|0\n++\n#0 explicit_return_trace at tests/capy-backtrace.capy:20:1\n#1 CLI at tests/capy-backtrace.capy:32:1\n~~\n#0 CLI at tests/capy-backtrace.capy:32:1\n@@\n|#0 bounds at tests/capy-backtrace.capy:24:1\n#1 CLI at tests/capy-backtrace.capy:32:1||#0 bounds at tests/capy-backtrace.capy:24:1\n#1 CLI at tests/capy-backtrace.capy:32:1'
|
|
expect_equal "Capy source-mapped guest backtrace" "$expected_backtrace" "$backtrace_output"
|
|
(
|
|
backtrace_test_dir=$(mktemp -d "$site_directory/tests/capy-backtrace-test.XXXXXX")
|
|
trap 'rm -rf -- "$backtrace_test_dir"' EXIT
|
|
backtrace_test_name=${backtrace_test_dir##*/}
|
|
{
|
|
printf 'function tail() string { -> backtrace_get_frames(2147483647, 0) }\n'
|
|
for ((frame = 0; frame < 300; ++frame)); do
|
|
if ((frame == 299)); then
|
|
printf 'function ring_%d() string { -> tail() }\n' "$frame"
|
|
else
|
|
printf 'function ring_%d() string { -> ring_%d() }\n' "$frame" "$((frame + 1))"
|
|
fi
|
|
done
|
|
printf 'function CLI(request : any) { print(ring_0()) }\n'
|
|
} > "$backtrace_test_dir/ring.capy"
|
|
ring_output=$(scripts/bearer-cli "/tests/$backtrace_test_name/ring.capy")
|
|
mapfile -t ring_frames <<< "$ring_output"
|
|
[[ ${#ring_frames[@]} -eq 256 && ${#ring_output} -gt 4096 ]] || {
|
|
echo "Capy backtrace ring/two-pass output bounds mismatch" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "Capy backtrace ring newest frame" "#0 tail at tests/$backtrace_test_name/ring.capy:1:1" "${ring_frames[0]}"
|
|
expect_equal "Capy backtrace ring oldest retained frame" "#255 ring_45 at tests/$backtrace_test_name/ring.capy:47:1" "${ring_frames[255]}"
|
|
)
|
|
dval_return_output=$(scripts/bearer-cli /tests/capy-any-return.capy)
|
|
[[ "$dval_return_output" == "3|0|42|1|0|000|0|9|1|0" ]] || {
|
|
echo "Capy any-loop early-return ARC mismatch: $dval_return_output" >&2
|
|
exit 1
|
|
}
|
|
wide_output=$(scripts/bearer-cli /tests/capy-wide-scalars.capy)
|
|
[[ "$wide_output" == "8|8|9|9|18446744073709551615|-9223372036854775808|-1|-3|-1|true|true|3|2|true|125|-1.5|5|3.5|0|9|0|true|truetrue|inf|falsetrue|-0|0" ]] || {
|
|
echo "Capy wide scalar operations mismatch: $wide_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "wide scalar invalid string fallback" "0" "$(scripts/bearer-cli /tests/capy-wide-conversion-trap.capy)"
|
|
expect_equal "wide scalar recovery" "$wide_output" "$(scripts/bearer-cli /tests/capy-wide-scalars.capy)"
|
|
rm -f /tmp/capy-files-phase-* /tmp/capy-files2-*
|
|
file_output=$(scripts/bearer-cli /tests/capy-files.capy)
|
|
[[ "$file_output" == "true|5|0|capy!|5|true|2|0|true|true" ]] || {
|
|
echo "Capy file handle/ARC mismatch: $file_output" >&2
|
|
exit 1
|
|
}
|
|
filesystem_output=$(scripts/bearer-cli /tests/capy-files2.capy)
|
|
[[ "$filesystem_output" == "a.txt|true|truetruetruetruetruetruetruetrue|abc|ab|abc|truetrue3|true|a.txt,b.txt,link.txt|truefalsetruetruetruetruetrue|true|true|true|true" ]] || {
|
|
echo "Capy filesystem stdlib/policy mismatch: $filesystem_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "filesystem recovery" "$filesystem_output" "$(scripts/bearer-cli /tests/capy-files2.capy)"
|
|
expect_equal "file/archive/split_kv adapters" "3|heXYZ|Ada:admin|na\\=me=Ada\\=Grace\\, Lovelace,role=admin|Ada=Grace, Lovelace:admin|archive data|true2truetruetrue|11|0|true" "$(scripts/bearer-cli /tests/capy-final-parity.capy)"
|
|
set +e
|
|
archive_trap_output=$(scripts/bearer-cli /tests/capy-archive-trap.capy 2>&1)
|
|
archive_trap_status=$?
|
|
set -e
|
|
[[ $archive_trap_status -ne 0 && "$archive_trap_output" == *"capy-archive-trap.capy:3:8"* && "$archive_trap_output" != *"capy://stdlib.capy"* ]]
|
|
expect_equal "archive recovery" "3|heXYZ|Ada:admin|na\\=me=Ada\\=Grace\\, Lovelace,role=admin|Ada=Grace, Lovelace:admin|archive data|true2truetruetrue|11|0|true" "$(scripts/bearer-cli /tests/capy-final-parity.capy)"
|
|
expect_equal "live MySQL stdlib adapters" "truetrue|'a\\'b'|7|true|0:0|truetruetrue|truetrue|truetruetrue|truetrue|truetruetrue7|truetruetrue|0" "$(scripts/bearer-cli /tests/capy-mysql.capy)"
|
|
expect_equal "memcache key normalization and complete response reads" "down|collision|large-ok|0" "$(scripts/bearer-cli /tests/capy-memcache.capy)"
|
|
expect_equal "shell_exec sync and on_done callback through a task" "true|sync:3|true|done 0 printf done|invalid_target|true|0" "$(scripts/bearer-cli /tests/capy-shell-callback.capy)"
|
|
for shell_exec_case in \
|
|
"capy-shell-exec-flags-trap|shell_exec: unknown key 'unknown'. Valid keys: stdin, env, timeout_ms, on_done" \
|
|
"capy-shell-exec-on-done-type-trap|shell_exec: on_done must be a string" \
|
|
"capy-shell-exec-timeout-type-trap|shell_exec: timeout_ms must be a positive number" \
|
|
"capy-shell-exec-timeout-value-trap|shell_exec: timeout_ms must be a positive number" \
|
|
"capy-shell-exec-stdin-type-trap|shell_exec: stdin must be a string" \
|
|
"capy-shell-exec-env-list-trap|shell_exec: env must be a map with string values" \
|
|
"capy-shell-exec-env-value-trap|shell_exec: env must be a map with string values"; do
|
|
shell_exec_fixture=${shell_exec_case%%|*}
|
|
shell_exec_message=${shell_exec_case#*|}
|
|
set +e
|
|
shell_exec_flags_output=$(scripts/bearer-cli "/tests/$shell_exec_fixture.capy" 2>&1)
|
|
shell_exec_flags_status=$?
|
|
set -e
|
|
[[ $shell_exec_flags_status -ne 0 && "$shell_exec_flags_output" == *"$shell_exec_message"* ]] || {
|
|
echo "Capy $shell_exec_fixture diagnostic mismatch: $shell_exec_flags_output" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
expect_equal "named CLI entry point" "echo:hi" "$(scripts/bearer-cli --get /tests/capy-cli-named.capy:ECHO message=hi)"
|
|
expect_equal "default CLI entry point beside a named one" "default" "$(scripts/bearer-cli /tests/capy-cli-named.capy)"
|
|
expect_equal "task_execute runs detached, bounded by its timeout" "true|Ada|true|false|invalid_target" "$(scripts/bearer-cli /tests/capy-task-execute.capy)"
|
|
expect_equal "service missing target and complete status" "false|false|false|missing|6|0|0|0|0|false" \
|
|
"$(timeout 30 scripts/bearer-cli --get /tests/capy-services-api.capy mode=basic)"
|
|
for service_case in every:service_every spawn:port_spawn connect:port_connect listen:port_listen write:port_write close:port_close; do
|
|
service_mode=${service_case%%:*}
|
|
service_function=${service_case#*:}
|
|
set +e
|
|
service_output=$(timeout 30 scripts/bearer-cli --get /tests/capy-services-api.capy "mode=$service_mode" 2>&1)
|
|
service_status=$?
|
|
set -e
|
|
[[ $service_status -ne 0 && "$service_output" == *"$service_function: requires a service worker"* ]] || {
|
|
echo "Capy $service_function context diagnostic mismatch: $service_output" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
expect_equal "unit-scope variables persist across handler calls in one workspace" "2|ab|2|2|c|100" "$(scripts/bearer-cli /tests/capy-unit-vars.capy)"
|
|
expect_equal "unit-scope variables start fresh per request" "2|ab|2|2|c|100" "$(scripts/bearer-cli /tests/capy-unit-vars.capy)"
|
|
BEARER_TEST_SITE_DIRECTORY="$site_directory" timeout 20 bash scripts/test_capy_services.sh
|
|
expect_equal "unit administration" "true|true|true|2|0" "$(scripts/bearer-cli /tests/capy-unit-admin.capy)"
|
|
expect_equal "DValue array merge" "rightyesnewright|abcd|valueitem|right|value|z|oddkeep|map|unicode|9|0" "$(scripts/bearer-cli /tests/capy-any-merge.capy)"
|
|
expect_equal "DValue value API" 'truefalseAda42tail|trueone|Bearerfalse|truearray"A"|ftruey|truefalseAdanameengineer|23truefalse|-4242-918446744073709551615|0' "$(scripts/bearer-cli /tests/capy-any-api.capy)"
|
|
set +e
|
|
expect_equal "DValue value API recovery" 'truefalseAda42tail|trueone|Bearerfalse|truearray"A"|ftruey|truefalseAdanameengineer|23truefalse|-4242-918446744073709551615|0' "$(scripts/bearer-cli /tests/capy-any-api.capy)"
|
|
expect_equal "shared mutable DValue identity, live loops, range bindings, codec copies, and ARC" \
|
|
'SR|999secondyesyesyesyesvalue10|newnewtruetruetrue|1falsefalsetrueitemtruearraynewnewvalue1|true|102132|1021|0199|7a|3|sourceresult|0' "$(scripts/bearer-cli /tests/capy-any-identity.capy)"
|
|
expect_equal "nested closure string DValue ARC" "BETA|ALPHA|0" "$(scripts/bearer-cli /tests/capy-map-arc.capy)"
|
|
sqlite_output=$(scripts/bearer-cli /tests/capy-sqlite.capy)
|
|
expect_equal "SQLite lifecycle/query" "11|1Ada|true|1|0" "$sqlite_output"
|
|
expect_equal "SQLite failed-connect diagnostic" "true|0" "$(scripts/bearer-cli /tests/capy-sqlite-failed.capy)"
|
|
for info_case in \
|
|
"capy-mysql-info-trap|mysql_info: unknown key 'missing'. Valid keys: connection, error, insert_id, affected_rows" \
|
|
"capy-sqlite-info-trap|sqlite_info: unknown key 'missing'. Valid keys: error, insert_id, affected_rows" \
|
|
"capy-posix-info-trap|posix_info: unknown key 'missing'. Valid keys: signal_name"; do
|
|
info_fixture=${info_case%%|*}
|
|
info_message=${info_case#*|}
|
|
set +e
|
|
info_trap_output=$(scripts/bearer-cli "/tests/$info_fixture.capy" 2>&1)
|
|
info_trap_status=$?
|
|
set -e
|
|
[[ $info_trap_status -ne 0 && "$info_trap_output" == *"$info_message"* ]] || {
|
|
echo "Capy $info_fixture unknown-key diagnostic mismatch: $info_trap_output" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
for sqlite_case in \
|
|
"capy-sqlite-handle-trap|5:8" \
|
|
"capy-sqlite-params-trap|4:2" \
|
|
"capy-sqlite-zero-trap|3:8"; do
|
|
sqlite_fixture=${sqlite_case%%|*}
|
|
sqlite_location=${sqlite_case#*|}
|
|
set +e
|
|
sqlite_trap_output=$(scripts/bearer-cli "/tests/$sqlite_fixture.capy" 2>&1)
|
|
sqlite_trap_status=$?
|
|
set -e
|
|
[[ $sqlite_trap_status -ne 0 && "$sqlite_trap_output" == *"$sqlite_fixture.capy:$sqlite_location"* && "$sqlite_trap_output" != *"capy://stdlib.capy"* ]]
|
|
done
|
|
set +e
|
|
sized_host_trap_output=$(scripts/bearer-cli /tests/capy-sqlite-sized-host-trap.capy 2>&1)
|
|
sized_host_trap_status=$?
|
|
set -e
|
|
[[ $sized_host_trap_status -ne 0 && "$sized_host_trap_output" == *"capy-sqlite-sized-host-trap.capy:5:2"* && "$sized_host_trap_output" != *"capy://stdlib.capy" ]] || {
|
|
echo "Capy generic sized-host failure did not trap at the owned-input callsite: $sized_host_trap_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "generic sized-host failure recovery/ARC" "$sqlite_output" "$(scripts/bearer-cli /tests/capy-sqlite.capy)"
|
|
codec_output=$(scripts/bearer-cli /tests/capy-codecs.capy)
|
|
[[ "$codec_output" == "<Ada>|1.5true|Q2FweSE=|Capy!|0|a%20b%26|a b&|<&>"'|{}|3|0" ]] || {
|
|
echo "Capy codec/JSON/ARC mismatch: $codec_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "invalid f64 constructor fallback" "0" "$(scripts/bearer-cli /tests/capy-any-f64-trap.capy)"
|
|
expect_equal "codec recovery" "$codec_output" "$(scripts/bearer-cli /tests/capy-codecs.capy)"
|
|
expect_equal "custom and default sort" 'sort-ok' "$(scripts/bearer-cli /tests/capy-sort.capy)"
|
|
expect_equal "codec/text stdlib adapters" 'Ada|Ada|6|Ada|10|document|<p><strong>bold</strong></p>|"x"|{"name": "Ada"}|<&>"|8|0' "$(scripts/bearer-cli /tests/capy-codecs-text.capy)"
|
|
set +e
|
|
codec_text_trap_output=$(scripts/bearer-cli /tests/capy-codecs-text-trap.capy 2>&1)
|
|
codec_text_trap_status=$?
|
|
set -e
|
|
[[ $codec_text_trap_status -ne 0 && "$codec_text_trap_output" == *"capy-codecs-text-trap.capy:3:2"* ]] || {
|
|
echo "Capy malformed XML did not trap at the call site: $codec_text_trap_output" >&2
|
|
exit 1
|
|
}
|
|
crypto_output=$(scripts/bearer-cli /tests/capy-crypto-random.capy)
|
|
[[ "$crypto_output" =~ ^a9993e364706816aba3e25717850c26c9cd0d89d\|20\|32\|ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad\|32\|6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a\|414243\|truefalse\|16\|truefalse\|76\|9546680768582587775\|true\|7\|true\|true\|true\|[0-9]+\|0$ ]] || {
|
|
echo "Capy crypto/random adapter mismatch: $crypto_output" >&2
|
|
exit 1
|
|
}
|
|
regex_output=$(scripts/bearer-cli /tests/capy-regex.capy)
|
|
[[ "$regex_output" == "true|Capy|4|Capy|3TWO|false|<one> <TWO>|a::b|truetruetruefalse|false|2|:a:b:|0" ]] || {
|
|
echo "Capy regex/ARC mismatch: $regex_output" >&2
|
|
exit 1
|
|
}
|
|
for regex_case in \
|
|
"capy-regex-trap|could not compile pattern" \
|
|
"capy-regex-flag-trap|unknown regex flag 'q'" \
|
|
"capy-regex-replace-trap|substitution failed: unknown substring"; do
|
|
regex_trap=${regex_case%%|*}
|
|
regex_error=${regex_case#*|}
|
|
set +e
|
|
regex_trap_output=$(scripts/bearer-cli "/tests/$regex_trap.capy" 2>&1)
|
|
regex_trap_status=$?
|
|
set -e
|
|
[[ $regex_trap_status -ne 0 && "$regex_trap_output" == *"$regex_error"* && "$regex_trap_output" == *"$regex_trap.capy:3:8"* ]]
|
|
done
|
|
expect_equal "regex recovery" "$regex_output" "$(scripts/bearer-cli /tests/capy-regex.capy)"
|
|
if compgen -G '/tmp/capy-files-phase-*' >/dev/null || compgen -G '/tmp/capy-files2-*' >/dev/null; then
|
|
echo "Capy filesystem fixture left temporary files" >&2
|
|
rm -f /tmp/capy-files-phase-* /tmp/capy-files2-*
|
|
exit 1
|
|
fi
|
|
expect_equal "concat-only demand surface" "ab|0" "$(scripts/bearer-cli /tests/capy-string-concat-only.capy)"
|
|
expect_equal "zero-argument first" "[]|0" "$(scripts/bearer-cli /tests/capy-first-empty.capy)"
|
|
string_list_output=$(scripts/bearer-cli /tests/capy-string-lists.capy)
|
|
[[ "$string_list_output" == "a::b:|a,,b,|true|Capy|||Gr:ße||one|value|x-y|[]|[ keep ]|chosen|eplpick|5|0" ]] || {
|
|
echo "Capy split/join/ARC mismatch: $string_list_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "numeric DValue list join conversion" "1" "$(scripts/bearer-cli /tests/capy-string-list-trap.capy)"
|
|
set +e
|
|
string_list_trap_output=$(scripts/bearer-cli /tests/capy-string-list-scalar-trap.capy 2>&1)
|
|
string_list_trap_status=$?
|
|
set -e
|
|
[[ $string_list_trap_status -ne 0 && "$string_list_trap_output" == *"capy-string-list-scalar-trap.capy:3:8"* ]]
|
|
expect_equal "split/join recovery" "$string_list_output" "$(scripts/bearer-cli /tests/capy-string-lists.capy)"
|
|
expect_equal "core utility value adapters" "falsetruefalseBETA,ALPHA,BETA|alpha|beta,alpha|truetrue|alpha|0,1,2|bearer:BEARER|Caffile1:core|0:255:truetrue|1.5:-9:42|truetrue|a=one&b=last&flag=:last:example.test:docs:two|docs/index:true:|one,two,three:A,é:example.test|alpha,beta,beta:true:SIGSEGV:0:true:40|0" "$(scripts/bearer-cli /tests/capy-coreutil.capy)"
|
|
set +e
|
|
coreutil_trap_output=$(scripts/bearer-cli /tests/capy-coreutil-trap.capy 2>&1)
|
|
coreutil_trap_status=$?
|
|
set -e
|
|
[[ $coreutil_trap_status -ne 0 && "$coreutil_trap_output" == *"capy-coreutil-trap.capy:3:2"* ]] || {
|
|
echo "Capy negative usleep did not trap at call site: $coreutil_trap_output" >&2
|
|
exit 1
|
|
}
|
|
string_output=$(scripts/bearer-cli /tests/capy-strings.capy)
|
|
[[ "$string_output" == "Capy Bearer|11|truetrue|Bearer|Bearer|Capy:Bearer|5:5:-1|5:-1|Capy Runtime|capy bearer|CAPY BEARER|3|truefalsetrue|3|0" ]] || {
|
|
echo "Capy string operations/ARC mismatch: $string_output" >&2
|
|
exit 1
|
|
}
|
|
markup_output=$(scripts/bearer-cli /tests/capy-markup.capy)
|
|
[[ "$markup_output" == "once;<p>static</p><main><side><&>"'<&>\"'<strong><&>"'</strong><i>&lt;&amp;&gt;&quot;&#39;</i><aside>-2147483648:0:2147483647:true:false</aside><wide>-9223372036854775808:18446744073709551615:-1.5</wide></main>|-2147483648|0" ]] || {
|
|
echo "Capy markup output mismatch: $markup_output" >&2
|
|
exit 1
|
|
}
|
|
context_markup=$(scripts/bearer-cli /tests/capy-markup-context.capy)
|
|
[[ "$context_markup" == once\;* && "${context_markup#once;}" != *'once;'* &&
|
|
"$context_markup" == *'<once><p title="</script><style>&"''* &&
|
|
"$context_markup" == *'\u003C\/script\u003E'* && "$context_markup" == *'\u000A\u2028\u2029'* &&
|
|
"$context_markup" == *'\3C /script\3E '* && "$context_markup" == *'\A \2028 \2029 '* &&
|
|
"$context_markup" == *'|0' ]] || {
|
|
echo "Contextual markup escaping mismatch: $context_markup" >&2
|
|
exit 1
|
|
}
|
|
loop_control_output=$(scripts/bearer-cli /tests/capy-loop-control.capy)
|
|
[[ "$loop_control_output" == "7|owned-return|0|13|0|023|0|ab|0|0002||2022|0" ]] || {
|
|
echo "Capy break/continue ARC output mismatch: $loop_control_output" >&2
|
|
exit 1
|
|
}
|
|
closure_output=$(scripts/bearer-cli /tests/capy-closures.capy)
|
|
[[ "$closure_output" == "captured:15|2|second:22|2|5|2|second:23|3|temporary:31|3|immediate:42|3|nested:7|3|0" ]] || {
|
|
echo "Capy closure output mismatch: $closure_output" >&2
|
|
exit 1
|
|
}
|
|
callable_expected=$'3:12:3:12|{"list": [null, {"closure": null}], "map": {"closure": null, "named": null}}|list:\n - null\n -\n closure: null\nmap:\n closure: null\n named: null\n|<root><list><item/><item><closure/></item></list><map><closure/><named/></map></root>|truetruefalsefalsefalsefalse|2:2:1|true|true|true|true|true'
|
|
expect_equal "callable DValue projection, capture, and ARC" "$callable_expected" "$(scripts/bearer-cli /tests/capy-any-callable.capy)"
|
|
set +e
|
|
callable_signature_output=$(scripts/bearer-cli /tests/capy-any-callable-signature-trap.capy 2>&1)
|
|
callable_signature_status=$?
|
|
set -e
|
|
[[ $callable_signature_status -ne 0 && "$callable_signature_output" == *"capy-any-callable-signature-trap.capy:9:2"* ]] || {
|
|
echo "Capy callable DValue signature mismatch did not trap at the extraction" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "callable DValue trap recovery" "$callable_expected" "$(scripts/bearer-cli /tests/capy-any-callable.capy)"
|
|
expect_equal "Capy dynamic type and generic marker" "4|7|generic|9" "$(scripts/bearer-cli /tests/capy-generic-dynamic.capy)"
|
|
phase3_output=$(scripts/bearer-cli /tests/capy-phase3.capy)
|
|
[[ "$phase3_output" == "second|1|0|7|generic|5|fallback|2|name|9pair|2|pair|0|pair|0|pair|3|innerouter|4|temporary|0|nested|0|5-1pair|0|0|falsetrue0|0" ]] || {
|
|
echo "Capy generic specialization output mismatch: $phase3_output" >&2
|
|
exit 1
|
|
}
|
|
phase3_cache="$(scripts/unit_cache_directory "$bin_directory")$site_directory/tests/capy-phase3.capy"
|
|
wasm-validate "$phase3_cache.wasm"
|
|
expect_equal "Capy embedded struct reflection" "MMReflectionValue|s32|[s32]|7|string|ok|7|3|2|ready|9007199254740993|function(s32) s32|8|0|{}|0" "$(scripts/bearer-cli /tests/capy-reflection.capy)"
|
|
[[ "$(scripts/bearer-cli /tests/capy-cross.capy)" == "capy-render-ok|counted;roundtripother|1|3|0" ]] || {
|
|
echo "Capy Bearer unit dispatch failed" >&2
|
|
exit 1
|
|
}
|
|
[[ "$(scripts/bearer-cli /tests/capy-cross-caller.capy)" == "capy-component-ok|capy-named-ok" ]] || {
|
|
echo "Capy component dispatch failed" >&2
|
|
exit 1
|
|
}
|
|
request_component_output=$(scripts/bearer-cli /tests/capy-request-context-caller.capy)
|
|
[[ "$request_component_output" == "component-prop|false|0|handle|0" ]] || {
|
|
echo "Capy request props snapshot mismatch: $request_component_output" >&2
|
|
exit 1
|
|
}
|
|
component_props_output=$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-component-props.capy?name=FromCapy')
|
|
[[ "$component_props_output" == "truefalse|child|false|0|FromCapy|false|0|child|child|2" ]] || {
|
|
echo "Capy component props dispatch mismatch: $component_props_output" >&2
|
|
exit 1
|
|
}
|
|
component_trap_body=$(mktemp)
|
|
component_trap_status=$(curl -sS --max-time 30 -o "$component_trap_body" -w '%{http_code}' -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-component-props.capy?name=FromCapy&trap=1')
|
|
[[ "$component_trap_status" == "500" ]] && ! grep -q 'must-not-leak' "$component_trap_body"
|
|
rm -f "$component_trap_body"
|
|
[[ "$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-component-props.capy?name=Recovered')" == "truefalse|child|false|0|Recovered|false|0|child|child|2" ]]
|
|
(
|
|
component_cache_dir=$(mktemp -d "$site_directory/tests/capy-component-cache.XXXXXX")
|
|
chmod 755 "$component_cache_dir"
|
|
trap 'rm -rf -- "$component_cache_dir"' EXIT
|
|
component_cache_name=${component_cache_dir##*/}
|
|
cat >"$component_cache_dir/parent.capy" <<'EOF'
|
|
function RENDER(request : any) {
|
|
print(length(component_resolve("child")) > 0)
|
|
}
|
|
EOF
|
|
printf '%s\n' 'function COMPONENT(request : any) { print("child") }' >"$component_cache_dir/child.capy"
|
|
expect_equal "Capy warm component resolution" "true" "$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' "http://127.0.0.1/tests/$component_cache_name/parent.capy")"
|
|
rm "$component_cache_dir/child.capy"
|
|
expect_equal "Capy deleted component rejection" "false" "$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' "http://127.0.0.1/tests/$component_cache_name/parent.capy")"
|
|
printf '%s\n' 'function COMPONENT(request : any) { print("child") }' >"$component_cache_dir/child.capy"
|
|
expect_equal "Capy component resolution recovery" "true" "$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' "http://127.0.0.1/tests/$component_cache_name/parent.capy")"
|
|
)
|
|
(
|
|
settings=/etc/bearer/settings.cfg
|
|
settings_backup=$(mktemp)
|
|
cp --preserve=mode,ownership,timestamps "$settings" "$settings_backup"
|
|
compile_error_dir=$(mktemp -d "$site_directory/tests/capy-component-compile-error.XXXXXX")
|
|
chmod 755 "$compile_error_dir"
|
|
compile_error_name=${compile_error_dir##*/}
|
|
compile_error_artifact_dir="$(scripts/unit_cache_directory "$bin_directory")$compile_error_dir"
|
|
compile_error_body=$(mktemp)
|
|
wait_for_bearer() {
|
|
local code
|
|
for _ in $(seq 1 60); do
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 -H 'Host: bearer.openfu.com' http://127.0.0.1/info/ || true)
|
|
[[ "$code" == "200" ]] && return 0
|
|
sleep 1
|
|
done
|
|
return 1
|
|
}
|
|
restart_bearer() {
|
|
systemctl restart bearer.service
|
|
wait_for_bearer
|
|
}
|
|
cleanup_compile_error_test() {
|
|
cp --preserve=mode,ownership,timestamps "$settings_backup" "$settings"
|
|
rm -f "$settings_backup" "$compile_error_body"
|
|
rm -rf -- "$compile_error_dir" "$compile_error_artifact_dir"
|
|
restart_bearer
|
|
}
|
|
trap cleanup_compile_error_test EXIT
|
|
cat >"$compile_error_dir/parent.capy" <<'EOF'
|
|
function RENDER(request : any) {
|
|
print(component("child.capy"))
|
|
}
|
|
EOF
|
|
printf '%s\n' 'function COMPONENT(request : any) { print(not_a_constant) }' >"$compile_error_dir/child.capy"
|
|
child_path=$(realpath "$compile_error_dir/child.capy")
|
|
sed -i \
|
|
-e 's#^page_runtime_error=.*#page_runtime_error=site/errors/runtime-error.capy#' \
|
|
-e 's/^SHOW_DYNAMIC_COMPILE_ERRORS=.*/SHOW_DYNAMIC_COMPILE_ERRORS=1/' "$settings"
|
|
restart_bearer
|
|
compile_error_status=$(curl -sS --max-time 30 -o "$compile_error_body" -w '%{http_code}' -H 'Host: bearer.openfu.com' \
|
|
"http://127.0.0.1/tests/$compile_error_name/parent.capy")
|
|
[[ "$compile_error_status" == "500" && -s "$compile_error_body" ]]
|
|
grep -Fq "$child_path" "$compile_error_body"
|
|
grep -Fq "unknown local" "$compile_error_body"
|
|
|
|
sed -i 's/^SHOW_DYNAMIC_COMPILE_ERRORS=.*/SHOW_DYNAMIC_COMPILE_ERRORS=0/' "$settings"
|
|
restart_bearer
|
|
compile_error_status=$(curl -sS --max-time 30 -o "$compile_error_body" -w '%{http_code}' -H 'Host: bearer.openfu.com' \
|
|
"http://127.0.0.1/tests/$compile_error_name/parent.capy")
|
|
[[ "$compile_error_status" == "500" && -s "$compile_error_body" ]]
|
|
grep -Fq "$child_path" "$compile_error_body"
|
|
! grep -Fq "unknown local" "$compile_error_body"
|
|
)
|
|
request_headers=$(mktemp)
|
|
request_http_output=$(curl -fsS --max-time 30 -D "$request_headers" -H 'Host: bearer.openfu.com' -H 'X-Capy-Schema: yes' -d 'answer=42' 'http://127.0.0.1/tests/capy-request-context.capy?name=Ada')
|
|
[[ "$request_http_output" == "POST|Ada|42|answer=42|0" ]] || {
|
|
echo "Capy HTTP request snapshot mismatch: $request_http_output" >&2
|
|
exit 1
|
|
}
|
|
grep -Eq '^HTTP/1\.[01] 201 ' "$request_headers"
|
|
grep -Eqi '^X-Capy-Context: yes' "$request_headers"
|
|
grep -Eqi '^X-Capy-Clean: first X-Injected: bad' "$request_headers"
|
|
! grep -Eqi '^X-Injected:' "$request_headers"
|
|
rm -f "$request_headers"
|
|
upload_file=$(mktemp --suffix=-request-upload.txt)
|
|
printf 'data' >"$upload_file"
|
|
upload_output=$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' -F before=1 -F "upload=@$upload_file;filename=request-upload.txt" -F after=2 -F "second=@$upload_file;filename=second.txt" 'http://127.0.0.1/tests/capy-request-context.capy?upload=1')
|
|
rm -f "$upload_file"
|
|
[[ "$upload_output" == "request-upload.txt|true|true|upload|1|2|2" ]] || {
|
|
echo "Capy upload snapshot mismatch: $upload_output" >&2
|
|
exit 1
|
|
}
|
|
request_http_second=$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' -H 'X-Capy-Schema: yes' -d 'answer=7' 'http://127.0.0.1/tests/capy-request-context.capy?name=Grace')
|
|
[[ "$request_http_second" == "POST|Grace|7|answer=7|0" ]] || {
|
|
echo "Capy request isolation mismatch: $request_http_second" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "kv cache stores, counts, caches, and refuses oversized values" "fresh|4231|53|2048|truefalse|false|none|true" "$(scripts/bearer-cli /tests/capy-kv.capy)"
|
|
expect_equal "kv cache is shared across requests" "shared|4230|86|2048|truefalse|false|none|true" "$(scripts/bearer-cli /tests/capy-kv.capy)"
|
|
expect_equal "kv cache clears" "cleared" "$(scripts/bearer-cli --get /tests/capy-kv.capy mode=clear)"
|
|
expect_equal "clone copies arrays and structs deeply" "19|bz|1ada17|2grace28|13|0" "$(scripts/bearer-cli /tests/capy-clone.capy)"
|
|
expect_equal "an any is one object under every name" "Grace|loop@example.test|2|loop@example.test|Copy|copy@example.test|guest|0" "$(scripts/bearer-cli /tests/capy-any-shared.capy)"
|
|
expect_equal "current_request shares the handler value with exported functions" "Hello, Ada|true|Hello, Ada|0" "$(scripts/bearer-cli /tests/capy-current-request.capy)"
|
|
request_writeback_headers=$(mktemp)
|
|
request_writeback_output=$(curl -fsS --max-time 30 -D "$request_writeback_headers" -H 'Host: bearer.openfu.com' -d 'value=form' 'http://127.0.0.1/tests/capy-request-writeback.capy?value=query')
|
|
expect_equal "Capy shared request identity and write-back" "form|Ada|404|pending|1|component|inner|2" "$request_writeback_output"
|
|
grep -Eq '^HTTP/1\.[01] 203 ' "$request_writeback_headers"
|
|
grep -Eqi '^X-Capy-Phase5: inner' "$request_writeback_headers"
|
|
grep -Eqi '^X-Capy-Parent: pending' "$request_writeback_headers"
|
|
grep -Eiq '^Set-Cookie: capy-parent=pending(;|$)' "$request_writeback_headers"
|
|
grep -Eiq '^Set-Cookie: capy-phase5=cookie(;|$)' "$request_writeback_headers"
|
|
rm -f "$request_writeback_headers"
|
|
response_trap_body=$(mktemp)
|
|
response_trap_status=$(curl -sS --max-time 30 -o "$response_trap_body" -w '%{http_code}' -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-response-header-trap.capy)
|
|
rm -f "$response_trap_body"
|
|
[[ "$response_trap_status" == "500" ]] || {
|
|
echo "Capy malformed response header did not fail: HTTP $response_trap_status" >&2
|
|
exit 1
|
|
}
|
|
status_trap_body=$(mktemp)
|
|
status_trap_status=$(curl -sS --max-time 30 -o "$status_trap_body" -w '%{http_code}' -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-response-status-trap.capy)
|
|
rm -f "$status_trap_body"
|
|
[[ "$status_trap_status" == "500" ]] || {
|
|
echo "Capy invalid response status did not fail: HTTP $status_trap_status" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "typed response status" "202" "$(curl -sS --max-time 30 -o /dev/null -w '%{http_code}' -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-response-status-typed.capy)"
|
|
expect_equal "response status recovers after a native failure" "recovered" "$(scripts/bearer-cli /tests/capy-response-recover.capy)"
|
|
expect_equal "request/response stdlib CLI" "Ada|Ada|inner|outer+inner|index|workspace/projectstruetrue|truetruetrue|true|0" "$(scripts/bearer-cli /tests/capy-request-parity.capy name=Ada)"
|
|
request_parity_headers=$(mktemp)
|
|
request_parity_output=$(curl -fsS --max-time 30 -D "$request_parity_headers" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-request-parity.capy?workspace/projects&name=Grace&status=yes')
|
|
[[ "$request_parity_output" == "Grace|Grace|inner|outer+inner|workspace/projects|workspace/projectstruetrue|truetruetrue|true|0" ]]
|
|
grep -Eq '^HTTP/1\.[01] 202 Accepted' "$request_parity_headers"
|
|
rm -f "$request_parity_headers"
|
|
session_replacement_headers=$(mktemp)
|
|
[[ "$(curl -fsS --max-time 30 -D "$session_replacement_headers" -b 'capy-session=not-a-session' -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=read')" == "missing||1" ]]
|
|
grep -Eiq '^Set-Cookie: capy-session=[0-9a-f]{64}.*HttpOnly.*SameSite=Lax' "$session_replacement_headers"
|
|
rm -f "$session_replacement_headers"
|
|
session_jar=$(mktemp)
|
|
session_headers=$(mktemp)
|
|
[[ "$(curl -fsS --max-time 30 -D "$session_headers" -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=set&value=Ada')" == "set" ]]
|
|
grep -Eiq '^Set-Cookie: capy-session=.*HttpOnly.*SameSite=Lax' "$session_headers"
|
|
grep -Eiq '^Set-Cookie: capy-extra=yes.*HttpOnly.*SameSite=Lax' "$session_headers"
|
|
[[ "$(curl -fsS --max-time 30 -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=read')" == "Ada|yes|1" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=remove')" == "removed" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=read')" == "missing|yes|1" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=set&value=Grace')" == "set" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=destroy')" == "destroyed" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$session_jar" -b "$session_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-session.capy?action=read')" == "missing|yes|1" ]]
|
|
rm -f "$session_jar" "$session_headers"
|
|
redirect_headers=$(mktemp)
|
|
redirect_body=$(curl -sS --max-time 30 -D "$redirect_headers" -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-redirect.capy)
|
|
grep -Eq '^HTTP/1\.[01] 302 ' "$redirect_headers"
|
|
grep -Eqi '^Location: /tests/capy-session.capy\?action=read' "$redirect_headers"
|
|
[[ "$redirect_body" == "redirected" ]]
|
|
rm -f "$redirect_headers"
|
|
csrf_jar=$(mktemp)
|
|
csrf_token=$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-csrf.capy?action=token')
|
|
[[ "$csrf_token" =~ ^[0-9a-f]{64}$ ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' "http://127.0.0.1/tests/capy-csrf.capy?action=valid&submitted=$csrf_token")" == "true" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-csrf.capy?action=valid&submitted=wrong')" == "false" ]]
|
|
csrf_rotated=$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-csrf.capy?action=rotate')
|
|
[[ "$csrf_rotated" =~ ^[0-9a-f]{64}$ && "$csrf_rotated" != "$csrf_token" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' "http://127.0.0.1/tests/capy-csrf.capy?action=valid&submitted=$csrf_token")" == "false" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' "http://127.0.0.1/tests/capy-csrf.capy?action=valid&submitted=$csrf_rotated")" == "true" ]]
|
|
[[ "$(curl -fsS --max-time 30 -c "$csrf_jar" -b "$csrf_jar" -H 'Host: bearer.openfu.com' 'http://127.0.0.1/tests/capy-csrf.capy?action=field')" == "<input type=\"hidden\" name=\"submitted\" value=\"$csrf_rotated\">" ]]
|
|
rm -f "$csrf_jar"
|
|
websocket_test=$(mktemp)
|
|
trap 'rm -f "$websocket_test"' RETURN
|
|
clang++ -std=c++20 -Wall -Wextra -Werror -pedantic scripts/test_capy_websocket.cpp -o "$websocket_test"
|
|
"$websocket_test"
|
|
rm -f "$websocket_test"
|
|
expect_equal "SERVE_HTTP caller" "serve-ok" "$(scripts/bearer-cli /tests/capy-serve-http-caller.capy)"
|
|
rich_dval_output=$(scripts/bearer-cli /tests/capy-any-rich.capy)
|
|
[[ "$rich_dval_output" == "capy|Ada|9|custom-once;capy|capy|true|Ada|42|true|logic|truefalse|4|active;age;name;tags;|0=math;1=logic;|2;|4|0|falsefalse|2|0" ]] || {
|
|
echo "Capy rich DValue output mismatch: $rich_dval_output" >&2
|
|
exit 1
|
|
}
|
|
[[ "$(scripts/bearer-cli /tests/capy-any-rich-caller.capy)" == "custom-once;capy|Capy|roundtrip|3|true" ]] || {
|
|
echo "Capy-to-Capy custom DValue export failed" >&2
|
|
exit 1
|
|
}
|
|
none_output=$(scripts/bearer-cli /tests/capy-any-none.capy)
|
|
expect_equal "DValue none, safe read, conversion, codec, path update, and ARC contract" \
|
|
"false|true|true|true|true|false|false|false|false|false|true|false|Ada|[][string]|false|true|0|32|0|-64|0|64|0|6.5|false|false|false|null|true|false|true|false|false|created|replaced|old|kept|updated|changed|changed|ABR|done|false|0|<Ada>::|0" "$none_output"
|
|
[[ "$(scripts/bearer-cli /tests/capy-any-rich.capy)" == "$rich_dval_output" ]] || {
|
|
echo "Capy DValue workspace did not reset after strict trap" >&2
|
|
exit 1
|
|
}
|
|
for fixture_and_location in \
|
|
"capy-any-assign-list-trap:4:12"; do
|
|
fixture=${fixture_and_location%%:*}
|
|
location=${fixture_and_location#*:}
|
|
set +e
|
|
assignment_trap_output=$(scripts/bearer-cli "/tests/$fixture.capy" 2>&1)
|
|
assignment_trap_status=$?
|
|
set -e
|
|
[[ $assignment_trap_status -ne 0 && "$assignment_trap_output" == *"$fixture.capy:$location"* && "$assignment_trap_output" != *"capy://stdlib.capy"* ]] || {
|
|
echo "Capy nested DValue assignment trap/source mapping mismatch: fixture=$fixture output=$assignment_trap_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "DValue none recovery after $fixture" "$none_output" "$(scripts/bearer-cli /tests/capy-any-none.capy)"
|
|
done
|
|
arc_output=$(scripts/bearer-cli /tests/capy-arc.capy)
|
|
expected_arc='first|0|alphaalpha|1|1|1|789|1|8|2|4|1|12|3|ownedtwo|4|temp|1|picked|1|4|2|pair42|3|pair|6|inside|9|field|1|betaalpha|2|betaalphaalphabeta|3|tempz|4|double|5|nested|6|5|0'
|
|
[[ "$arc_output" == "$expected_arc" ]] || {
|
|
echo "Capy ARC ownership output mismatch: $arc_output" >&2
|
|
exit 1
|
|
}
|
|
arc_cache="$(scripts/unit_cache_directory "$bin_directory")$site_directory/tests/capy-arc.capy"
|
|
wasm-validate "$arc_cache.wasm"
|
|
wasm-objdump -x "$arc_cache.wasm" >"$arc_cache.objdump"
|
|
grep -q 'env.bearer_alloc' "$arc_cache.objdump"
|
|
grep -q 'env.bearer_free' "$arc_cache.objdump"
|
|
grep -Eq 'mem_p2align *: 3' "$arc_cache.objdump"
|
|
rm -f "$arc_cache.objdump"
|
|
set +e
|
|
trap_output=$(scripts/bearer-cli /tests/capy-arc-trap.capy 2>&1)
|
|
trap_status=$?
|
|
set -e
|
|
[[ $trap_status -ne 0 && "$trap_output" == *'wasm `unreachable` instruction executed'* && "$trap_output" == *'capy-arc-trap.capy'* ]] || {
|
|
echo "Capy ARC trap containment did not produce a source-associated trap" >&2
|
|
echo "$trap_output" >&2
|
|
exit 1
|
|
}
|
|
[[ "$(scripts/bearer-cli /tests/capy-arc.capy)" == "$expected_arc" ]] || {
|
|
echo "Capy ARC workspace did not reset after a trapping request" >&2
|
|
exit 1
|
|
}
|
|
for array_trap in capy-array-trap capy-array-negative-trap; do
|
|
set +e
|
|
array_trap_output=$(scripts/bearer-cli "/tests/$array_trap.capy" 2>&1)
|
|
array_trap_status=$?
|
|
set -e
|
|
[[ $array_trap_status -ne 0 && "$array_trap_output" == *"$array_trap.capy:3:11"* ]] || {
|
|
echo "Capy array bounds trap/source mapping mismatch: fixture=$array_trap status=$array_trap_status output=$array_trap_output" >&2
|
|
exit 1
|
|
}
|
|
[[ "$(scripts/bearer-cli /tests/capy-arc.capy)" == "$expected_arc" ]] || {
|
|
echo "Capy ARC workspace did not reset after array bounds trap $array_trap" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
render_output=$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-render.capy)
|
|
[[ "$render_output" == "capy-render-ok" ]] || {
|
|
echo "Capy HTTP RENDER output mismatch: $render_output" >&2
|
|
exit 1
|
|
}
|
|
|
|
cache="$(scripts/unit_cache_directory "$bin_directory")$site_directory/tests/capy-phase1.capy"
|
|
[[ -s "$cache.wasm" && -s "$cache.cwasm" && -s "$cache.cwasm.manifest" && -s "$cache.wasm.source-map" && -s "$cache.meta.txt" ]]
|
|
[[ $(sed -n '1p' "$cache.cwasm.manifest") == "$(sha256sum "$cache.wasm" | awk '{print $1}')" ]]
|
|
[[ $(sed -n '2p' "$cache.cwasm.manifest") == "$(sha256sum "$cache.cwasm" | awk '{print $1}')" ]]
|
|
grep -qx 'format=bearer-unit-metadata-v3' "$cache.meta.txt"
|
|
grep -Eq '^source_map_sha256=[0-9a-f]{64}$' "$cache.meta.txt"
|
|
grep -qx "wasm_core_abi_version=$(awk '/BEARER_WASM_CORE_ABI_VERSION/ {print $3; exit}' src/wasm/abi.h)" "$cache.meta.txt"
|
|
grep -qx "wasm_sha256=$(sha256sum "$cache.wasm" | awk '{print $1}')" "$cache.meta.txt"
|
|
grep -qx "exports_sha256=$(sha256sum "$cache.exports.txt" | awk '{print $1}')" "$cache.meta.txt"
|
|
bin/capyc --check-unit "$cache.wasm" "$(awk '/BEARER_WASM_CORE_ABI_VERSION/ {print $3; exit}' src/wasm/abi.h)"
|
|
wasm-objdump -x "$cache.wasm" >"$cache.objdump"
|
|
grep -q 'env.bearer_print_bytes' "$cache.objdump"
|
|
grep -q '__bearer_cli' "$cache.objdump"
|
|
grep -Eq 'mem_p2align *: 3' "$cache.objdump"
|
|
! grep -q 'wasi_snapshot_preview1' "$cache.objdump"
|
|
rm -f "$cache.objdump"
|
|
|
|
fixture="capy-compile-recovery-$$"
|
|
source_dir="$site_directory/$fixture"
|
|
artifact_dir="$(scripts/unit_cache_directory "$bin_directory")$source_dir"
|
|
cleanup() {
|
|
rm -rf "$source_dir" "$artifact_dir"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$source_dir"
|
|
for size in 63 64 127 128; do
|
|
payload=$(printf '%*s' "$size" '' | tr ' ' x)
|
|
printf 'function CLI(request : any) { print("%s") }\n' "$payload" >"$source_dir/entry.capy"
|
|
expect_equal "signed-LEB output boundary ($size bytes)" "$payload" "$(scripts/bearer-cli "/$fixture/entry.capy")"
|
|
done
|
|
render_prefix=$(printf '%*s' 64 '' | tr ' ' r)
|
|
printf 'function RENDER(request : any) { print("%s") }\nfunction CLI(request : any) { print("offset-ok") }\n' "$render_prefix" >"$source_dir/entry.capy"
|
|
expect_equal "source-map offset runtime" "offset-ok" "$(scripts/bearer-cli "/$fixture/entry.capy")"
|
|
|
|
printf '%s\n' 'function CLI(request : any) { print(not_a_constant) }' >"$source_dir/entry.capy"
|
|
set +e
|
|
failure=$(scripts/bearer-cli "/$fixture/entry.capy" 2>&1)
|
|
status=$?
|
|
set -e
|
|
[[ $status -ne 0 ]]
|
|
[[ "$failure" == *"entry.capy:1:"* && "$failure" == *"unknown local 'not_a_constant'"* ]]
|
|
[[ ! -e "$artifact_dir/entry.capy.wasm" ]]
|
|
|
|
printf '%s\n' 'function CLI(request : any) { print("capy-recovered") }' >"$source_dir/entry.capy"
|
|
expect_equal "source-map trap recovery" "capy-recovered" "$(scripts/bearer-cli "/$fixture/entry.capy")"
|
|
[[ -s "$artifact_dir/entry.capy.wasm" ]]
|
|
|
|
expect_equal "network socket/hardened HTTP adapters" "true|true|true|200|invalid_request|4" "$(scripts/bearer-cli /tests/capy-network-parity.capy)"
|
|
bash scripts/test_php_interop.sh site/tests/capy-php-interop.capy
|
|
expect_equal "time adapters" "5|0|981158400|0|0|1970|true|true|0" "$(scripts/bearer-cli /tests/capy-time-parity.capy)"
|
|
lifecycle_output=$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-lifecycle-parity.capy)
|
|
expect_equal "INIT/ONCE lifecycle" "entry-init;entry-once;child-init;child-once;child-component;child-component;render" "$lifecycle_output"
|
|
expect_equal "INIT/ONCE lifecycle in a later request workspace" \
|
|
"entry-init;entry-once;child-init;child-once;child-component;child-component;render" \
|
|
"$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-lifecycle-parity.capy)"
|
|
component_parity_output=$(curl -fsS --max-time 30 -H 'Host: bearer.openfu.com' http://127.0.0.1/tests/capy-component-parity.capy)
|
|
expect_equal "component/unit DValue conversions" "Capy|false|0|child|false|0|scalar|render|unit|true" "$component_parity_output"
|
|
expect_equal "general methods receiver order/overload/generic/local shadow/function fields" "receiver;argument;method;10|s32|string|9|shadow;5|field;5|extension;6" "$(scripts/bearer-cli /tests/capy-methods.capy)"
|
|
module_output=$(scripts/bearer-cli /tests/capy-module-caller.capy)
|
|
expect_equal "Capy module capability/default input/nested BRRB/legacy/none/copied identity/handler/ARC" "counted;handler-once;handler-render;default|nested|once|capy|legacy|false|resultresult|{}|{}|0" "$module_output"
|
|
# An absolute #import is a web path under the site root, resolved the same way
|
|
# unit_call() and component() resolve one. These units are temporary because the
|
|
# artifact-golden gate compiles every tracked site/tests unit without a
|
|
# --source-root, where a web path cannot resolve.
|
|
(
|
|
absolute_import=$(mktemp "$site_directory/tests/capy-absolute-import.XXXXXX")
|
|
rm -f "$absolute_import"
|
|
absolute_import="$absolute_import.capy"
|
|
trap 'rm -f -- "$absolute_import"' EXIT
|
|
cat >"$absolute_import" <<'UNIT'
|
|
#import "/tests/capy-unit-reference-target.capy" as target
|
|
|
|
function CLI(request : any) {
|
|
var profile : target.Profile = target.Profile("absolute")
|
|
print(string(target.current_user({scope: "web-path"}).scope), "|", profile.name)
|
|
}
|
|
UNIT
|
|
expect_equal "absolute #import resolves under the site root" "web-path|absolute" \
|
|
"$(scripts/bearer-cli "/tests/$(basename "$absolute_import")")"
|
|
)
|
|
expect_equal "unit reference forms: compound target/#import alias/imported struct" \
|
|
"legacy3|none|compound|none|ref-once;ref-render;|props|Ada|none|struct|{\"bool\": true, \"raw\": true, \"string\": \"true\"}" \
|
|
"$(scripts/bearer-cli /tests/capy-unit-reference.capy)"
|
|
module_exports="$(scripts/unit_cache_directory "$bin_directory")$site_directory/tests/capy-module-target.capy.exports.txt"
|
|
grep -qx 'DValue\* echo(DValue\*);' "$module_exports"
|
|
grep -qx 'DValue\* counted(DValue\*);' "$module_exports"
|
|
grep -qx 'capy function echo(any):any' "$module_exports"
|
|
(
|
|
metadata_dir=$(mktemp -d "$site_directory/tests/capy-export-metadata.XXXXXX")
|
|
trap 'rm -rf -- "$metadata_dir"' EXIT
|
|
metadata_name=${metadata_dir##*/}
|
|
cat >"$metadata_dir/provider.capy" <<'EOF'
|
|
#exports echo, Shape, ShapeAlias
|
|
struct Shape { value : s32 }
|
|
type ShapeAlias = Shape
|
|
function echo(input : any) any { -> input }
|
|
function Shape(input : any) Shape { -> Shape(1) }
|
|
EOF
|
|
cat >"$metadata_dir/reader.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
unit_compile("provider.capy")
|
|
var info := unit_info("provider.capy")
|
|
print(string(info.function_exports["0"]), "|", string(info.function_exports["1"]), "|", string(info.type_exports["0"]), "|", string(info.type_exports["1"]))
|
|
}
|
|
EOF
|
|
cat >"$metadata_dir/importer.capy" <<'EOF'
|
|
#import"provider.capy" as provider
|
|
|
|
type ImportedShape = provider.Shape
|
|
type ImportedAlias = provider.ShapeAlias
|
|
|
|
function CLI(request : any) {
|
|
print("imported")
|
|
}
|
|
EOF
|
|
expect_equal "Capy #exports function and type metadata" "echo|Shape|struct Shape{value:s32}|alias ShapeAlias=struct:Shape" "$(scripts/bearer-cli "/tests/$metadata_name/reader.capy")"
|
|
expect_equal "Capy #import type metadata" "imported" "$(scripts/bearer-cli "/tests/$metadata_name/importer.capy")"
|
|
printf '%s\n' '#exports Shape' 'struct Shape { value : MissingType }' >"$metadata_dir/provider.capy"
|
|
set +e
|
|
import_failure=$(scripts/bearer-cli "/tests/$metadata_name/importer.capy" 2>&1)
|
|
import_status=$?
|
|
set -e
|
|
[[ $import_status -ne 0 && "$import_failure" == *"#import failed to compile"* ]] || {
|
|
echo "Capy #import dependency invalidation did not reject a broken provider: $import_failure" >&2
|
|
exit 1
|
|
}
|
|
cat >"$metadata_dir/provider.capy" <<'EOF'
|
|
#exports echo, Shape, ShapeAlias
|
|
struct Shape { value : s32 }
|
|
type ShapeAlias = Shape
|
|
function echo(input : any) any { -> input }
|
|
function Shape(input : any) Shape { -> Shape(1) }
|
|
EOF
|
|
expect_equal "Capy #import recovery" "imported" "$(scripts/bearer-cli "/tests/$metadata_name/importer.capy")"
|
|
cat >"$metadata_dir/cycle-a.capy" <<'EOF'
|
|
#import"cycle-b.capy" as b
|
|
type A = b.B
|
|
function CLI(request : any) { print("bad") }
|
|
EOF
|
|
cat >"$metadata_dir/cycle-b.capy" <<'EOF'
|
|
#import"cycle-a.capy" as a
|
|
type B = a.A
|
|
EOF
|
|
set +e
|
|
cycle_failure=$(scripts/bearer-cli "/tests/$metadata_name/cycle-a.capy" 2>&1)
|
|
cycle_status=$?
|
|
set -e
|
|
[[ $cycle_status -ne 0 && "$cycle_failure" == *"#import dependency cycle"* ]] || {
|
|
echo "Capy #import cycle was not rejected: $cycle_failure" >&2
|
|
exit 1
|
|
}
|
|
)
|
|
(
|
|
module_test_dir=$(mktemp -d "$site_directory/tests/capy-module-trap.XXXXXX")
|
|
trap 'rm -rf -- "$module_test_dir"' EXIT
|
|
module_test_name=${module_test_dir##*/}
|
|
check_module_trap() {
|
|
local name=$1 source=$2
|
|
printf '%s\n' "$source" >"$module_test_dir/caller.capy"
|
|
set +e
|
|
local output
|
|
output=$(scripts/bearer-cli "/tests/$module_test_name/caller.capy" 2>&1)
|
|
local status=$?
|
|
set -e
|
|
[[ $status -ne 0 && "$output" == *"BEARER_MODULE_"* && "$output" == *"caller.capy:"* && "$output" != *"capy://stdlib.capy"* ]] || {
|
|
echo "Capy module $name trap/source mapping mismatch: $output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "Capy module $name recovery" "counted;handler-once;handler-render;default|nested|once|capy|legacy|false|resultresult|{}|{}|0" "$(scripts/bearer-cli /tests/capy-module-caller.capy)"
|
|
}
|
|
check_module_trap missing 'function CLI(request : any) { var module := unit_load("missing.capy") }'
|
|
check_module_trap unauthorized 'function CLI(request : any) { var module := unit_load("/etc/passwd") }'
|
|
check_module_trap undeclared $'function CLI(request : any) {\n unit_call("/tests/capy-module-target.capy:hidden", "")\n}'
|
|
check_module_trap wrong_target $'function CLI(request : any) {\n var module := unit_load("/tests/capy-module-caller.capy")\n module.call("CLI")\n}'
|
|
external_unit=$(mktemp /tmp/capy-module-external.XXXXXX.capy)
|
|
trap 'rm -f -- "$external_unit"; rm -rf -- "$module_test_dir"' EXIT
|
|
printf '%s\n' '#exports echo' 'function echo(input : any) any { -> any("external") }' >"$external_unit"
|
|
check_module_trap external_source "function CLI(request : any) { var module := unit_load(\"$external_unit\"); print(string(module.call(\"echo\"))) }"
|
|
ln -s "$external_unit" "$module_test_dir/escape.capy"
|
|
check_module_trap source_symlink 'function CLI(request : any) { var module := unit_load("escape.capy"); print(string(module.call("echo"))) }'
|
|
printf '%s\n' 'function CLI(request : any) { print(not_a_constant) }' >"$module_test_dir/broken.capy"
|
|
check_module_trap failed_compile 'function CLI(request : any) { var module := unit_load("broken.capy") }'
|
|
printf '%s\n' '#exports echo' 'function echo(input : any) any { -> any("v1") }' >"$module_test_dir/target.capy"
|
|
cat >"$module_test_dir/pinned.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
var module := unit_load("target.capy")
|
|
file_put_contents("target.capy", "#exports echo\nfunction echo(input : any) any { -> any(\"v2\") }\n")
|
|
print(string(module.call("echo", "")))
|
|
}
|
|
EOF
|
|
printf '%s\n' 'function CLI(request : any) { print(string(unit_load("target.capy").call("echo", ""))) }' >"$module_test_dir/fresh.capy"
|
|
expect_equal "Capy module request pin" "v1" "$(scripts/bearer-cli "/tests/$module_test_name/pinned.capy")"
|
|
expect_equal "Capy module next-request reload" "v2" "$(scripts/bearer-cli "/tests/$module_test_name/fresh.capy")"
|
|
printf '%s\n' 'function CLI(request : any) { print(not_a_constant) }' >"$module_test_dir/target.capy"
|
|
check_module_trap failed_reload_compile 'function CLI(request : any) { var module := unit_load("target.capy") }'
|
|
printf '%s\n' '#exports echo' 'function echo(input : any) any { -> any("v3") }' >"$module_test_dir/target.capy"
|
|
expect_equal "Capy module failed-compile recovery" "v3" "$(scripts/bearer-cli "/tests/$module_test_name/fresh.capy")"
|
|
module_artifacts="$(scripts/unit_cache_directory "$bin_directory")$module_test_dir/target.capy"
|
|
printf '\377' | dd of="$module_artifacts.wasm" bs=1 seek=8 conv=notrunc status=none
|
|
check_module_trap wasm_tamper 'function CLI(request : any) { unit_call("target.capy:echo", "") }'
|
|
printf '%s\n' 'DValue* echo(DValue*);' 'not an export declaration' >"$module_artifacts.exports.txt"
|
|
check_module_trap exports_tamper 'function CLI(request : any) { unit_call("target.capy:echo", "") }'
|
|
sed -i 's/^wasm_sha256=.*/wasm_sha256=0000000000000000000000000000000000000000000000000000000000000000/' "$module_artifacts.meta.txt"
|
|
check_module_trap metadata_tamper 'function CLI(request : any) { unit_call("target.capy:echo", "") }'
|
|
printf '%s\n' '#exports echo, nested, file_relative, boom' \
|
|
'function echo(input : any) any { -> any("v4") }' \
|
|
'function nested(input : any) any { -> unit_load("nested.capy").call("echo", input) }' \
|
|
'function file_relative(input : any) any { -> any(file_get_contents("marker.txt")) }' \
|
|
'function boom(input : any) any { trap(); -> any("") }' >"$module_test_dir/target.capy"
|
|
printf '%s\n' '#exports echo' 'function echo(input : any) any { -> any("nested") }' >"$module_test_dir/nested.capy"
|
|
printf '%s' 'target-file' >"$module_test_dir/marker.txt"
|
|
cat >"$module_test_dir/provenance.capy" <<'EOF'
|
|
function CLI(request : any) {
|
|
var target := unit_load("target.capy")
|
|
print(string(target.call("nested", "")), "|", string(target.call("file_relative", "")))
|
|
}
|
|
EOF
|
|
expect_equal "Capy module target provenance" "nested|target-file" "$(scripts/bearer-cli "/tests/$module_test_name/provenance.capy")"
|
|
printf '%s\n' 'function CLI(request : any) {' ' unit_load("target.capy").call("boom", "")' '}' >"$module_test_dir/caller.capy"
|
|
set +e
|
|
target_trap_output=$(scripts/bearer-cli "/tests/$module_test_name/caller.capy" 2>&1)
|
|
target_trap_status=$?
|
|
set -e
|
|
[[ $target_trap_status -ne 0 && "$target_trap_output" == *"caller.capy:2:29"* && "$target_trap_output" != *"capy://stdlib.capy"* ]] || {
|
|
echo "Capy module target trap/source map mismatch: $target_trap_output" >&2
|
|
exit 1
|
|
}
|
|
expect_equal "Capy module trap reset recovery" "nested|target-file" "$(scripts/bearer-cli "/tests/$module_test_name/provenance.capy")"
|
|
)
|
|
|
|
echo "Capy phase 1 parser/direct-Wasm/CLI smoke passed"
|