Add unit and integration tests for all major implementations

This commit is contained in:
Russell Ballestrini 2026-01-05 21:29:44 -05:00
parent ad074c1b91
commit a379e061fc
10 changed files with 2523 additions and 0 deletions

64
tests/integration/run_all.sh Executable file
View file

@ -0,0 +1,64 @@
#!/bin/bash
# Run all integration tests for UN CLI implementations
# These tests verify component interactions without making real API calls
set -o pipefail
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
cd "$(dirname "$0")"
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ UN CLI Inception - Integration Tests ║${NC}"
echo -e "${CYAN}║ Testing component interactions, no real API calls ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
echo ""
passed=0
failed=0
skipped=0
run_test() {
local name="$1"
local cmd="$2"
local interpreter="$3"
printf "%-30s" "$name"
if [ -n "$interpreter" ] && ! command -v "$interpreter" &> /dev/null; then
echo -e "${YELLOW}SKIP${NC} ($interpreter not found)"
skipped=$((skipped + 1))
return
fi
if $cmd > /dev/null 2>&1; then
echo -e "${GREEN}PASS${NC}"
passed=$((passed + 1))
else
echo -e "${RED}FAIL${NC}"
failed=$((failed + 1))
fi
}
echo -e "${CYAN}━━━ Request Building Tests ━━━${NC}"
run_test "Python Request Building" "python3 test_request_building.py" "python3"
echo ""
# Summary
echo -e "${CYAN}══════════════════════════════════════════════════════════════${NC}"
echo ""
total=$((passed + failed + skipped))
echo -e "Results: ${GREEN}$passed PASS${NC} | ${RED}$failed FAIL${NC} | ${YELLOW}$skipped SKIP${NC} | Total: $total"
echo ""
if [ $failed -eq 0 ]; then
echo -e "${GREEN}All integration tests passed.${NC}"
exit 0
else
echo -e "${RED}$failed test(s) failed.${NC}"
exit 1
fi

View file

@ -0,0 +1,374 @@
#!/usr/bin/env python3
"""
Integration tests for request building - tests component interactions without API calls
"""
import sys
import os
import json
import hmac
import hashlib
import base64
import tempfile
import unittest
from unittest.mock import patch, MagicMock
from io import StringIO
# Set mock environment before importing
os.environ['UNSANDBOX_PUBLIC_KEY'] = 'test-pk-1234'
os.environ['UNSANDBOX_SECRET_KEY'] = 'test-sk-5678'
class TestRequestBuilding(unittest.TestCase):
"""Test that requests are built correctly with all components"""
def test_execute_request_structure(self):
"""Test execute request has correct structure"""
language = "python"
code = 'print("hello world")'
env_vars = {"DEBUG": "1", "NAME": "test"}
network_mode = "zerotrust"
# Build request body
body = {
"language": language,
"code": code,
"env": env_vars,
"network_mode": network_mode
}
json_body = json.dumps(body)
parsed = json.loads(json_body)
self.assertEqual(parsed["language"], "python")
self.assertEqual(parsed["code"], code)
self.assertEqual(parsed["env"]["DEBUG"], "1")
self.assertEqual(parsed["network_mode"], "zerotrust")
def test_execute_request_with_files(self):
"""Test execute request with input files"""
# Create temp file
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
f.write("test data content")
temp_path = f.name
try:
# Read and encode file
with open(temp_path, 'rb') as f:
content = f.read()
encoded = base64.b64encode(content).decode('utf-8')
files = [{
"name": os.path.basename(temp_path),
"content": encoded
}]
body = {
"language": "python",
"code": "print(open('test.txt').read())",
"files": files
}
json_body = json.dumps(body)
parsed = json.loads(json_body)
self.assertIn("files", parsed)
self.assertEqual(len(parsed["files"]), 1)
self.assertIn("content", parsed["files"][0])
# Verify content can be decoded back
decoded = base64.b64decode(parsed["files"][0]["content"])
self.assertEqual(decoded.decode('utf-8'), "test data content")
finally:
os.unlink(temp_path)
def test_hmac_signature_generation(self):
"""Test HMAC signature is generated correctly for requests"""
public_key = "test-pk-1234"
secret_key = "test-sk-5678"
timestamp = "1704067200"
method = "POST"
endpoint = "/execute"
body = '{"language":"python","code":"print(1)"}'
# Build signature input
signature_input = f"{timestamp}:{method}:{endpoint}:{body}"
# Generate signature
signature = hmac.new(
secret_key.encode(),
signature_input.encode(),
hashlib.sha256
).hexdigest()
# Verify signature properties
self.assertEqual(len(signature), 64)
self.assertTrue(all(c in '0123456789abcdef' for c in signature))
# Verify same input produces same signature
signature2 = hmac.new(
secret_key.encode(),
signature_input.encode(),
hashlib.sha256
).hexdigest()
self.assertEqual(signature, signature2)
def test_session_request_structure(self):
"""Test session request has correct structure"""
body = {
"shell": "python3",
"network_mode": "semitrusted"
}
json_body = json.dumps(body)
parsed = json.loads(json_body)
self.assertEqual(parsed["shell"], "python3")
self.assertEqual(parsed["network_mode"], "semitrusted")
def test_service_request_structure(self):
"""Test service request has correct structure"""
body = {
"name": "my-service",
"ports": [8080, 443],
"bootstrap": "python3 -m http.server 8080",
"network_mode": "semitrusted"
}
json_body = json.dumps(body)
parsed = json.loads(json_body)
self.assertEqual(parsed["name"], "my-service")
self.assertEqual(parsed["ports"], [8080, 443])
self.assertEqual(parsed["bootstrap"], "python3 -m http.server 8080")
def test_authorization_header_format(self):
"""Test Authorization header is formatted correctly"""
public_key = "unsb-pk-1234-5678-abcd-efgh"
auth_header = f"Bearer {public_key}"
self.assertTrue(auth_header.startswith("Bearer "))
self.assertIn(public_key, auth_header)
def test_request_headers_structure(self):
"""Test all required headers are present"""
public_key = "unsb-pk-1234"
timestamp = "1704067200"
signature = "abc123def456"
headers = {
"Authorization": f"Bearer {public_key}",
"X-Timestamp": timestamp,
"X-Signature": signature,
"Content-Type": "application/json"
}
self.assertIn("Authorization", headers)
self.assertIn("X-Timestamp", headers)
self.assertIn("X-Signature", headers)
self.assertIn("Content-Type", headers)
self.assertEqual(headers["Content-Type"], "application/json")
class TestResponseParsing(unittest.TestCase):
"""Test that responses are parsed correctly"""
def test_execute_response_parsing(self):
"""Test execute response is parsed correctly"""
response_json = {
"id": "exec-12345",
"status": "completed",
"stdout": "hello world\n",
"stderr": "",
"exit_code": 0,
"execution_time": 0.123
}
json_str = json.dumps(response_json)
parsed = json.loads(json_str)
self.assertEqual(parsed["id"], "exec-12345")
self.assertEqual(parsed["status"], "completed")
self.assertEqual(parsed["stdout"], "hello world\n")
self.assertEqual(parsed["exit_code"], 0)
def test_session_response_parsing(self):
"""Test session response is parsed correctly"""
response_json = {
"id": "sess-12345",
"status": "ready",
"websocket_url": "wss://api.unsandbox.com/ws/sess-12345"
}
json_str = json.dumps(response_json)
parsed = json.loads(json_str)
self.assertEqual(parsed["id"], "sess-12345")
self.assertEqual(parsed["status"], "ready")
self.assertIn("websocket_url", parsed)
def test_service_response_parsing(self):
"""Test service response is parsed correctly"""
response_json = {
"id": "svc-12345",
"name": "my-service",
"status": "running",
"domains": ["my-service.unsandbox.run"],
"ports": {"8080": "https://my-service.unsandbox.run:8080"}
}
json_str = json.dumps(response_json)
parsed = json.loads(json_str)
self.assertEqual(parsed["id"], "svc-12345")
self.assertEqual(parsed["name"], "my-service")
self.assertEqual(parsed["status"], "running")
self.assertIsInstance(parsed["domains"], list)
def test_error_response_parsing(self):
"""Test error response is parsed correctly"""
response_json = {
"error": "Authentication failed",
"code": "AUTH_ERROR",
"status": 401
}
json_str = json.dumps(response_json)
parsed = json.loads(json_str)
self.assertIn("error", parsed)
self.assertEqual(parsed["code"], "AUTH_ERROR")
self.assertEqual(parsed["status"], 401)
def test_artifacts_response_parsing(self):
"""Test response with artifacts is parsed correctly"""
response_json = {
"id": "exec-12345",
"status": "completed",
"stdout": "Generated output\n",
"artifacts": [
{
"name": "output.png",
"content": "iVBORw0KGgo=", # base64
"size": 12345
}
]
}
json_str = json.dumps(response_json)
parsed = json.loads(json_str)
self.assertIn("artifacts", parsed)
self.assertEqual(len(parsed["artifacts"]), 1)
self.assertEqual(parsed["artifacts"][0]["name"], "output.png")
class TestErrorHandling(unittest.TestCase):
"""Test error handling components"""
def test_missing_api_key_detection(self):
"""Test detection of missing API keys"""
# Save current env
saved_pub = os.environ.get('UNSANDBOX_PUBLIC_KEY')
saved_sec = os.environ.get('UNSANDBOX_SECRET_KEY')
try:
del os.environ['UNSANDBOX_PUBLIC_KEY']
del os.environ['UNSANDBOX_SECRET_KEY']
# Check that keys are missing
public_key = os.environ.get('UNSANDBOX_PUBLIC_KEY')
secret_key = os.environ.get('UNSANDBOX_SECRET_KEY')
self.assertIsNone(public_key)
self.assertIsNone(secret_key)
finally:
# Restore env
if saved_pub:
os.environ['UNSANDBOX_PUBLIC_KEY'] = saved_pub
if saved_sec:
os.environ['UNSANDBOX_SECRET_KEY'] = saved_sec
def test_invalid_json_handling(self):
"""Test handling of invalid JSON responses"""
invalid_json = "not valid json {"
with self.assertRaises(json.JSONDecodeError):
json.loads(invalid_json)
def test_file_not_found_handling(self):
"""Test handling of missing files"""
with self.assertRaises(FileNotFoundError):
with open("/nonexistent/file/path.py", 'r') as f:
f.read()
def test_invalid_extension_handling(self):
"""Test handling of unrecognized file extensions"""
EXT_MAP = {".py": "python", ".js": "javascript"}
ext = ".xyz"
lang = EXT_MAP.get(ext)
self.assertIsNone(lang)
class TestFileProcessing(unittest.TestCase):
"""Test file processing integration"""
def test_multiple_files_encoding(self):
"""Test encoding multiple files for request"""
files_data = []
# Create multiple temp files
temp_files = []
for i in range(3):
with tempfile.NamedTemporaryFile(mode='w', suffix=f'.txt', delete=False) as f:
f.write(f"File {i} content")
temp_files.append(f.name)
try:
for temp_path in temp_files:
with open(temp_path, 'rb') as f:
content = f.read()
files_data.append({
"name": os.path.basename(temp_path),
"content": base64.b64encode(content).decode('utf-8')
})
# Verify all files encoded
self.assertEqual(len(files_data), 3)
# Verify each file can be decoded
for i, file_data in enumerate(files_data):
decoded = base64.b64decode(file_data["content"])
self.assertIn(f"File {i} content", decoded.decode('utf-8'))
finally:
for temp_path in temp_files:
os.unlink(temp_path)
def test_binary_file_encoding(self):
"""Test encoding binary files"""
# Create binary content
binary_content = bytes([0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD])
with tempfile.NamedTemporaryFile(mode='wb', delete=False) as f:
f.write(binary_content)
temp_path = f.name
try:
with open(temp_path, 'rb') as f:
content = f.read()
encoded = base64.b64encode(content).decode('utf-8')
# Verify can decode back to original
decoded = base64.b64decode(encoded)
self.assertEqual(decoded, binary_content)
finally:
os.unlink(temp_path)
if __name__ == '__main__':
unittest.main(verbosity=2)

72
tests/unit/run_all.sh Executable file
View file

@ -0,0 +1,72 @@
#!/bin/bash
# Run all unit tests for UN CLI implementations
# These tests do NOT call the API - they test internal logic only
set -o pipefail
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
cd "$(dirname "$0")"
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ UN CLI Inception - Unit Tests ║${NC}"
echo -e "${CYAN}║ Testing internal logic, no API calls ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
echo ""
passed=0
failed=0
skipped=0
run_test() {
local name="$1"
local cmd="$2"
local interpreter="$3"
printf "%-20s" "$name"
if [ -n "$interpreter" ] && ! command -v "$interpreter" &> /dev/null; then
echo -e "${YELLOW}SKIP${NC} ($interpreter not found)"
skipped=$((skipped + 1))
return
fi
if $cmd > /dev/null 2>&1; then
echo -e "${GREEN}PASS${NC}"
passed=$((passed + 1))
else
echo -e "${RED}FAIL${NC}"
failed=$((failed + 1))
fi
}
echo -e "${CYAN}━━━ Scripting Languages ━━━${NC}"
run_test "Python" "python3 test_python.py" "python3"
run_test "JavaScript" "node test_javascript.js" "node"
run_test "Ruby" "ruby test_ruby.rb" "ruby"
run_test "Lua" "lua test_lua.lua" "lua"
run_test "Bash" "bash test_bash.sh" "bash"
echo ""
echo -e "${CYAN}━━━ Systems Languages ━━━${NC}"
run_test "Go" "go run test_go.go" "go"
echo ""
# Summary
echo -e "${CYAN}══════════════════════════════════════════════════════════════${NC}"
echo ""
total=$((passed + failed + skipped))
echo -e "Results: ${GREEN}$passed PASS${NC} | ${RED}$failed FAIL${NC} | ${YELLOW}$skipped SKIP${NC} | Total: $total"
echo ""
if [ $failed -eq 0 ]; then
echo -e "${GREEN}All unit tests passed.${NC}"
exit 0
else
echo -e "${RED}$failed test(s) failed.${NC}"
exit 1
fi

278
tests/unit/test_bash.sh Executable file
View file

@ -0,0 +1,278 @@
#!/bin/bash
# Unit tests for un.sh - tests internal functions without API calls
set -o pipefail
PASSED=0
FAILED=0
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
test_case() {
local name="$1"
shift
if "$@"; then
echo -e " ${GREEN}${NC} $name"
PASSED=$((PASSED + 1))
else
echo -e " ${RED}${NC} $name"
FAILED=$((FAILED + 1))
fi
}
assert_equal() {
local actual="$1"
local expected="$2"
[ "$actual" = "$expected" ]
}
assert_not_equal() {
local a="$1"
local b="$2"
[ "$a" != "$b" ]
}
assert_contains() {
local str="$1"
local substr="$2"
[[ "$str" == *"$substr"* ]]
}
# ============================================================================
# Extension Mapping Tests
# ============================================================================
echo ""
echo "=== Extension Mapping Tests ==="
# Extension to language mapping (from un.sh)
get_language() {
local ext="${1##*.}"
case ".$ext" in
.py) echo "python" ;;
.js) echo "javascript" ;;
.ts) echo "typescript" ;;
.rb) echo "ruby" ;;
.php) echo "php" ;;
.pl) echo "perl" ;;
.lua) echo "lua" ;;
.sh) echo "bash" ;;
.go) echo "go" ;;
.rs) echo "rust" ;;
.c) echo "c" ;;
.cpp|.cc|.cxx) echo "cpp" ;;
.java) echo "java" ;;
.kt) echo "kotlin" ;;
.cs) echo "csharp" ;;
.fs) echo "fsharp" ;;
.hs) echo "haskell" ;;
.ml) echo "ocaml" ;;
.clj) echo "clojure" ;;
.scm) echo "scheme" ;;
.lisp) echo "commonlisp" ;;
.erl) echo "erlang" ;;
.ex|.exs) echo "elixir" ;;
.jl) echo "julia" ;;
.r|.R) echo "r" ;;
.cr) echo "crystal" ;;
.d) echo "d" ;;
.nim) echo "nim" ;;
.zig) echo "zig" ;;
.v) echo "v" ;;
.dart) echo "dart" ;;
.groovy) echo "groovy" ;;
.scala) echo "scala" ;;
.f90|.f95) echo "fortran" ;;
.cob) echo "cobol" ;;
.pro) echo "prolog" ;;
.forth|.4th) echo "forth" ;;
.tcl) echo "tcl" ;;
.raku) echo "raku" ;;
.m) echo "objc" ;;
*) echo "" ;;
esac
}
test_case "Python extension maps correctly" assert_equal "$(get_language script.py)" "python"
test_case "JavaScript extension maps correctly" assert_equal "$(get_language app.js)" "javascript"
test_case "TypeScript extension maps correctly" assert_equal "$(get_language app.ts)" "typescript"
test_case "Ruby extension maps correctly" assert_equal "$(get_language app.rb)" "ruby"
test_case "Go extension maps correctly" assert_equal "$(get_language main.go)" "go"
test_case "Rust extension maps correctly" assert_equal "$(get_language main.rs)" "rust"
test_case "C extension maps correctly" assert_equal "$(get_language main.c)" "c"
test_case "C++ extension maps correctly" assert_equal "$(get_language main.cpp)" "cpp"
test_case "Java extension maps correctly" assert_equal "$(get_language Main.java)" "java"
test_case "Kotlin extension maps correctly" assert_equal "$(get_language main.kt)" "kotlin"
test_case "Haskell extension maps correctly" assert_equal "$(get_language main.hs)" "haskell"
test_case "Elixir extension maps correctly" assert_equal "$(get_language main.ex)" "elixir"
test_case "Julia extension maps correctly" assert_equal "$(get_language main.jl)" "julia"
# ============================================================================
# HMAC Signature Tests
# ============================================================================
echo ""
echo "=== HMAC Signature Tests ==="
# HMAC function (requires openssl)
hmac_sha256() {
local secret="$1"
local message="$2"
echo -n "$message" | openssl dgst -sha256 -hmac "$secret" | sed 's/^.* //'
}
test_case "HMAC-SHA256 generates 64 character hex string" \
bash -c 'sig=$(echo -n "test" | openssl dgst -sha256 -hmac "secret" | sed "s/^.* //"); [ ${#sig} -eq 64 ]'
test_case "Same input produces same signature" \
bash -c '[[ "$(echo -n "msg" | openssl dgst -sha256 -hmac "key" | sed "s/^.* //")" == "$(echo -n "msg" | openssl dgst -sha256 -hmac "key" | sed "s/^.* //")" ]]'
SIG1=$(echo -n "msg" | openssl dgst -sha256 -hmac "key1" | sed 's/^.* //')
SIG2=$(echo -n "msg" | openssl dgst -sha256 -hmac "key2" | sed 's/^.* //')
test_case "Different secrets produce different signatures" assert_not_equal "$SIG1" "$SIG2"
SIG3=$(echo -n "msg1" | openssl dgst -sha256 -hmac "key" | sed 's/^.* //')
SIG4=$(echo -n "msg2" | openssl dgst -sha256 -hmac "key" | sed 's/^.* //')
test_case "Different messages produce different signatures" assert_not_equal "$SIG3" "$SIG4"
test_case "Signature format is timestamp:METHOD:path:body" \
bash -c 'msg="1234567890:POST:/execute:{}"; [[ "$msg" == *":"* ]] && [[ $(echo "$msg" | tr -cd ":" | wc -c) -eq 3 ]]'
# ============================================================================
# Language Detection Tests
# ============================================================================
echo ""
echo "=== Language Detection Tests ==="
detect_from_shebang() {
local first_line="$1"
if [[ "$first_line" == "#!"* ]]; then
case "$first_line" in
*python*) echo "python" ;;
*node*) echo "javascript" ;;
*ruby*) echo "ruby" ;;
*perl*) echo "perl" ;;
*bash*|*/sh*) echo "bash" ;;
*lua*) echo "lua" ;;
*php*) echo "php" ;;
*) echo "" ;;
esac
fi
}
test_case "Python shebang detection" \
assert_equal "$(detect_from_shebang '#!/usr/bin/env python3')" "python"
test_case "Node shebang detection" \
assert_equal "$(detect_from_shebang '#!/usr/bin/env node')" "javascript"
test_case "Ruby shebang detection" \
assert_equal "$(detect_from_shebang '#!/usr/bin/env ruby')" "ruby"
test_case "Bash shebang detection" \
assert_equal "$(detect_from_shebang '#!/bin/bash')" "bash"
test_case "Sh shebang detection" \
assert_equal "$(detect_from_shebang '#!/bin/sh')" "bash"
# ============================================================================
# Argument Parsing Tests
# ============================================================================
echo ""
echo "=== Argument Parsing Tests ==="
parse_env_var() {
local arg="$1"
local key="${arg%%=*}"
local value="${arg#*=}"
echo "$key|$value"
}
test_case "Parse -e KEY=VALUE format" \
assert_equal "$(parse_env_var 'DEBUG=1')" "DEBUG|1"
test_case "Parse -e KEY=VALUE with equals in value" \
assert_equal "$(parse_env_var 'URL=https://example.com?foo=bar')" "URL|https://example.com?foo=bar"
test_case "Valid network mode zerotrust" \
bash -c '[[ "zerotrust" =~ ^(zerotrust|semitrusted)$ ]]'
test_case "Valid network mode semitrusted" \
bash -c '[[ "semitrusted" =~ ^(zerotrust|semitrusted)$ ]]'
test_case "Invalid network mode rejected" \
bash -c '! [[ "invalid" =~ ^(zerotrust|semitrusted)$ ]]'
is_subcommand() {
local cmd="$1"
case "$cmd" in
session|service|key|restore) return 0 ;;
*) return 1 ;;
esac
}
test_case "Subcommand detection - session" is_subcommand "session"
test_case "Subcommand detection - service" is_subcommand "service"
test_case "Subcommand detection - key" is_subcommand "key"
test_case "Not a subcommand - script.py" bash -c '! is_subcommand "script.py"'
# ============================================================================
# File Operations Tests
# ============================================================================
echo ""
echo "=== File Operations Tests ==="
TMPFILE=$(mktemp --suffix=.py)
echo -n 'print("hello world")' > "$TMPFILE"
test_case "Read text file" \
assert_equal "$(cat "$TMPFILE")" 'print("hello world")'
test_case "Base64 encoding/decoding" \
bash -c '[[ "$(echo -n "hello" | base64 | base64 -d)" == "hello" ]]'
test_case "Extract file basename" \
assert_equal "$(basename /home/user/project/script.py)" "script.py"
EXT="${TMPFILE##*.}"
test_case "Extract file extension" \
assert_equal "$EXT" "py"
rm -f "$TMPFILE"
# ============================================================================
# API Constants Tests
# ============================================================================
echo ""
echo "=== API Constants Tests ==="
API_BASE="https://api.unsandbox.com"
PORTAL_BASE="https://unsandbox.com"
test_case "API base URL starts with https" \
bash -c '[[ "https://api.unsandbox.com" == https://* ]]'
test_case "API base URL contains unsandbox.com" \
assert_contains "$API_BASE" "unsandbox.com"
test_case "Portal base URL starts with https" \
bash -c '[[ "https://unsandbox.com" == https://* ]]'
# ============================================================================
# Summary
# ============================================================================
echo ""
echo "=== Summary ==="
echo "Passed: $PASSED"
echo "Failed: $FAILED"
echo "Total: $((PASSED + FAILED))"
exit $((FAILED > 0 ? 1 : 0))

376
tests/unit/test_go.go Normal file
View file

@ -0,0 +1,376 @@
// Unit tests for un.go - tests internal functions without API calls
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"strings"
)
var passed, failed int
func test(name string, fn func() error) {
if err := fn(); err != nil {
fmt.Printf(" ✗ %s\n", name)
fmt.Printf(" %s\n", err)
failed++
} else {
fmt.Printf(" ✓ %s\n", name)
passed++
}
}
func assertEqual(actual, expected string) error {
if actual != expected {
return fmt.Errorf("expected '%s' but got '%s'", expected, actual)
}
return nil
}
func assertNotEqual(a, b string) error {
if a == b {
return fmt.Errorf("expected values to be different but both were '%s'", a)
}
return nil
}
func assertContains(str, substr string) error {
if !strings.Contains(str, substr) {
return fmt.Errorf("expected '%s' to contain '%s'", str, substr)
}
return nil
}
func assertTrue(val bool) error {
if !val {
return fmt.Errorf("expected true but got false")
}
return nil
}
// Extension mapping (copied from un.go)
var extMap = map[string]string{
".py": "python", ".js": "javascript", ".ts": "typescript",
".rb": "ruby", ".php": "php", ".pl": "perl", ".lua": "lua",
".sh": "bash", ".go": "go", ".rs": "rust", ".c": "c",
".cpp": "cpp", ".cc": "cpp", ".cxx": "cpp",
".java": "java", ".kt": "kotlin", ".cs": "csharp", ".fs": "fsharp",
".hs": "haskell", ".ml": "ocaml", ".clj": "clojure", ".scm": "scheme",
".lisp": "commonlisp", ".erl": "erlang", ".ex": "elixir", ".exs": "elixir",
".jl": "julia", ".r": "r", ".R": "r", ".cr": "crystal",
".d": "d", ".nim": "nim", ".zig": "zig", ".v": "v",
".dart": "dart", ".groovy": "groovy", ".scala": "scala",
".f90": "fortran", ".f95": "fortran", ".cob": "cobol",
".pro": "prolog", ".forth": "forth", ".4th": "forth",
".tcl": "tcl", ".raku": "raku", ".m": "objc",
}
func main() {
// Extension Mapping Tests
fmt.Println("\n=== Extension Mapping Tests ===")
test("Python extension maps correctly", func() error {
return assertEqual(extMap[".py"], "python")
})
test("JavaScript extensions map correctly", func() error {
if err := assertEqual(extMap[".js"], "javascript"); err != nil {
return err
}
return assertEqual(extMap[".ts"], "typescript")
})
test("Ruby extension maps correctly", func() error {
return assertEqual(extMap[".rb"], "ruby")
})
test("Go extension maps correctly", func() error {
return assertEqual(extMap[".go"], "go")
})
test("Rust extension maps correctly", func() error {
return assertEqual(extMap[".rs"], "rust")
})
test("C/C++ extensions map correctly", func() error {
if err := assertEqual(extMap[".c"], "c"); err != nil {
return err
}
if err := assertEqual(extMap[".cpp"], "cpp"); err != nil {
return err
}
if err := assertEqual(extMap[".cc"], "cpp"); err != nil {
return err
}
return assertEqual(extMap[".cxx"], "cpp")
})
test("JVM extensions map correctly", func() error {
if err := assertEqual(extMap[".java"], "java"); err != nil {
return err
}
if err := assertEqual(extMap[".kt"], "kotlin"); err != nil {
return err
}
return assertEqual(extMap[".groovy"], "groovy")
})
test("Functional language extensions map correctly", func() error {
if err := assertEqual(extMap[".hs"], "haskell"); err != nil {
return err
}
if err := assertEqual(extMap[".ml"], "ocaml"); err != nil {
return err
}
if err := assertEqual(extMap[".clj"], "clojure"); err != nil {
return err
}
return assertEqual(extMap[".erl"], "erlang")
})
// HMAC Signature Tests
fmt.Println("\n=== HMAC Signature Tests ===")
test("HMAC-SHA256 generates 64 character hex string", func() error {
secret := "test-secret-key"
message := "1234567890:POST:/execute:{}"
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(message))
signature := hex.EncodeToString(h.Sum(nil))
if len(signature) != 64 {
return fmt.Errorf("expected length 64 but got %d", len(signature))
}
return nil
})
test("Same input produces same signature", func() error {
secret := "test-secret-key"
message := "1234567890:POST:/execute:{}"
h1 := hmac.New(sha256.New, []byte(secret))
h1.Write([]byte(message))
sig1 := hex.EncodeToString(h1.Sum(nil))
h2 := hmac.New(sha256.New, []byte(secret))
h2.Write([]byte(message))
sig2 := hex.EncodeToString(h2.Sum(nil))
return assertEqual(sig1, sig2)
})
test("Different secrets produce different signatures", func() error {
message := "1234567890:POST:/execute:{}"
h1 := hmac.New(sha256.New, []byte("secret1"))
h1.Write([]byte(message))
sig1 := hex.EncodeToString(h1.Sum(nil))
h2 := hmac.New(sha256.New, []byte("secret2"))
h2.Write([]byte(message))
sig2 := hex.EncodeToString(h2.Sum(nil))
return assertNotEqual(sig1, sig2)
})
test("Different messages produce different signatures", func() error {
secret := "test-secret"
h1 := hmac.New(sha256.New, []byte(secret))
h1.Write([]byte("message1"))
sig1 := hex.EncodeToString(h1.Sum(nil))
h2 := hmac.New(sha256.New, []byte(secret))
h2.Write([]byte("message2"))
sig2 := hex.EncodeToString(h2.Sum(nil))
return assertNotEqual(sig1, sig2)
})
test("Signature format is timestamp:METHOD:path:body", func() error {
timestamp := "1704067200"
method := "POST"
endpoint := "/execute"
body := `{"language":"python","code":"print(1)"}`
message := fmt.Sprintf("%s:%s:%s:%s", timestamp, method, endpoint, body)
// Verify format: starts with timestamp, has method and path
if err := assertTrue(strings.HasPrefix(message, timestamp)); err != nil {
return err
}
if err := assertContains(message, ":POST:"); err != nil {
return err
}
return assertContains(message, ":/execute:")
})
// Language Detection Tests
fmt.Println("\n=== Language Detection Tests ===")
test("Detect language from .py extension", func() error {
filename := "script.py"
ext := strings.ToLower(filepath.Ext(filename))
return assertEqual(extMap[ext], "python")
})
test("Detect language from .go extension", func() error {
filename := "main.go"
ext := strings.ToLower(filepath.Ext(filename))
return assertEqual(extMap[ext], "go")
})
test("Python shebang detection", func() error {
content := "#!/usr/bin/env python3\nprint('hello')"
firstLine := strings.Split(content, "\n")[0]
if err := assertTrue(strings.HasPrefix(firstLine, "#!")); err != nil {
return err
}
return assertContains(firstLine, "python")
})
test("Bash shebang detection", func() error {
content := "#!/bin/bash\necho hello"
firstLine := strings.Split(content, "\n")[0]
if err := assertTrue(strings.HasPrefix(firstLine, "#!")); err != nil {
return err
}
return assertTrue(strings.Contains(firstLine, "bash") || strings.Contains(firstLine, "/sh"))
})
// Argument Parsing Tests
fmt.Println("\n=== Argument Parsing Tests ===")
test("Parse -e KEY=VALUE format", func() error {
arg := "DEBUG=1"
parts := strings.SplitN(arg, "=", 2)
key := parts[0]
value := parts[1]
if err := assertEqual(key, "DEBUG"); err != nil {
return err
}
return assertEqual(value, "1")
})
test("Parse -e KEY=VALUE with equals in value", func() error {
arg := "URL=https://example.com?foo=bar"
parts := strings.SplitN(arg, "=", 2)
key := parts[0]
value := parts[1]
if err := assertEqual(key, "URL"); err != nil {
return err
}
return assertEqual(value, "https://example.com?foo=bar")
})
test("Valid network modes", func() error {
validModes := map[string]bool{"zerotrust": true, "semitrusted": true}
if !validModes["zerotrust"] {
return fmt.Errorf("zerotrust should be valid")
}
if !validModes["semitrusted"] {
return fmt.Errorf("semitrusted should be valid")
}
if validModes["invalid"] {
return fmt.Errorf("invalid should not be valid")
}
return nil
})
test("Subcommand detection", func() error {
args := []string{"session", "--shell", "python3"}
subcommands := map[string]bool{"session": true, "service": true, "key": true, "restore": true}
var subcommand string
if len(args) > 0 && subcommands[args[0]] {
subcommand = args[0]
}
return assertEqual(subcommand, "session")
})
// File Operations Tests
fmt.Println("\n=== File Operations Tests ===")
test("Read text file", func() error {
tmpfile, err := os.CreateTemp("", "test_un_go_*.py")
if err != nil {
return err
}
defer os.Remove(tmpfile.Name())
content := "print('hello world')"
if _, err := tmpfile.WriteString(content); err != nil {
return err
}
tmpfile.Close()
data, err := os.ReadFile(tmpfile.Name())
if err != nil {
return err
}
return assertEqual(string(data), content)
})
test("Base64 encoding/decoding", func() error {
content := "print('hello world')"
encoded := base64.StdEncoding.EncodeToString([]byte(content))
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return err
}
return assertEqual(string(decoded), content)
})
test("Extract file basename", func() error {
path := "/home/user/project/script.py"
basename := filepath.Base(path)
return assertEqual(basename, "script.py")
})
test("Extract file extension", func() error {
path := "/home/user/project/script.py"
ext := filepath.Ext(path)
return assertEqual(ext, ".py")
})
// API Constants Tests
fmt.Println("\n=== API Constants Tests ===")
test("API base URL format", func() error {
apiBase := "https://api.unsandbox.com"
if err := assertTrue(strings.HasPrefix(apiBase, "https://")); err != nil {
return err
}
return assertContains(apiBase, "unsandbox.com")
})
test("Portal base URL format", func() error {
portalBase := "https://unsandbox.com"
return assertTrue(strings.HasPrefix(portalBase, "https://"))
})
// Summary
fmt.Println("\n=== Summary ===")
fmt.Printf("Passed: %d\n", passed)
fmt.Printf("Failed: %d\n", failed)
fmt.Printf("Total: %d\n", passed+failed)
if failed > 0 {
os.Exit(1)
}
}

View file

@ -0,0 +1,355 @@
#!/usr/bin/env node
/**
* Unit tests for un.js - tests internal functions without API calls
*/
const crypto = require('crypto');
const path = require('path');
const fs = require('fs');
const os = require('os');
const assert = require('assert');
// Test counters
let passed = 0;
let failed = 0;
function test(name, fn) {
try {
fn();
console.log(`${name}`);
passed++;
} catch (e) {
console.log(`${name}`);
console.log(` ${e.message}`);
failed++;
}
}
function assertEqual(actual, expected, msg = '') {
if (actual !== expected) {
throw new Error(`Expected "${expected}" but got "${actual}" ${msg}`);
}
}
function assertIncludes(str, substr) {
if (!str.includes(substr)) {
throw new Error(`Expected "${str}" to include "${substr}"`);
}
}
function assertNotEqual(a, b) {
if (a === b) {
throw new Error(`Expected values to be different but both were "${a}"`);
}
}
// ============================================================================
// Extension Mapping Tests
// ============================================================================
console.log('\n=== Extension Mapping Tests ===');
const EXT_MAP = {
".py": "python", ".js": "javascript", ".ts": "typescript",
".rb": "ruby", ".php": "php", ".pl": "perl", ".lua": "lua",
".sh": "bash", ".go": "go", ".rs": "rust", ".c": "c",
".cpp": "cpp", ".cc": "cpp", ".cxx": "cpp",
".java": "java", ".kt": "kotlin", ".cs": "csharp", ".fs": "fsharp",
".hs": "haskell", ".ml": "ocaml", ".clj": "clojure", ".scm": "scheme",
".lisp": "commonlisp", ".erl": "erlang", ".ex": "elixir", ".exs": "elixir",
".jl": "julia", ".r": "r", ".R": "r", ".cr": "crystal",
".d": "d", ".nim": "nim", ".zig": "zig", ".v": "v",
".dart": "dart", ".groovy": "groovy", ".scala": "scala",
".f90": "fortran", ".f95": "fortran", ".cob": "cobol",
".pro": "prolog", ".forth": "forth", ".4th": "forth",
".tcl": "tcl", ".raku": "raku", ".m": "objc",
};
test('Python extension maps correctly', () => {
assertEqual(EXT_MAP['.py'], 'python');
});
test('JavaScript extensions map correctly', () => {
assertEqual(EXT_MAP['.js'], 'javascript');
assertEqual(EXT_MAP['.ts'], 'typescript');
});
test('Ruby extension maps correctly', () => {
assertEqual(EXT_MAP['.rb'], 'ruby');
});
test('Go extension maps correctly', () => {
assertEqual(EXT_MAP['.go'], 'go');
});
test('Rust extension maps correctly', () => {
assertEqual(EXT_MAP['.rs'], 'rust');
});
test('C/C++ extensions map correctly', () => {
assertEqual(EXT_MAP['.c'], 'c');
assertEqual(EXT_MAP['.cpp'], 'cpp');
assertEqual(EXT_MAP['.cc'], 'cpp');
assertEqual(EXT_MAP['.cxx'], 'cpp');
});
test('JVM extensions map correctly', () => {
assertEqual(EXT_MAP['.java'], 'java');
assertEqual(EXT_MAP['.kt'], 'kotlin');
assertEqual(EXT_MAP['.groovy'], 'groovy');
assertEqual(EXT_MAP['.scala'], 'scala');
});
test('.NET extensions map correctly', () => {
assertEqual(EXT_MAP['.cs'], 'csharp');
assertEqual(EXT_MAP['.fs'], 'fsharp');
});
test('Functional language extensions map correctly', () => {
assertEqual(EXT_MAP['.hs'], 'haskell');
assertEqual(EXT_MAP['.ml'], 'ocaml');
assertEqual(EXT_MAP['.clj'], 'clojure');
assertEqual(EXT_MAP['.scm'], 'scheme');
assertEqual(EXT_MAP['.lisp'], 'commonlisp');
assertEqual(EXT_MAP['.erl'], 'erlang');
assertEqual(EXT_MAP['.ex'], 'elixir');
assertEqual(EXT_MAP['.exs'], 'elixir');
});
test('Scientific language extensions map correctly', () => {
assertEqual(EXT_MAP['.jl'], 'julia');
assertEqual(EXT_MAP['.r'], 'r');
assertEqual(EXT_MAP['.R'], 'r');
assertEqual(EXT_MAP['.f90'], 'fortran');
assertEqual(EXT_MAP['.f95'], 'fortran');
});
test('Systems language extensions map correctly', () => {
assertEqual(EXT_MAP['.d'], 'd');
assertEqual(EXT_MAP['.nim'], 'nim');
assertEqual(EXT_MAP['.zig'], 'zig');
assertEqual(EXT_MAP['.v'], 'v');
assertEqual(EXT_MAP['.cr'], 'crystal');
assertEqual(EXT_MAP['.dart'], 'dart');
});
test('Legacy/exotic extensions map correctly', () => {
assertEqual(EXT_MAP['.cob'], 'cobol');
assertEqual(EXT_MAP['.pro'], 'prolog');
assertEqual(EXT_MAP['.forth'], 'forth');
assertEqual(EXT_MAP['.4th'], 'forth');
assertEqual(EXT_MAP['.tcl'], 'tcl');
assertEqual(EXT_MAP['.raku'], 'raku');
assertEqual(EXT_MAP['.m'], 'objc');
});
// ============================================================================
// HMAC Signature Tests
// ============================================================================
console.log('\n=== HMAC Signature Tests ===');
test('HMAC-SHA256 generates 64 character hex string', () => {
const secret = 'test-secret-key';
const message = '1234567890:POST:/execute:{}';
const signature = crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
assertEqual(signature.length, 64);
});
test('Same input produces same signature', () => {
const secret = 'test-secret-key';
const message = '1234567890:POST:/execute:{}';
const sig1 = crypto.createHmac('sha256', secret).update(message).digest('hex');
const sig2 = crypto.createHmac('sha256', secret).update(message).digest('hex');
assertEqual(sig1, sig2);
});
test('Different secrets produce different signatures', () => {
const message = '1234567890:POST:/execute:{}';
const sig1 = crypto.createHmac('sha256', 'secret1').update(message).digest('hex');
const sig2 = crypto.createHmac('sha256', 'secret2').update(message).digest('hex');
assertNotEqual(sig1, sig2);
});
test('Different messages produce different signatures', () => {
const secret = 'test-secret';
const sig1 = crypto.createHmac('sha256', secret).update('message1').digest('hex');
const sig2 = crypto.createHmac('sha256', secret).update('message2').digest('hex');
assertNotEqual(sig1, sig2);
});
test('Signature format is timestamp:METHOD:path:body', () => {
const timestamp = '1704067200';
const method = 'POST';
const endpoint = '/execute';
const body = '{"language":"python","code":"print(1)"}';
const message = `${timestamp}:${method}:${endpoint}:${body}`;
// Verify format: starts with timestamp, has method and path
assertEqual(message.startsWith(timestamp), true);
assertIncludes(message, ':POST:');
assertIncludes(message, ':/execute:');
});
// ============================================================================
// Language Detection Tests
// ============================================================================
console.log('\n=== Language Detection Tests ===');
test('Detect language from .py extension', () => {
const filename = 'script.py';
const ext = path.extname(filename).toLowerCase();
assertEqual(EXT_MAP[ext], 'python');
});
test('Detect language from .js extension', () => {
const filename = 'app.js';
const ext = path.extname(filename).toLowerCase();
assertEqual(EXT_MAP[ext], 'javascript');
});
test('Python shebang detection', () => {
const content = '#!/usr/bin/env python3\nprint("hello")';
const firstLine = content.split('\n')[0];
assertEqual(firstLine.startsWith('#!'), true);
assertEqual(firstLine.includes('python'), true);
});
test('Node shebang detection', () => {
const content = '#!/usr/bin/env node\nconsole.log("hello")';
const firstLine = content.split('\n')[0];
assertEqual(firstLine.startsWith('#!'), true);
assertEqual(firstLine.includes('node'), true);
});
test('Bash shebang detection', () => {
const content = '#!/bin/bash\necho hello';
const firstLine = content.split('\n')[0];
assertEqual(firstLine.startsWith('#!'), true);
assertEqual(firstLine.includes('bash') || firstLine.includes('/sh'), true);
});
// ============================================================================
// Argument Parsing Tests
// ============================================================================
console.log('\n=== Argument Parsing Tests ===');
test('Parse -e KEY=VALUE format', () => {
const arg = 'DEBUG=1';
const [key, ...rest] = arg.split('=');
const value = rest.join('=');
assertEqual(key, 'DEBUG');
assertEqual(value, '1');
});
test('Parse -e KEY=VALUE with equals in value', () => {
const arg = 'URL=https://example.com?foo=bar';
const [key, ...rest] = arg.split('=');
const value = rest.join('=');
assertEqual(key, 'URL');
assertEqual(value, 'https://example.com?foo=bar');
});
test('Valid network modes', () => {
const validModes = ['zerotrust', 'semitrusted'];
assertEqual(validModes.includes('zerotrust'), true);
assertEqual(validModes.includes('semitrusted'), true);
assertEqual(validModes.includes('invalid'), false);
});
test('Subcommand detection', () => {
const args = ['session', '--shell', 'python3'];
const subcommands = ['session', 'service', 'key', 'restore'];
const subcommand = subcommands.includes(args[0]) ? args[0] : null;
assertEqual(subcommand, 'session');
});
// ============================================================================
// File Operations Tests
// ============================================================================
console.log('\n=== File Operations Tests ===');
test('Read text file', () => {
const tempFile = path.join(os.tmpdir(), 'test_un_js_' + Date.now() + '.py');
fs.writeFileSync(tempFile, 'print("hello world")');
try {
const content = fs.readFileSync(tempFile, 'utf-8');
assertEqual(content, 'print("hello world")');
} finally {
fs.unlinkSync(tempFile);
}
});
test('Base64 encoding/decoding', () => {
const content = 'print("hello world")';
const encoded = Buffer.from(content).toString('base64');
const decoded = Buffer.from(encoded, 'base64').toString();
assertEqual(decoded, content);
});
test('Extract file basename', () => {
const filepath = '/home/user/project/script.py';
const basename = path.basename(filepath);
assertEqual(basename, 'script.py');
});
test('Extract file extension', () => {
const filepath = '/home/user/project/script.py';
const ext = path.extname(filepath);
assertEqual(ext, '.py');
});
// ============================================================================
// API Constants Tests
// ============================================================================
console.log('\n=== API Constants Tests ===');
test('API base URL format', () => {
const API_BASE = 'https://api.unsandbox.com';
assertEqual(API_BASE.startsWith('https://'), true);
assertIncludes(API_BASE, 'unsandbox.com');
});
test('Portal base URL format', () => {
const PORTAL_BASE = 'https://unsandbox.com';
assertEqual(PORTAL_BASE.startsWith('https://'), true);
});
// ============================================================================
// Summary
// ============================================================================
console.log('\n=== Summary ===');
console.log(`Passed: ${passed}`);
console.log(`Failed: ${failed}`);
console.log(`Total: ${passed + failed}`);
process.exit(failed > 0 ? 1 : 0);

319
tests/unit/test_lua.lua Executable file
View file

@ -0,0 +1,319 @@
#!/usr/bin/env lua
-- Unit tests for un.lua - tests internal functions without API calls
local passed = 0
local failed = 0
local function test(name, fn)
local ok, err = pcall(fn)
if ok then
print("" .. name)
passed = passed + 1
else
print("" .. name)
print(" " .. tostring(err))
failed = failed + 1
end
end
local function assert_equal(actual, expected, msg)
if actual ~= expected then
error(string.format("Expected '%s' but got '%s' %s", tostring(expected), tostring(actual), msg or ""))
end
end
local function assert_not_equal(a, b)
if a == b then
error(string.format("Expected values to be different but both were '%s'", tostring(a)))
end
end
local function assert_contains(str, substr)
if not string.find(str, substr, 1, true) then
error(string.format("Expected '%s' to contain '%s'", str, substr))
end
end
local function assert_true(val)
if not val then
error("Expected true but got false")
end
end
-- Extension mapping (from un.lua)
local ext_map = {
[".py"] = "python", [".js"] = "javascript", [".ts"] = "typescript",
[".rb"] = "ruby", [".php"] = "php", [".pl"] = "perl", [".lua"] = "lua",
[".sh"] = "bash", [".go"] = "go", [".rs"] = "rust", [".c"] = "c",
[".cpp"] = "cpp", [".cc"] = "cpp", [".cxx"] = "cpp",
[".java"] = "java", [".kt"] = "kotlin", [".cs"] = "csharp", [".fs"] = "fsharp",
[".hs"] = "haskell", [".ml"] = "ocaml", [".clj"] = "clojure", [".scm"] = "scheme",
[".lisp"] = "commonlisp", [".erl"] = "erlang", [".ex"] = "elixir", [".exs"] = "elixir",
[".jl"] = "julia", [".r"] = "r", [".R"] = "r", [".cr"] = "crystal",
[".d"] = "d", [".nim"] = "nim", [".zig"] = "zig", [".v"] = "v",
[".dart"] = "dart", [".groovy"] = "groovy", [".scala"] = "scala",
[".f90"] = "fortran", [".f95"] = "fortran", [".cob"] = "cobol",
[".pro"] = "prolog", [".forth"] = "forth", [".4th"] = "forth",
[".tcl"] = "tcl", [".raku"] = "raku", [".m"] = "objc",
}
-- ============================================================================
-- Extension Mapping Tests
-- ============================================================================
print("\n=== Extension Mapping Tests ===")
test("Python extension maps correctly", function()
assert_equal(ext_map[".py"], "python")
end)
test("JavaScript extensions map correctly", function()
assert_equal(ext_map[".js"], "javascript")
assert_equal(ext_map[".ts"], "typescript")
end)
test("Ruby extension maps correctly", function()
assert_equal(ext_map[".rb"], "ruby")
end)
test("Go extension maps correctly", function()
assert_equal(ext_map[".go"], "go")
end)
test("Rust extension maps correctly", function()
assert_equal(ext_map[".rs"], "rust")
end)
test("C/C++ extensions map correctly", function()
assert_equal(ext_map[".c"], "c")
assert_equal(ext_map[".cpp"], "cpp")
assert_equal(ext_map[".cc"], "cpp")
assert_equal(ext_map[".cxx"], "cpp")
end)
test("Lua extension maps correctly", function()
assert_equal(ext_map[".lua"], "lua")
end)
test("JVM extensions map correctly", function()
assert_equal(ext_map[".java"], "java")
assert_equal(ext_map[".kt"], "kotlin")
assert_equal(ext_map[".groovy"], "groovy")
end)
test("Functional language extensions map correctly", function()
assert_equal(ext_map[".hs"], "haskell")
assert_equal(ext_map[".ml"], "ocaml")
assert_equal(ext_map[".clj"], "clojure")
assert_equal(ext_map[".erl"], "erlang")
end)
-- ============================================================================
-- HMAC Signature Tests (using openssl via shell)
-- ============================================================================
print("\n=== HMAC Signature Tests ===")
local function hmac_sha256(secret, message)
local cmd = string.format("echo -n '%s' | openssl dgst -sha256 -hmac '%s' 2>/dev/null | sed 's/^.* //'",
message:gsub("'", "'\\''"), secret:gsub("'", "'\\''"))
local handle = io.popen(cmd)
if handle then
local result = handle:read("*a"):gsub("%s+$", "")
handle:close()
return result
end
return nil
end
test("HMAC-SHA256 generates 64 character hex string", function()
local sig = hmac_sha256("test-secret", "test-message")
if sig then
assert_equal(#sig, 64)
end
end)
test("Same input produces same signature", function()
local sig1 = hmac_sha256("key", "message")
local sig2 = hmac_sha256("key", "message")
if sig1 and sig2 then
assert_equal(sig1, sig2)
end
end)
test("Different secrets produce different signatures", function()
local sig1 = hmac_sha256("key1", "message")
local sig2 = hmac_sha256("key2", "message")
if sig1 and sig2 then
assert_not_equal(sig1, sig2)
end
end)
test("Different messages produce different signatures", function()
local sig1 = hmac_sha256("key", "message1")
local sig2 = hmac_sha256("key", "message2")
if sig1 and sig2 then
assert_not_equal(sig1, sig2)
end
end)
test("Signature format is timestamp:METHOD:path:body", function()
local timestamp = "1704067200"
local method = "POST"
local endpoint = "/execute"
local body = '{"language":"python","code":"print(1)"}'
local message = timestamp .. ":" .. method .. ":" .. endpoint .. ":" .. body
assert_contains(message, ":")
local count = 0
for _ in message:gmatch(":") do count = count + 1 end
assert_equal(count, 3)
assert_true(message:sub(1, #timestamp) == timestamp)
end)
-- ============================================================================
-- Language Detection Tests
-- ============================================================================
print("\n=== Language Detection Tests ===")
local function get_extension(filename)
return filename:match("%.([^%.]+)$")
end
local function detect_from_shebang(first_line)
if first_line:sub(1, 2) == "#!" then
if first_line:find("python") then return "python" end
if first_line:find("node") then return "javascript" end
if first_line:find("ruby") then return "ruby" end
if first_line:find("perl") then return "perl" end
if first_line:find("bash") or first_line:find("/sh") then return "bash" end
if first_line:find("lua") then return "lua" end
if first_line:find("php") then return "php" end
end
return nil
end
test("Detect language from .py extension", function()
local ext = "." .. get_extension("script.py")
assert_equal(ext_map[ext], "python")
end)
test("Detect language from .lua extension", function()
local ext = "." .. get_extension("script.lua")
assert_equal(ext_map[ext], "lua")
end)
test("Python shebang detection", function()
assert_equal(detect_from_shebang("#!/usr/bin/env python3"), "python")
end)
test("Node shebang detection", function()
assert_equal(detect_from_shebang("#!/usr/bin/env node"), "javascript")
end)
test("Lua shebang detection", function()
assert_equal(detect_from_shebang("#!/usr/bin/env lua"), "lua")
end)
test("Bash shebang detection", function()
assert_equal(detect_from_shebang("#!/bin/bash"), "bash")
end)
-- ============================================================================
-- Argument Parsing Tests
-- ============================================================================
print("\n=== Argument Parsing Tests ===")
local function parse_env_var(arg)
local key, value = arg:match("^([^=]+)=(.*)$")
return key, value
end
test("Parse -e KEY=VALUE format", function()
local key, value = parse_env_var("DEBUG=1")
assert_equal(key, "DEBUG")
assert_equal(value, "1")
end)
test("Parse -e KEY=VALUE with equals in value", function()
local key, value = parse_env_var("URL=https://example.com?foo=bar")
assert_equal(key, "URL")
assert_equal(value, "https://example.com?foo=bar")
end)
test("Valid network modes", function()
local valid_modes = { zerotrust = true, semitrusted = true }
assert_true(valid_modes["zerotrust"])
assert_true(valid_modes["semitrusted"])
assert_true(not valid_modes["invalid"])
end)
test("Subcommand detection", function()
local subcommands = { session = true, service = true, key = true, restore = true }
assert_true(subcommands["session"])
assert_true(subcommands["service"])
assert_true(not subcommands["script.py"])
end)
-- ============================================================================
-- File Operations Tests
-- ============================================================================
print("\n=== File Operations Tests ===")
test("Read text file", function()
local tmpname = os.tmpname() .. ".py"
local f = io.open(tmpname, "w")
f:write('print("hello world")')
f:close()
f = io.open(tmpname, "r")
local content = f:read("*a")
f:close()
os.remove(tmpname)
assert_equal(content, 'print("hello world")')
end)
test("Extract file basename", function()
local path = "/home/user/project/script.py"
local basename = path:match("([^/]+)$")
assert_equal(basename, "script.py")
end)
test("Extract file extension", function()
local path = "/home/user/project/script.py"
local ext = path:match("%.([^%.]+)$")
assert_equal(ext, "py")
end)
-- ============================================================================
-- API Constants Tests
-- ============================================================================
print("\n=== API Constants Tests ===")
test("API base URL format", function()
local api_base = "https://api.unsandbox.com"
assert_true(api_base:sub(1, 8) == "https://")
assert_contains(api_base, "unsandbox.com")
end)
test("Portal base URL format", function()
local portal_base = "https://unsandbox.com"
assert_true(portal_base:sub(1, 8) == "https://")
end)
-- ============================================================================
-- Summary
-- ============================================================================
print("\n=== Summary ===")
print("Passed: " .. passed)
print("Failed: " .. failed)
print("Total: " .. (passed + failed))
os.exit(failed > 0 and 1 or 0)

294
tests/unit/test_python.py Executable file
View file

@ -0,0 +1,294 @@
#!/usr/bin/env python3
"""
Unit tests for un.py - tests internal functions without API calls
"""
import sys
import os
import hmac
import hashlib
import tempfile
import unittest
# Add parent directory to path to import un module functions
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
# We need to mock the API key check before importing
os.environ['UNSANDBOX_PUBLIC_KEY'] = 'test-public-key'
os.environ['UNSANDBOX_SECRET_KEY'] = 'test-secret-key'
class TestExtensionMapping(unittest.TestCase):
"""Test the EXT_MAP extension to language mapping"""
def setUp(self):
# Import here after env is set
import importlib.util
spec = importlib.util.spec_from_file_location("un", os.path.join(os.path.dirname(__file__), '../../un.py'))
self.un = importlib.util.module_from_spec(spec)
# Don't execute the module, just load the constants
with open(os.path.join(os.path.dirname(__file__), '../../un.py')) as f:
content = f.read()
# Extract EXT_MAP
exec(compile(content.split('def get_api_keys')[0], 'un.py', 'exec'), self.un.__dict__)
def test_python_extensions(self):
self.assertEqual(self.un.EXT_MAP['.py'], 'python')
def test_javascript_extensions(self):
self.assertEqual(self.un.EXT_MAP['.js'], 'javascript')
self.assertEqual(self.un.EXT_MAP['.ts'], 'typescript')
def test_ruby_extension(self):
self.assertEqual(self.un.EXT_MAP['.rb'], 'ruby')
def test_go_extension(self):
self.assertEqual(self.un.EXT_MAP['.go'], 'go')
def test_rust_extension(self):
self.assertEqual(self.un.EXT_MAP['.rs'], 'rust')
def test_c_extensions(self):
self.assertEqual(self.un.EXT_MAP['.c'], 'c')
self.assertEqual(self.un.EXT_MAP['.cpp'], 'cpp')
self.assertEqual(self.un.EXT_MAP['.cc'], 'cpp')
self.assertEqual(self.un.EXT_MAP['.cxx'], 'cpp')
def test_jvm_extensions(self):
self.assertEqual(self.un.EXT_MAP['.java'], 'java')
self.assertEqual(self.un.EXT_MAP['.kt'], 'kotlin')
self.assertEqual(self.un.EXT_MAP['.groovy'], 'groovy')
self.assertEqual(self.un.EXT_MAP['.scala'], 'scala')
def test_dotnet_extensions(self):
self.assertEqual(self.un.EXT_MAP['.cs'], 'csharp')
self.assertEqual(self.un.EXT_MAP['.fs'], 'fsharp')
def test_functional_extensions(self):
self.assertEqual(self.un.EXT_MAP['.hs'], 'haskell')
self.assertEqual(self.un.EXT_MAP['.ml'], 'ocaml')
self.assertEqual(self.un.EXT_MAP['.clj'], 'clojure')
self.assertEqual(self.un.EXT_MAP['.scm'], 'scheme')
self.assertEqual(self.un.EXT_MAP['.lisp'], 'commonlisp')
self.assertEqual(self.un.EXT_MAP['.erl'], 'erlang')
self.assertEqual(self.un.EXT_MAP['.ex'], 'elixir')
self.assertEqual(self.un.EXT_MAP['.exs'], 'elixir')
def test_scientific_extensions(self):
self.assertEqual(self.un.EXT_MAP['.jl'], 'julia')
self.assertEqual(self.un.EXT_MAP['.r'], 'r')
self.assertEqual(self.un.EXT_MAP['.R'], 'r')
self.assertEqual(self.un.EXT_MAP['.f90'], 'fortran')
self.assertEqual(self.un.EXT_MAP['.f95'], 'fortran')
def test_exotic_extensions(self):
self.assertEqual(self.un.EXT_MAP['.d'], 'd')
self.assertEqual(self.un.EXT_MAP['.nim'], 'nim')
self.assertEqual(self.un.EXT_MAP['.zig'], 'zig')
self.assertEqual(self.un.EXT_MAP['.v'], 'v')
self.assertEqual(self.un.EXT_MAP['.cr'], 'crystal')
self.assertEqual(self.un.EXT_MAP['.dart'], 'dart')
def test_legacy_extensions(self):
self.assertEqual(self.un.EXT_MAP['.cob'], 'cobol')
self.assertEqual(self.un.EXT_MAP['.pro'], 'prolog')
self.assertEqual(self.un.EXT_MAP['.forth'], 'forth')
self.assertEqual(self.un.EXT_MAP['.4th'], 'forth')
def test_other_extensions(self):
self.assertEqual(self.un.EXT_MAP['.tcl'], 'tcl')
self.assertEqual(self.un.EXT_MAP['.raku'], 'raku')
self.assertEqual(self.un.EXT_MAP['.m'], 'objc')
self.assertEqual(self.un.EXT_MAP['.lua'], 'lua')
self.assertEqual(self.un.EXT_MAP['.pl'], 'perl')
self.assertEqual(self.un.EXT_MAP['.php'], 'php')
self.assertEqual(self.un.EXT_MAP['.sh'], 'bash')
class TestHMACSignature(unittest.TestCase):
"""Test HMAC signature generation"""
def test_hmac_sha256_basic(self):
"""Test basic HMAC-SHA256 generation"""
secret = "test-secret-key"
message = "1234567890:POST:/execute:{}"
expected = hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
# Verify we can generate the same signature
actual = hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
self.assertEqual(expected, actual)
self.assertEqual(len(actual), 64) # SHA256 hex is 64 chars
def test_hmac_different_secrets(self):
"""Test that different secrets produce different signatures"""
message = "1234567890:POST:/execute:{}"
sig1 = hmac.new(b"secret1", message.encode(), hashlib.sha256).hexdigest()
sig2 = hmac.new(b"secret2", message.encode(), hashlib.sha256).hexdigest()
self.assertNotEqual(sig1, sig2)
def test_hmac_different_messages(self):
"""Test that different messages produce different signatures"""
secret = b"test-secret"
sig1 = hmac.new(secret, b"message1", hashlib.sha256).hexdigest()
sig2 = hmac.new(secret, b"message2", hashlib.sha256).hexdigest()
self.assertNotEqual(sig1, sig2)
def test_signature_format(self):
"""Test the signature input format: timestamp:METHOD:path:body"""
timestamp = "1704067200"
method = "POST"
path = "/execute"
body = '{"language":"python","code":"print(1)"}'
message = f"{timestamp}:{method}:{path}:{body}"
# Verify format: starts with timestamp, has method and path
self.assertTrue(message.startswith(timestamp))
self.assertIn(":POST:", message)
self.assertIn(":/execute:", message)
class TestLanguageDetection(unittest.TestCase):
"""Test language detection from file extensions and shebangs"""
def test_detect_from_extension(self):
"""Test detection from file extension"""
# Create a temp file with .py extension
with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as f:
f.write(b'print("hello")')
temp_path = f.name
try:
ext = os.path.splitext(temp_path)[1].lower()
# Simulate EXT_MAP lookup
EXT_MAP = {'.py': 'python', '.js': 'javascript', '.rb': 'ruby'}
self.assertEqual(EXT_MAP.get(ext), 'python')
finally:
os.unlink(temp_path)
def test_detect_from_shebang_python(self):
"""Test detection from python shebang"""
content = "#!/usr/bin/env python3\nprint('hello')"
first_line = content.split('\n')[0]
self.assertTrue(first_line.startswith('#!'))
self.assertIn('python', first_line)
def test_detect_from_shebang_node(self):
"""Test detection from node shebang"""
content = "#!/usr/bin/env node\nconsole.log('hello')"
first_line = content.split('\n')[0]
self.assertTrue(first_line.startswith('#!'))
self.assertIn('node', first_line)
def test_detect_from_shebang_bash(self):
"""Test detection from bash shebang"""
content = "#!/bin/bash\necho hello"
first_line = content.split('\n')[0]
self.assertTrue(first_line.startswith('#!'))
self.assertTrue('bash' in first_line or '/sh' in first_line)
class TestArgumentParsing(unittest.TestCase):
"""Test command-line argument patterns"""
def test_env_var_format(self):
"""Test -e KEY=VALUE parsing"""
arg = "DEBUG=1"
key, value = arg.split('=', 1)
self.assertEqual(key, "DEBUG")
self.assertEqual(value, "1")
def test_env_var_with_equals_in_value(self):
"""Test -e KEY=VALUE=WITH=EQUALS parsing"""
arg = "URL=https://example.com?foo=bar"
key, value = arg.split('=', 1)
self.assertEqual(key, "URL")
self.assertEqual(value, "https://example.com?foo=bar")
def test_network_mode_values(self):
"""Test valid network mode values"""
valid_modes = ['zerotrust', 'semitrusted']
self.assertIn('zerotrust', valid_modes)
self.assertIn('semitrusted', valid_modes)
def test_subcommand_detection(self):
"""Test subcommand detection"""
args = ['session', '--shell', 'python3']
subcommand = args[0] if args and args[0] in ['session', 'service', 'key', 'restore'] else None
self.assertEqual(subcommand, 'session')
class TestFileOperations(unittest.TestCase):
"""Test file reading and encoding"""
def test_read_text_file(self):
"""Test reading a text file"""
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
f.write('print("hello world")')
temp_path = f.name
try:
with open(temp_path, 'r') as f:
content = f.read()
self.assertEqual(content, 'print("hello world")')
finally:
os.unlink(temp_path)
def test_base64_encoding(self):
"""Test base64 encoding for file contents"""
import base64
content = b'print("hello world")'
encoded = base64.b64encode(content).decode('utf-8')
decoded = base64.b64decode(encoded)
self.assertEqual(decoded, content)
def test_file_basename(self):
"""Test extracting file basename"""
path = "/home/user/project/script.py"
basename = os.path.basename(path)
self.assertEqual(basename, "script.py")
class TestAPIConstants(unittest.TestCase):
"""Test API-related constants"""
def test_api_base_url(self):
"""Test API base URL format"""
API_BASE = "https://api.unsandbox.com"
self.assertTrue(API_BASE.startswith('https://'))
self.assertIn('unsandbox.com', API_BASE)
def test_portal_base_url(self):
"""Test portal base URL format"""
PORTAL_BASE = "https://unsandbox.com"
self.assertTrue(PORTAL_BASE.startswith('https://'))
if __name__ == '__main__':
unittest.main(verbosity=2)

334
tests/unit/test_ruby.rb Executable file
View file

@ -0,0 +1,334 @@
#!/usr/bin/env ruby
# Unit tests for un.rb - tests internal functions without API calls
require 'openssl'
require 'base64'
require 'tempfile'
require 'fileutils'
# Test counters
$passed = 0
$failed = 0
def test(name)
begin
yield
puts "#{name}"
$passed += 1
rescue => e
puts "#{name}"
puts " #{e.message}"
$failed += 1
end
end
def assert_equal(actual, expected, msg = '')
raise "Expected '#{expected}' but got '#{actual}' #{msg}" unless actual == expected
end
def assert_not_equal(a, b)
raise "Expected values to be different but both were '#{a}'" if a == b
end
def assert_includes(str, substr)
raise "Expected '#{str}' to include '#{substr}'" unless str.include?(substr)
end
def assert_true(val)
raise "Expected true but got #{val}" unless val
end
# Extension mapping (copied from un.rb)
EXT_MAP = {
'.py' => 'python', '.js' => 'javascript', '.ts' => 'typescript',
'.rb' => 'ruby', '.php' => 'php', '.pl' => 'perl', '.lua' => 'lua',
'.sh' => 'bash', '.go' => 'go', '.rs' => 'rust', '.c' => 'c',
'.cpp' => 'cpp', '.cc' => 'cpp', '.cxx' => 'cpp',
'.java' => 'java', '.kt' => 'kotlin', '.cs' => 'csharp', '.fs' => 'fsharp',
'.hs' => 'haskell', '.ml' => 'ocaml', '.clj' => 'clojure', '.scm' => 'scheme',
'.lisp' => 'commonlisp', '.erl' => 'erlang', '.ex' => 'elixir', '.exs' => 'elixir',
'.jl' => 'julia', '.r' => 'r', '.R' => 'r', '.cr' => 'crystal',
'.d' => 'd', '.nim' => 'nim', '.zig' => 'zig', '.v' => 'v',
'.dart' => 'dart', '.groovy' => 'groovy', '.scala' => 'scala',
'.f90' => 'fortran', '.f95' => 'fortran', '.cob' => 'cobol',
'.pro' => 'prolog', '.forth' => 'forth', '.4th' => 'forth',
'.tcl' => 'tcl', '.raku' => 'raku', '.m' => 'objc'
}.freeze
# ============================================================================
# Extension Mapping Tests
# ============================================================================
puts "\n=== Extension Mapping Tests ==="
test('Python extension maps correctly') do
assert_equal EXT_MAP['.py'], 'python'
end
test('JavaScript extensions map correctly') do
assert_equal EXT_MAP['.js'], 'javascript'
assert_equal EXT_MAP['.ts'], 'typescript'
end
test('Ruby extension maps correctly') do
assert_equal EXT_MAP['.rb'], 'ruby'
end
test('Go extension maps correctly') do
assert_equal EXT_MAP['.go'], 'go'
end
test('Rust extension maps correctly') do
assert_equal EXT_MAP['.rs'], 'rust'
end
test('C/C++ extensions map correctly') do
assert_equal EXT_MAP['.c'], 'c'
assert_equal EXT_MAP['.cpp'], 'cpp'
assert_equal EXT_MAP['.cc'], 'cpp'
assert_equal EXT_MAP['.cxx'], 'cpp'
end
test('JVM extensions map correctly') do
assert_equal EXT_MAP['.java'], 'java'
assert_equal EXT_MAP['.kt'], 'kotlin'
assert_equal EXT_MAP['.groovy'], 'groovy'
assert_equal EXT_MAP['.scala'], 'scala'
end
test('.NET extensions map correctly') do
assert_equal EXT_MAP['.cs'], 'csharp'
assert_equal EXT_MAP['.fs'], 'fsharp'
end
test('Functional language extensions map correctly') do
assert_equal EXT_MAP['.hs'], 'haskell'
assert_equal EXT_MAP['.ml'], 'ocaml'
assert_equal EXT_MAP['.clj'], 'clojure'
assert_equal EXT_MAP['.scm'], 'scheme'
assert_equal EXT_MAP['.lisp'], 'commonlisp'
assert_equal EXT_MAP['.erl'], 'erlang'
assert_equal EXT_MAP['.ex'], 'elixir'
end
test('Scientific extensions map correctly') do
assert_equal EXT_MAP['.jl'], 'julia'
assert_equal EXT_MAP['.r'], 'r'
assert_equal EXT_MAP['.f90'], 'fortran'
end
test('Exotic extensions map correctly') do
assert_equal EXT_MAP['.d'], 'd'
assert_equal EXT_MAP['.nim'], 'nim'
assert_equal EXT_MAP['.zig'], 'zig'
assert_equal EXT_MAP['.v'], 'v'
assert_equal EXT_MAP['.cr'], 'crystal'
end
# ============================================================================
# HMAC Signature Tests
# ============================================================================
puts "\n=== HMAC Signature Tests ==="
test('HMAC-SHA256 generates 64 character hex string') do
secret = 'test-secret-key'
message = '1234567890:POST:/execute:{}'
signature = OpenSSL::HMAC.hexdigest('SHA256', secret, message)
assert_equal signature.length, 64
end
test('Same input produces same signature') do
secret = 'test-secret-key'
message = '1234567890:POST:/execute:{}'
sig1 = OpenSSL::HMAC.hexdigest('SHA256', secret, message)
sig2 = OpenSSL::HMAC.hexdigest('SHA256', secret, message)
assert_equal sig1, sig2
end
test('Different secrets produce different signatures') do
message = '1234567890:POST:/execute:{}'
sig1 = OpenSSL::HMAC.hexdigest('SHA256', 'secret1', message)
sig2 = OpenSSL::HMAC.hexdigest('SHA256', 'secret2', message)
assert_not_equal sig1, sig2
end
test('Different messages produce different signatures') do
secret = 'test-secret'
sig1 = OpenSSL::HMAC.hexdigest('SHA256', secret, 'message1')
sig2 = OpenSSL::HMAC.hexdigest('SHA256', secret, 'message2')
assert_not_equal sig1, sig2
end
test('Signature format is timestamp:METHOD:path:body') do
timestamp = '1704067200'
method = 'POST'
endpoint = '/execute'
body = '{"language":"python","code":"print(1)"}'
message = "#{timestamp}:#{method}:#{endpoint}:#{body}"
# Verify format: starts with timestamp, has method and path
assert_true message.start_with?(timestamp)
assert_includes message, ':POST:'
assert_includes message, ':/execute:'
end
# ============================================================================
# Language Detection Tests
# ============================================================================
puts "\n=== Language Detection Tests ==="
test('Detect language from .py extension') do
filename = 'script.py'
ext = File.extname(filename).downcase
assert_equal EXT_MAP[ext], 'python'
end
test('Detect language from .rb extension') do
filename = 'app.rb'
ext = File.extname(filename).downcase
assert_equal EXT_MAP[ext], 'ruby'
end
test('Python shebang detection') do
content = "#!/usr/bin/env python3\nprint('hello')"
first_line = content.lines.first
assert_true first_line.start_with?('#!')
assert_includes first_line, 'python'
end
test('Ruby shebang detection') do
content = "#!/usr/bin/env ruby\nputs 'hello'"
first_line = content.lines.first
assert_true first_line.start_with?('#!')
assert_includes first_line, 'ruby'
end
test('Bash shebang detection') do
content = "#!/bin/bash\necho hello"
first_line = content.lines.first
assert_true first_line.start_with?('#!')
assert_true first_line.include?('bash') || first_line.include?('/sh')
end
# ============================================================================
# Argument Parsing Tests
# ============================================================================
puts "\n=== Argument Parsing Tests ==="
test('Parse -e KEY=VALUE format') do
arg = 'DEBUG=1'
key, value = arg.split('=', 2)
assert_equal key, 'DEBUG'
assert_equal value, '1'
end
test('Parse -e KEY=VALUE with equals in value') do
arg = 'URL=https://example.com?foo=bar'
key, value = arg.split('=', 2)
assert_equal key, 'URL'
assert_equal value, 'https://example.com?foo=bar'
end
test('Valid network modes') do
valid_modes = %w[zerotrust semitrusted]
assert_true valid_modes.include?('zerotrust')
assert_true valid_modes.include?('semitrusted')
assert_true !valid_modes.include?('invalid')
end
test('Subcommand detection') do
args = %w[session --shell python3]
subcommands = %w[session service key restore]
subcommand = subcommands.include?(args[0]) ? args[0] : nil
assert_equal subcommand, 'session'
end
# ============================================================================
# File Operations Tests
# ============================================================================
puts "\n=== File Operations Tests ==="
test('Read text file') do
file = Tempfile.new(['test', '.py'])
file.write('print("hello world")')
file.close
begin
content = File.read(file.path)
assert_equal content, 'print("hello world")'
ensure
file.unlink
end
end
test('Base64 encoding/decoding') do
content = 'print("hello world")'
encoded = Base64.strict_encode64(content)
decoded = Base64.strict_decode64(encoded)
assert_equal decoded, content
end
test('Extract file basename') do
filepath = '/home/user/project/script.py'
basename = File.basename(filepath)
assert_equal basename, 'script.py'
end
test('Extract file extension') do
filepath = '/home/user/project/script.py'
ext = File.extname(filepath)
assert_equal ext, '.py'
end
# ============================================================================
# API Constants Tests
# ============================================================================
puts "\n=== API Constants Tests ==="
test('API base URL format') do
api_base = 'https://api.unsandbox.com'
assert_true api_base.start_with?('https://')
assert_includes api_base, 'unsandbox.com'
end
test('Portal base URL format') do
portal_base = 'https://unsandbox.com'
assert_true portal_base.start_with?('https://')
end
# ============================================================================
# Summary
# ============================================================================
puts "\n=== Summary ==="
puts "Passed: #{$passed}"
puts "Failed: #{$failed}"
puts "Total: #{$passed + $failed}"
exit($failed > 0 ? 1 : 0)