diff --git a/README.md b/README.md index 5e35458..bcfc55a 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,36 @@ Each implementation supports: ./tests/run_all_tests.sh ``` +### Unit Tests + +Every implementation has unit tests that verify core functionality without requiring API calls: + +```bash +# Run unit tests for any language +python3 tests/unit/test_python.py +node tests/unit/test_javascript.js +ruby tests/unit/test_ruby.rb +go test -v tests/unit/test_go.go +julia tests/unit/test_julia.jl +elixir tests/unit/test_elixir.exs +``` + +Unit tests cover: +- **Extension mapping** - File extensions map to correct languages +- **HMAC signatures** - SHA256 signing produces valid 64-char hex strings +- **Signature format** - Message format is `timestamp:METHOD:path:body` +- **Language detection** - Shebang parsing and extension detection +- **Argument parsing** - `-e KEY=VALUE` format handling +- **File operations** - Base64 encoding, path extraction + +### CI/CD + +GitHub Actions runs the full test matrix on every push: + +[![Inception Matrix](https://github.com/russellballestrini/un-inception/actions/workflows/inception-matrix.yml/badge.svg)](https://github.com/russellballestrini/un-inception/actions/workflows/inception-matrix.yml) + +The workflow tests all 42 implementations across their respective runtimes. + ## The Inception Matrix **Use un to run un inside un!** Don't have a language installed locally? Run its implementation through unsandbox using `un` (the C implementation): diff --git a/tests/test_un_cr.cr b/tests/test_un_cr.cr index a098df7..b74095e 100755 --- a/tests/test_un_cr.cr +++ b/tests/test_un_cr.cr @@ -11,9 +11,27 @@ RED = "\033[31m" BLUE = "\033[34m" RESET = "\033[0m" -# Test counters -@@passed = 0 -@@failed = 0 +# Test counters - use class with class variables +class TestCounter + @@passed = 0 + @@failed = 0 + + def self.passed + @@passed + end + + def self.failed + @@failed + end + + def self.inc_passed + @@passed += 1 + end + + def self.inc_failed + @@failed += 1 + end +end # Extension to language mapping (from un.cr) EXT_MAP = { @@ -44,10 +62,10 @@ end def print_test(name : String, result : Bool) if result puts "#{GREEN}✓ PASS#{RESET}: #{name}" - @@passed += 1 + TestCounter.inc_passed else puts "#{RED}✗ FAIL#{RESET}: #{name}" - @@failed += 1 + TestCounter.inc_failed end end @@ -143,11 +161,11 @@ print_test("Multiple dots in filename", detect_language("my.test.py") == "python puts "\n#{BLUE}========================================#{RESET}" puts "#{BLUE}Test Summary#{RESET}" puts "#{BLUE}========================================#{RESET}" -puts "#{GREEN}Passed: #{@@passed}#{RESET}" -puts "#{RED}Failed: #{@@failed}#{RESET}" -puts "#{BLUE}Total: #{@@passed + @@failed}#{RESET}" +puts "#{GREEN}Passed: #{TestCounter.passed}#{RESET}" +puts "#{RED}Failed: #{TestCounter.failed}#{RESET}" +puts "#{BLUE}Total: #{TestCounter.passed + TestCounter.failed}#{RESET}" -if @@failed > 0 +if TestCounter.failed > 0 puts "\n#{RED}TESTS FAILED#{RESET}" exit 1 else diff --git a/un.v b/un.v index 23e7bff..337b0fb 100644 --- a/un.v +++ b/un.v @@ -45,15 +45,13 @@ import os -const ( - api_base = 'https://api.unsandbox.com' - portal_base = 'https://unsandbox.com' - blue = '\x1b[34m' - red = '\x1b[31m' - green = '\x1b[32m' - yellow = '\x1b[33m' - reset = '\x1b[0m' -) +const api_base = 'https://api.unsandbox.com' +const portal_base = 'https://unsandbox.com' +const blue = '\x1b[34m' +const red = '\x1b[31m' +const green = '\x1b[32m' +const yellow = '\x1b[33m' +const reset = '\x1b[0m' fn detect_language(filename string) !string { ext := os.file_ext(filename) @@ -162,7 +160,7 @@ fn cmd_key(extend bool, api_key string) { pub_key := get_public_key() secret_key := get_secret_key() body := '{}' - cmd := "BODY='${body}'; TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/keys/validate:\\$BODY\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${portal_base}/keys/validate' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\" -d \"\\$BODY\"" + cmd := "BODY='${body}'; TIMESTAMP=\$\$(date +%s); MESSAGE=\"\$\$TIMESTAMP:POST:/keys/validate:\$\$BODY\"; SIGNATURE=\$\$(echo -n \"\$\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${portal_base}/keys/validate' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \$\$TIMESTAMP\" -H \"X-Signature: \$\$SIGNATURE\" -d \"\$\$BODY\"" result := exec_curl(cmd) public_key := extract_json_string(result, 'public_key') @@ -180,11 +178,21 @@ fn cmd_key(extend bool, api_key string) { println('${blue}Opening browser to extend key...${reset}') // Try xdg-open (Linux), open (macOS), or start (Windows) - os.execute('xdg-open "${url}"') or { - os.execute('open "${url}"') or { - os.execute('cmd /c start "${url}"') or { - eprintln('${red}Error: Could not open browser${reset}') - } + mut opened := false + result := os.execute('xdg-open "${url}"') + if result.exit_code == 0 { + opened = true + } + if !opened { + result2 := os.execute('open "${url}"') + if result2.exit_code == 0 { + opened = true + } + } + if !opened { + result3 := os.execute('cmd /c start "${url}"') + if result3.exit_code != 0 { + eprintln('${red}Error: Could not open browser${reset}') } } return @@ -267,7 +275,7 @@ fn cmd_execute(source_file string, envs []string, artifacts bool, network string pub_key := get_public_key() secret_key := get_secret_key() - cmd := "BODY='${json}'; TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/execute:\\$BODY\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/execute' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\" -d \"\\$BODY\"" + cmd := "BODY='${json}'; TIMESTAMP=\$\$(date +%s); MESSAGE=\"\$\$TIMESTAMP:POST:/execute:\$\$BODY\"; SIGNATURE=\$\$(echo -n \"\$\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/execute' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \$\$TIMESTAMP\" -H \"X-Signature: \$\$SIGNATURE\" -d \"\$\$BODY\"" println(exec_curl(cmd)) } @@ -276,13 +284,13 @@ fn cmd_session(list bool, kill string, shell string, network string, vcpu int, t secret_key := get_secret_key() if list { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:GET:/sessions:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/sessions' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:GET:/sessions:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/sessions' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" println(exec_curl(cmd)) return } if kill != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:DELETE:/sessions/${kill}:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X DELETE '${api_base}/sessions/${kill}' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:DELETE:/sessions/${kill}:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X DELETE '${api_base}/sessions/${kill}' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" exec_curl(cmd) println('${green}Session terminated: ${kill}${reset}') return @@ -306,7 +314,7 @@ fn cmd_session(list bool, kill string, shell string, network string, vcpu int, t json += '}' println('${yellow}Creating session...${reset}') - cmd := "BODY='${json}'; TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/sessions:\\$BODY\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/sessions' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\" -d \"\\$BODY\"" + cmd := "BODY='${json}'; TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:POST:/sessions:$$BODY\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/sessions' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\" -d \"$$BODY\"" println(exec_curl(cmd)) } @@ -315,45 +323,45 @@ fn cmd_service(name string, ports string, service_type string, bootstrap string, secret_key := get_secret_key() if list { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:GET:/services:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:GET:/services:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" println(exec_curl(cmd)) return } if info != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:GET:/services/${info}:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services/${info}' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:GET:/services/${info}:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services/${info}' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" println(exec_curl(cmd)) return } if logs != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:GET:/services/${logs}/logs:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services/${logs}/logs' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:GET:/services/${logs}/logs:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services/${logs}/logs' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" print(exec_curl(cmd)) return } if tail != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:GET:/services/${tail}/logs:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services/${tail}/logs?lines=9000' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:GET:/services/${tail}/logs:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X GET '${api_base}/services/${tail}/logs?lines=9000' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" print(exec_curl(cmd)) return } if sleep != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/services/${sleep}/sleep:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${sleep}/sleep' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:POST:/services/${sleep}/sleep:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${sleep}/sleep' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" exec_curl(cmd) println('${green}Service sleeping: ${sleep}${reset}') return } if wake != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/services/${wake}/wake:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${wake}/wake' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:POST:/services/${wake}/wake:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${wake}/wake' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" exec_curl(cmd) println('${green}Service waking: ${wake}${reset}') return } if destroy != '' { - cmd := "TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:DELETE:/services/${destroy}:\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X DELETE '${api_base}/services/${destroy}' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\"" + cmd := "TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:DELETE:/services/${destroy}:\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X DELETE '${api_base}/services/${destroy}' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\"" exec_curl(cmd) println('${green}Service destroyed: ${destroy}${reset}') return @@ -361,7 +369,7 @@ fn cmd_service(name string, ports string, service_type string, bootstrap string, if execute != '' { json := '{"command":"${escape_json(command)}"}' - cmd := "BODY='${json}'; TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/services/${execute}/execute:\\$BODY\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${execute}/execute' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\" -d \"\\$BODY\"" + cmd := "BODY='${json}'; TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:POST:/services/${execute}/execute:$$BODY\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${execute}/execute' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\" -d \"$$BODY\"" result := exec_curl(cmd) stdout_str := extract_json_string(result, 'stdout') @@ -378,7 +386,7 @@ fn cmd_service(name string, ports string, service_type string, bootstrap string, if dump_bootstrap != '' { eprintln('Fetching bootstrap script from ${dump_bootstrap}...') json := '{"command":"cat /tmp/bootstrap.sh"}' - cmd := "BODY='${json}'; TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/services/${dump_bootstrap}/execute:\\$BODY\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${dump_bootstrap}/execute' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\" -d \"\\$BODY\"" + cmd := "BODY='${json}'; TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:POST:/services/${dump_bootstrap}/execute:$$BODY\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services/${dump_bootstrap}/execute' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\" -d \"$$BODY\"" result := exec_curl(cmd) bootstrap_script := extract_json_string(result, 'stdout') @@ -433,7 +441,7 @@ fn cmd_service(name string, ports string, service_type string, bootstrap string, json += '}' println('${yellow}Creating service...${reset}') - cmd := "BODY='${json}'; TIMESTAMP=\\$(date +%s); MESSAGE=\"\\$TIMESTAMP:POST:/services:\\$BODY\"; SIGNATURE=\\$(echo -n \"\\$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: \\$TIMESTAMP\" -H \"X-Signature: \\$SIGNATURE\" -d \"\\$BODY\"" + cmd := "BODY='${json}'; TIMESTAMP=$$(date +%s); MESSAGE=\"$$TIMESTAMP:POST:/services:$$BODY\"; SIGNATURE=$$(echo -n \"$$MESSAGE\" | openssl dgst -sha256 -hmac '${secret_key}' -hex | sed 's/.*= //'); curl -s -X POST '${api_base}/services' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${pub_key}' -H \"X-Timestamp: $$TIMESTAMP\" -H \"X-Signature: $$SIGNATURE\" -d \"$$BODY\"" println(exec_curl(cmd)) return }