Add unit tests for 18 languages
This commit is contained in:
parent
a379e061fc
commit
1e46faef8b
13 changed files with 2169 additions and 16 deletions
142
.github/workflows/inception-matrix.yml
vendored
142
.github/workflows/inception-matrix.yml
vendored
|
|
@ -21,8 +21,8 @@ jobs:
|
|||
# ============================================================================
|
||||
# UNIT TESTS - Test internal logic without API calls
|
||||
# ============================================================================
|
||||
unit-tests:
|
||||
name: "Unit Tests: ${{ matrix.lang }}"
|
||||
unit-tests-scripting:
|
||||
name: "Unit: ${{ matrix.lang }}"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
@ -34,35 +34,145 @@ jobs:
|
|||
run: node tests/unit/test_javascript.js
|
||||
- lang: Ruby
|
||||
run: ruby tests/unit/test_ruby.rb
|
||||
- lang: Go
|
||||
run: go run tests/unit/test_go.go
|
||||
- lang: Bash
|
||||
run: bash tests/unit/test_bash.sh
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
if: matrix.lang == 'Go'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
|
||||
- name: Run ${{ matrix.lang }} unit tests
|
||||
run: ${{ matrix.run }}
|
||||
|
||||
unit-tests-go:
|
||||
name: "Unit: Go"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
- name: Run Go unit tests
|
||||
run: go run tests/unit/test_go.go
|
||||
|
||||
unit-tests-lua:
|
||||
name: "Unit Tests: Lua"
|
||||
name: "Unit: Lua"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Lua
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y lua5.4
|
||||
run: sudo apt-get update && sudo apt-get install -y lua5.4
|
||||
- name: Run Lua unit tests
|
||||
run: lua tests/unit/test_lua.lua
|
||||
|
||||
unit-tests-php:
|
||||
name: "Unit: PHP"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
- name: Run PHP unit tests
|
||||
run: php tests/unit/test_php.php
|
||||
|
||||
unit-tests-perl:
|
||||
name: "Unit: Perl"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run Perl unit tests
|
||||
run: perl tests/unit/test_perl.pl
|
||||
|
||||
unit-tests-elixir:
|
||||
name: "Unit: Elixir"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: '26'
|
||||
elixir-version: '1.15'
|
||||
- name: Run Elixir unit tests
|
||||
run: elixir tests/unit/test_elixir.exs
|
||||
|
||||
unit-tests-erlang:
|
||||
name: "Unit: Erlang"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: '26'
|
||||
- name: Run Erlang unit tests
|
||||
run: escript tests/unit/test_erlang.erl
|
||||
|
||||
unit-tests-julia:
|
||||
name: "Unit: Julia"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: julia-actions/setup-julia@v1
|
||||
with:
|
||||
version: '1'
|
||||
- name: Run Julia unit tests
|
||||
run: julia tests/unit/test_julia.jl
|
||||
|
||||
unit-tests-r:
|
||||
name: "Unit: R"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: r-lib/actions/setup-r@v2
|
||||
- name: Install R packages
|
||||
run: Rscript -e 'install.packages(c("openssl", "base64enc"), repos="https://cloud.r-project.org")'
|
||||
- name: Run R unit tests
|
||||
run: Rscript tests/unit/test_r.R
|
||||
|
||||
unit-tests-clojure:
|
||||
name: "Unit: Clojure"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Clojure
|
||||
run: |
|
||||
curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh
|
||||
chmod +x linux-install.sh
|
||||
sudo ./linux-install.sh
|
||||
- name: Run Clojure unit tests
|
||||
run: clj -M tests/unit/test_clojure.clj
|
||||
|
||||
unit-tests-dart:
|
||||
name: "Unit: Dart"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dart-lang/setup-dart@v1
|
||||
- name: Install crypto package
|
||||
run: |
|
||||
cd tests/unit
|
||||
echo 'name: test_dart' > pubspec.yaml
|
||||
echo 'dependencies:' >> pubspec.yaml
|
||||
echo ' crypto: ^3.0.0' >> pubspec.yaml
|
||||
dart pub get
|
||||
- name: Run Dart unit tests
|
||||
run: cd tests/unit && dart run test_dart.dart
|
||||
|
||||
unit-tests-tcl:
|
||||
name: "Unit: TCL"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install TCL
|
||||
run: sudo apt-get update && sudo apt-get install -y tcl
|
||||
- name: Run TCL unit tests
|
||||
run: tclsh tests/unit/test_tcl.tcl
|
||||
|
||||
unit-tests-powershell:
|
||||
name: "Unit: PowerShell"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run PowerShell unit tests
|
||||
run: pwsh tests/unit/test_powershell.ps1
|
||||
|
||||
# ============================================================================
|
||||
# INTEGRATION TESTS - Test component interactions without real API calls
|
||||
# ============================================================================
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ run_test() {
|
|||
echo -e "${CYAN}━━━ Scripting Languages ━━━${NC}"
|
||||
run_test "Python" "python3 test_python.py" "python3"
|
||||
run_test "JavaScript" "node test_javascript.js" "node"
|
||||
run_test "TypeScript" "npx ts-node test_typescript.ts" "npx"
|
||||
run_test "Ruby" "ruby test_ruby.rb" "ruby"
|
||||
run_test "PHP" "php test_php.php" "php"
|
||||
run_test "Perl" "perl test_perl.pl" "perl"
|
||||
run_test "Lua" "lua test_lua.lua" "lua"
|
||||
run_test "Bash" "bash test_bash.sh" "bash"
|
||||
echo ""
|
||||
|
|
@ -56,6 +59,23 @@ echo -e "${CYAN}━━━ Systems Languages ━━━${NC}"
|
|||
run_test "Go" "go run test_go.go" "go"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}━━━ Functional Languages ━━━${NC}"
|
||||
run_test "Elixir" "elixir test_elixir.exs" "elixir"
|
||||
run_test "Erlang" "escript test_erlang.erl" "escript"
|
||||
run_test "Clojure" "clj -M test_clojure.clj" "clj"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}━━━ Scientific Languages ━━━${NC}"
|
||||
run_test "Julia" "julia test_julia.jl" "julia"
|
||||
run_test "R" "Rscript test_r.R" "Rscript"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}━━━ Other Languages ━━━${NC}"
|
||||
run_test "Dart" "dart run test_dart.dart" "dart"
|
||||
run_test "TCL" "tclsh test_tcl.tcl" "tclsh"
|
||||
run_test "PowerShell" "pwsh test_powershell.ps1" "pwsh"
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo -e "${CYAN}══════════════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
|
|
|||
146
tests/unit/test_clojure.clj
Executable file
146
tests/unit/test_clojure.clj
Executable file
|
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/env clj -M
|
||||
;; Unit tests for un.clj - tests internal functions without API calls
|
||||
|
||||
(ns test-un-clj
|
||||
(:import [javax.crypto Mac]
|
||||
[javax.crypto.spec SecretKeySpec]
|
||||
[java.util Base64]))
|
||||
|
||||
(def passed (atom 0))
|
||||
(def failed (atom 0))
|
||||
|
||||
(defn test-case [name f]
|
||||
(try
|
||||
(f)
|
||||
(println (str " ✓ " name))
|
||||
(swap! passed inc)
|
||||
(catch Exception e
|
||||
(println (str " ✗ " name))
|
||||
(println (str " " (.getMessage e)))
|
||||
(swap! failed inc))))
|
||||
|
||||
(defn assert-equal [actual expected]
|
||||
(when (not= actual expected)
|
||||
(throw (Exception. (str "Expected '" expected "' but got '" actual "'")))))
|
||||
|
||||
(defn assert-not-equal [a b]
|
||||
(when (= a b)
|
||||
(throw (Exception. (str "Expected values to be different but both were '" a "'")))))
|
||||
|
||||
(defn assert-contains [s substr]
|
||||
(when (not (.contains s substr))
|
||||
(throw (Exception. (str "Expected '" s "' to contain '" substr "'")))))
|
||||
|
||||
(defn assert-true [val]
|
||||
(when (not val)
|
||||
(throw (Exception. "Expected true but got false"))))
|
||||
|
||||
(defn hmac-sha256 [secret message]
|
||||
(let [mac (Mac/getInstance "HmacSHA256")
|
||||
key (SecretKeySpec. (.getBytes secret "UTF-8") "HmacSHA256")]
|
||||
(.init mac key)
|
||||
(apply str (map #(format "%02x" %) (.doFinal mac (.getBytes message "UTF-8"))))))
|
||||
|
||||
;; Extension mapping
|
||||
(def 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" ".java" "java" ".kt" "kotlin"
|
||||
".hs" "haskell" ".clj" "clojure" ".erl" "erlang"
|
||||
".ex" "elixir" ".jl" "julia" ".r" "r"})
|
||||
|
||||
(println "\n=== Extension Mapping Tests ===")
|
||||
|
||||
(test-case "Python extension maps correctly"
|
||||
#(assert-equal (ext-map ".py") "python"))
|
||||
|
||||
(test-case "Clojure extension maps correctly"
|
||||
#(assert-equal (ext-map ".clj") "clojure"))
|
||||
|
||||
(test-case "JavaScript extension maps correctly"
|
||||
#(assert-equal (ext-map ".js") "javascript"))
|
||||
|
||||
(test-case "Go extension maps correctly"
|
||||
#(assert-equal (ext-map ".go") "go"))
|
||||
|
||||
(test-case "Haskell extension maps correctly"
|
||||
#(assert-equal (ext-map ".hs") "haskell"))
|
||||
|
||||
(println "\n=== HMAC Signature Tests ===")
|
||||
|
||||
(test-case "HMAC-SHA256 generates 64 character hex string"
|
||||
#(let [sig (hmac-sha256 "test-secret" "test-message")]
|
||||
(assert-equal (count sig) 64)))
|
||||
|
||||
(test-case "Same input produces same signature"
|
||||
#(let [sig1 (hmac-sha256 "key" "msg")
|
||||
sig2 (hmac-sha256 "key" "msg")]
|
||||
(assert-equal sig1 sig2)))
|
||||
|
||||
(test-case "Different secrets produce different signatures"
|
||||
#(let [sig1 (hmac-sha256 "key1" "msg")
|
||||
sig2 (hmac-sha256 "key2" "msg")]
|
||||
(assert-not-equal sig1 sig2)))
|
||||
|
||||
(test-case "Signature format verification"
|
||||
#(let [timestamp "1704067200"
|
||||
method "POST"
|
||||
endpoint "/execute"
|
||||
body "{\"language\":\"python\"}"
|
||||
message (str timestamp ":" method ":" endpoint ":" body)]
|
||||
(assert-true (.startsWith message timestamp))
|
||||
(assert-contains message ":POST:")
|
||||
(assert-contains message ":/execute:")))
|
||||
|
||||
(println "\n=== Language Detection Tests ===")
|
||||
|
||||
(test-case "Detect language from .clj extension"
|
||||
#(let [filename "script.clj"
|
||||
ext (str "." (last (clojure.string/split filename #"\.")))]
|
||||
(assert-equal (ext-map ext) "clojure")))
|
||||
|
||||
(test-case "Python shebang detection"
|
||||
#(let [content "#!/usr/bin/env python3\nprint('hello')"
|
||||
first-line (first (clojure.string/split content #"\n"))]
|
||||
(assert-true (.startsWith first-line "#!"))
|
||||
(assert-contains first-line "python")))
|
||||
|
||||
(println "\n=== Argument Parsing Tests ===")
|
||||
|
||||
(test-case "Parse -e KEY=VALUE format"
|
||||
#(let [arg "DEBUG=1"
|
||||
[key value] (clojure.string/split arg #"=" 2)]
|
||||
(assert-equal key "DEBUG")
|
||||
(assert-equal value "1")))
|
||||
|
||||
(test-case "Parse -e KEY=VALUE with equals in value"
|
||||
#(let [arg "URL=https://example.com?foo=bar"
|
||||
[key value] (clojure.string/split arg #"=" 2)]
|
||||
(assert-equal key "URL")
|
||||
(assert-equal value "https://example.com?foo=bar")))
|
||||
|
||||
(println "\n=== File Operations Tests ===")
|
||||
|
||||
(test-case "Base64 encoding/decoding"
|
||||
#(let [content "print('hello world')"
|
||||
encoder (Base64/getEncoder)
|
||||
decoder (Base64/getDecoder)
|
||||
encoded (.encodeToString encoder (.getBytes content))
|
||||
decoded (String. (.decode decoder encoded))]
|
||||
(assert-equal decoded content)))
|
||||
|
||||
(println "\n=== API Constants Tests ===")
|
||||
|
||||
(test-case "API base URL format"
|
||||
#(let [api-base "https://api.unsandbox.com"]
|
||||
(assert-true (.startsWith api-base "https://"))
|
||||
(assert-contains api-base "unsandbox.com")))
|
||||
|
||||
;; Summary
|
||||
(println "\n=== Summary ===")
|
||||
(println (str "Passed: " @passed))
|
||||
(println (str "Failed: " @failed))
|
||||
(println (str "Total: " (+ @passed @failed)))
|
||||
|
||||
(System/exit (if (> @failed 0) 1 0))
|
||||
178
tests/unit/test_dart.dart
Executable file
178
tests/unit/test_dart.dart
Executable file
|
|
@ -0,0 +1,178 @@
|
|||
#!/usr/bin/env dart
|
||||
/// Unit tests for un.dart - tests internal functions without API calls
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
|
||||
void test(String name, void Function() fn) {
|
||||
try {
|
||||
fn();
|
||||
print(' ✓ $name');
|
||||
passed++;
|
||||
} catch (e) {
|
||||
print(' ✗ $name');
|
||||
print(' $e');
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(dynamic actual, dynamic expected) {
|
||||
if (actual != expected) {
|
||||
throw Exception("Expected '$expected' but got '$actual'");
|
||||
}
|
||||
}
|
||||
|
||||
void assertNotEqual(dynamic a, dynamic b) {
|
||||
if (a == b) {
|
||||
throw Exception("Expected values to be different but both were '$a'");
|
||||
}
|
||||
}
|
||||
|
||||
void assertContains(String str, String substr) {
|
||||
if (!str.contains(substr)) {
|
||||
throw Exception("Expected '$str' to contain '$substr'");
|
||||
}
|
||||
}
|
||||
|
||||
void assertTrue(bool val) {
|
||||
if (!val) {
|
||||
throw Exception("Expected true but got false");
|
||||
}
|
||||
}
|
||||
|
||||
String hmacSha256(String secret, String message) {
|
||||
final key = utf8.encode(secret);
|
||||
final bytes = utf8.encode(message);
|
||||
final hmac = Hmac(sha256, key);
|
||||
final digest = hmac.convert(bytes);
|
||||
return digest.toString();
|
||||
}
|
||||
|
||||
void main() {
|
||||
final extMap = {
|
||||
'.py': 'python', '.js': 'javascript', '.ts': 'typescript',
|
||||
'.rb': 'ruby', '.php': 'php', '.pl': 'perl', '.lua': 'lua',
|
||||
'.sh': 'bash', '.go': 'go', '.rs': 'rust', '.c': 'c',
|
||||
'.cpp': 'cpp', '.java': 'java', '.kt': 'kotlin',
|
||||
'.hs': 'haskell', '.clj': 'clojure', '.erl': 'erlang',
|
||||
'.ex': 'elixir', '.jl': 'julia', '.dart': 'dart',
|
||||
};
|
||||
|
||||
print('\n=== Extension Mapping Tests ===');
|
||||
|
||||
test('Python extension maps correctly', () {
|
||||
assertEqual(extMap['.py'], 'python');
|
||||
});
|
||||
|
||||
test('Dart extension maps correctly', () {
|
||||
assertEqual(extMap['.dart'], 'dart');
|
||||
});
|
||||
|
||||
test('JavaScript extension maps correctly', () {
|
||||
assertEqual(extMap['.js'], 'javascript');
|
||||
});
|
||||
|
||||
test('Go extension maps correctly', () {
|
||||
assertEqual(extMap['.go'], 'go');
|
||||
});
|
||||
|
||||
test('Kotlin extension maps correctly', () {
|
||||
assertEqual(extMap['.kt'], 'kotlin');
|
||||
});
|
||||
|
||||
print('\n=== HMAC Signature Tests ===');
|
||||
|
||||
test('HMAC-SHA256 generates 64 character hex string', () {
|
||||
final sig = hmacSha256('test-secret', 'test-message');
|
||||
assertEqual(sig.length, 64);
|
||||
});
|
||||
|
||||
test('Same input produces same signature', () {
|
||||
final sig1 = hmacSha256('key', 'msg');
|
||||
final sig2 = hmacSha256('key', 'msg');
|
||||
assertEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
test('Different secrets produce different signatures', () {
|
||||
final sig1 = hmacSha256('key1', 'msg');
|
||||
final sig2 = hmacSha256('key2', 'msg');
|
||||
assertNotEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
test('Signature format verification', () {
|
||||
final timestamp = '1704067200';
|
||||
final method = 'POST';
|
||||
final endpoint = '/execute';
|
||||
final body = '{"language":"python"}';
|
||||
|
||||
final message = '$timestamp:$method:$endpoint:$body';
|
||||
|
||||
assertTrue(message.startsWith(timestamp));
|
||||
assertContains(message, ':POST:');
|
||||
assertContains(message, ':/execute:');
|
||||
});
|
||||
|
||||
print('\n=== Language Detection Tests ===');
|
||||
|
||||
test('Detect language from .dart extension', () {
|
||||
final filename = 'script.dart';
|
||||
final ext = '.${filename.split('.').last}';
|
||||
assertEqual(extMap[ext], 'dart');
|
||||
});
|
||||
|
||||
test('Python shebang detection', () {
|
||||
final content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
final firstLine = content.split('\n')[0];
|
||||
assertTrue(firstLine.startsWith('#!'));
|
||||
assertContains(firstLine, 'python');
|
||||
});
|
||||
|
||||
print('\n=== Argument Parsing Tests ===');
|
||||
|
||||
test('Parse -e KEY=VALUE format', () {
|
||||
final arg = 'DEBUG=1';
|
||||
final parts = arg.split('=');
|
||||
final key = parts[0];
|
||||
final value = parts.sublist(1).join('=');
|
||||
assertEqual(key, 'DEBUG');
|
||||
assertEqual(value, '1');
|
||||
});
|
||||
|
||||
test('Parse -e KEY=VALUE with equals in value', () {
|
||||
final arg = 'URL=https://example.com?foo=bar';
|
||||
final parts = arg.split('=');
|
||||
final key = parts[0];
|
||||
final value = parts.sublist(1).join('=');
|
||||
assertEqual(key, 'URL');
|
||||
assertEqual(value, 'https://example.com?foo=bar');
|
||||
});
|
||||
|
||||
print('\n=== File Operations Tests ===');
|
||||
|
||||
test('Base64 encoding/decoding', () {
|
||||
final content = "print('hello world')";
|
||||
final encoded = base64Encode(utf8.encode(content));
|
||||
final decoded = utf8.decode(base64Decode(encoded));
|
||||
assertEqual(decoded, content);
|
||||
});
|
||||
|
||||
print('\n=== API Constants Tests ===');
|
||||
|
||||
test('API base URL format', () {
|
||||
final apiBase = 'https://api.unsandbox.com';
|
||||
assertTrue(apiBase.startsWith('https://'));
|
||||
assertContains(apiBase, 'unsandbox.com');
|
||||
});
|
||||
|
||||
// Summary
|
||||
print('\n=== Summary ===');
|
||||
print('Passed: $passed');
|
||||
print('Failed: $failed');
|
||||
print('Total: ${passed + failed}');
|
||||
|
||||
exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
186
tests/unit/test_elixir.exs
Executable file
186
tests/unit/test_elixir.exs
Executable file
|
|
@ -0,0 +1,186 @@
|
|||
#!/usr/bin/env elixir
|
||||
# Unit tests for un.ex - tests internal functions without API calls
|
||||
|
||||
defmodule UnTest do
|
||||
@passed Agent.start_link(fn -> 0 end, name: :passed)
|
||||
@failed Agent.start_link(fn -> 0 end, name: :failed)
|
||||
|
||||
def run do
|
||||
Agent.start_link(fn -> 0 end, name: :passed)
|
||||
Agent.start_link(fn -> 0 end, name: :failed)
|
||||
|
||||
IO.puts("\n=== Extension Mapping Tests ===")
|
||||
|
||||
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", ".java" => "java", ".kt" => "kotlin",
|
||||
".hs" => "haskell", ".clj" => "clojure", ".erl" => "erlang",
|
||||
".ex" => "elixir", ".exs" => "elixir", ".jl" => "julia"
|
||||
}
|
||||
|
||||
test("Python extension maps correctly", fn ->
|
||||
assert_equal(ext_map[".py"], "python")
|
||||
end)
|
||||
|
||||
test("Elixir extensions map correctly", fn ->
|
||||
assert_equal(ext_map[".ex"], "elixir")
|
||||
assert_equal(ext_map[".exs"], "elixir")
|
||||
end)
|
||||
|
||||
test("JavaScript extension maps correctly", fn ->
|
||||
assert_equal(ext_map[".js"], "javascript")
|
||||
end)
|
||||
|
||||
test("Go extension maps correctly", fn ->
|
||||
assert_equal(ext_map[".go"], "go")
|
||||
end)
|
||||
|
||||
test("Erlang extension maps correctly", fn ->
|
||||
assert_equal(ext_map[".erl"], "erlang")
|
||||
end)
|
||||
|
||||
IO.puts("\n=== HMAC Signature Tests ===")
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string", fn ->
|
||||
sig = :crypto.mac(:hmac, :sha256, "test-secret", "test-message")
|
||||
|> Base.encode16(case: :lower)
|
||||
assert_equal(String.length(sig), 64)
|
||||
end)
|
||||
|
||||
test("Same input produces same signature", fn ->
|
||||
sig1 = :crypto.mac(:hmac, :sha256, "key", "msg") |> Base.encode16(case: :lower)
|
||||
sig2 = :crypto.mac(:hmac, :sha256, "key", "msg") |> Base.encode16(case: :lower)
|
||||
assert_equal(sig1, sig2)
|
||||
end)
|
||||
|
||||
test("Different secrets produce different signatures", fn ->
|
||||
sig1 = :crypto.mac(:hmac, :sha256, "key1", "msg") |> Base.encode16(case: :lower)
|
||||
sig2 = :crypto.mac(:hmac, :sha256, "key2", "msg") |> Base.encode16(case: :lower)
|
||||
assert_not_equal(sig1, sig2)
|
||||
end)
|
||||
|
||||
test("Signature format verification", fn ->
|
||||
timestamp = "1704067200"
|
||||
method = "POST"
|
||||
endpoint = "/execute"
|
||||
body = ~s({"language":"python"})
|
||||
|
||||
message = "#{timestamp}:#{method}:#{endpoint}:#{body}"
|
||||
|
||||
assert_true(String.starts_with?(message, timestamp))
|
||||
assert_contains(message, ":POST:")
|
||||
assert_contains(message, ":/execute:")
|
||||
end)
|
||||
|
||||
IO.puts("\n=== Language Detection Tests ===")
|
||||
|
||||
test("Detect language from .exs extension", fn ->
|
||||
ext = Path.extname("script.exs")
|
||||
assert_equal(ext_map[ext], "elixir")
|
||||
end)
|
||||
|
||||
test("Python shebang detection", fn ->
|
||||
content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
[first_line | _] = String.split(content, "\n")
|
||||
assert_true(String.starts_with?(first_line, "#!"))
|
||||
assert_contains(first_line, "python")
|
||||
end)
|
||||
|
||||
IO.puts("\n=== Argument Parsing Tests ===")
|
||||
|
||||
test("Parse -e KEY=VALUE format", fn ->
|
||||
arg = "DEBUG=1"
|
||||
[key | rest] = String.split(arg, "=", parts: 2)
|
||||
value = Enum.join(rest, "=")
|
||||
assert_equal(key, "DEBUG")
|
||||
assert_equal(value, "1")
|
||||
end)
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value", fn ->
|
||||
arg = "URL=https://example.com?foo=bar"
|
||||
[key | rest] = String.split(arg, "=", parts: 2)
|
||||
value = Enum.join(rest, "=")
|
||||
assert_equal(key, "URL")
|
||||
assert_equal(value, "https://example.com?foo=bar")
|
||||
end)
|
||||
|
||||
IO.puts("\n=== File Operations Tests ===")
|
||||
|
||||
test("Base64 encoding/decoding", fn ->
|
||||
content = "print('hello world')"
|
||||
encoded = Base.encode64(content)
|
||||
{:ok, decoded} = Base.decode64(encoded)
|
||||
assert_equal(decoded, content)
|
||||
end)
|
||||
|
||||
test("Extract file basename", fn ->
|
||||
path = "/home/user/project/script.exs"
|
||||
assert_equal(Path.basename(path), "script.exs")
|
||||
end)
|
||||
|
||||
test("Extract file extension", fn ->
|
||||
path = "/home/user/project/script.exs"
|
||||
assert_equal(Path.extname(path), ".exs")
|
||||
end)
|
||||
|
||||
IO.puts("\n=== API Constants Tests ===")
|
||||
|
||||
test("API base URL format", fn ->
|
||||
api_base = "https://api.unsandbox.com"
|
||||
assert_true(String.starts_with?(api_base, "https://"))
|
||||
assert_contains(api_base, "unsandbox.com")
|
||||
end)
|
||||
|
||||
# Summary
|
||||
passed = Agent.get(:passed, & &1)
|
||||
failed = Agent.get(:failed, & &1)
|
||||
|
||||
IO.puts("\n=== Summary ===")
|
||||
IO.puts("Passed: #{passed}")
|
||||
IO.puts("Failed: #{failed}")
|
||||
IO.puts("Total: #{passed + failed}")
|
||||
|
||||
if failed > 0, do: System.halt(1), else: System.halt(0)
|
||||
end
|
||||
|
||||
defp test(name, fun) do
|
||||
try do
|
||||
fun.()
|
||||
IO.puts(" ✓ #{name}")
|
||||
Agent.update(:passed, &(&1 + 1))
|
||||
rescue
|
||||
e ->
|
||||
IO.puts(" ✗ #{name}")
|
||||
IO.puts(" #{Exception.message(e)}")
|
||||
Agent.update(:failed, &(&1 + 1))
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_equal(actual, expected) do
|
||||
if actual != expected do
|
||||
raise "Expected '#{expected}' but got '#{actual}'"
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_not_equal(a, b) do
|
||||
if a == b do
|
||||
raise "Expected values to be different but both were '#{a}'"
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_contains(str, substr) do
|
||||
unless String.contains?(str, substr) do
|
||||
raise "Expected '#{str}' to contain '#{substr}'"
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_true(val) do
|
||||
unless val do
|
||||
raise "Expected true but got false"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
UnTest.run()
|
||||
168
tests/unit/test_erlang.erl
Executable file
168
tests/unit/test_erlang.erl
Executable file
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/env escript
|
||||
%% Unit tests for un.erl - tests internal functions without API calls
|
||||
|
||||
-mode(compile).
|
||||
|
||||
main(_) ->
|
||||
put(passed, 0),
|
||||
put(failed, 0),
|
||||
|
||||
io:format("~n=== Extension Mapping Tests ===~n"),
|
||||
|
||||
ExtMap = #{
|
||||
".py" => "python", ".js" => "javascript", ".ts" => "typescript",
|
||||
".rb" => "ruby", ".php" => "php", ".pl" => "perl", ".lua" => "lua",
|
||||
".sh" => "bash", ".go" => "go", ".rs" => "rust", ".c" => "c",
|
||||
".cpp" => "cpp", ".java" => "java", ".kt" => "kotlin",
|
||||
".hs" => "haskell", ".clj" => "clojure", ".erl" => "erlang",
|
||||
".ex" => "elixir", ".jl" => "julia"
|
||||
},
|
||||
|
||||
test("Python extension maps correctly", fun() ->
|
||||
assert_equal(maps:get(".py", ExtMap), "python")
|
||||
end),
|
||||
|
||||
test("Erlang extension maps correctly", fun() ->
|
||||
assert_equal(maps:get(".erl", ExtMap), "erlang")
|
||||
end),
|
||||
|
||||
test("JavaScript extension maps correctly", fun() ->
|
||||
assert_equal(maps:get(".js", ExtMap), "javascript")
|
||||
end),
|
||||
|
||||
test("Go extension maps correctly", fun() ->
|
||||
assert_equal(maps:get(".go", ExtMap), "go")
|
||||
end),
|
||||
|
||||
io:format("~n=== HMAC Signature Tests ===~n"),
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string", fun() ->
|
||||
Sig = crypto:mac(hmac, sha256, <<"test-secret">>, <<"test-message">>),
|
||||
HexSig = binary_to_hex(Sig),
|
||||
assert_equal(length(HexSig), 64)
|
||||
end),
|
||||
|
||||
test("Same input produces same signature", fun() ->
|
||||
Sig1 = crypto:mac(hmac, sha256, <<"key">>, <<"msg">>),
|
||||
Sig2 = crypto:mac(hmac, sha256, <<"key">>, <<"msg">>),
|
||||
assert_equal(Sig1, Sig2)
|
||||
end),
|
||||
|
||||
test("Different secrets produce different signatures", fun() ->
|
||||
Sig1 = crypto:mac(hmac, sha256, <<"key1">>, <<"msg">>),
|
||||
Sig2 = crypto:mac(hmac, sha256, <<"key2">>, <<"msg">>),
|
||||
assert_not_equal(Sig1, Sig2)
|
||||
end),
|
||||
|
||||
test("Signature format verification", fun() ->
|
||||
Timestamp = "1704067200",
|
||||
Method = "POST",
|
||||
Endpoint = "/execute",
|
||||
Body = "{\"language\":\"python\"}",
|
||||
Message = Timestamp ++ ":" ++ Method ++ ":" ++ Endpoint ++ ":" ++ Body,
|
||||
assert_true(string:prefix(Message, Timestamp) =/= nomatch),
|
||||
assert_contains(Message, ":POST:"),
|
||||
assert_contains(Message, ":/execute:")
|
||||
end),
|
||||
|
||||
io:format("~n=== Language Detection Tests ===~n"),
|
||||
|
||||
test("Detect language from .erl extension", fun() ->
|
||||
Ext = filename:extension("script.erl"),
|
||||
assert_equal(maps:get(Ext, ExtMap), "erlang")
|
||||
end),
|
||||
|
||||
test("Python shebang detection", fun() ->
|
||||
Content = "#!/usr/bin/env python3\nprint('hello')",
|
||||
[FirstLine | _] = string:split(Content, "\n"),
|
||||
assert_true(string:prefix(FirstLine, "#!") =/= nomatch),
|
||||
assert_contains(FirstLine, "python")
|
||||
end),
|
||||
|
||||
io:format("~n=== Argument Parsing Tests ===~n"),
|
||||
|
||||
test("Parse -e KEY=VALUE format", fun() ->
|
||||
Arg = "DEBUG=1",
|
||||
[Key, Value] = string:split(Arg, "="),
|
||||
assert_equal(Key, "DEBUG"),
|
||||
assert_equal(Value, "1")
|
||||
end),
|
||||
|
||||
io:format("~n=== File Operations Tests ===~n"),
|
||||
|
||||
test("Base64 encoding/decoding", fun() ->
|
||||
Content = "print('hello world')",
|
||||
Encoded = base64:encode(Content),
|
||||
Decoded = base64:decode(Encoded),
|
||||
assert_equal(binary_to_list(Decoded), Content)
|
||||
end),
|
||||
|
||||
test("Extract file basename", fun() ->
|
||||
Path = "/home/user/project/script.erl",
|
||||
assert_equal(filename:basename(Path), "script.erl")
|
||||
end),
|
||||
|
||||
test("Extract file extension", fun() ->
|
||||
Path = "/home/user/project/script.erl",
|
||||
assert_equal(filename:extension(Path), ".erl")
|
||||
end),
|
||||
|
||||
io:format("~n=== API Constants Tests ===~n"),
|
||||
|
||||
test("API base URL format", fun() ->
|
||||
ApiBase = "https://api.unsandbox.com",
|
||||
assert_true(string:prefix(ApiBase, "https://") =/= nomatch),
|
||||
assert_contains(ApiBase, "unsandbox.com")
|
||||
end),
|
||||
|
||||
% Summary
|
||||
Passed = get(passed),
|
||||
Failed = get(failed),
|
||||
io:format("~n=== Summary ===~n"),
|
||||
io:format("Passed: ~p~n", [Passed]),
|
||||
io:format("Failed: ~p~n", [Failed]),
|
||||
io:format("Total: ~p~n", [Passed + Failed]),
|
||||
|
||||
case Failed > 0 of
|
||||
true -> halt(1);
|
||||
false -> halt(0)
|
||||
end.
|
||||
|
||||
test(Name, Fun) ->
|
||||
try
|
||||
Fun(),
|
||||
io:format(" ✓ ~s~n", [Name]),
|
||||
put(passed, get(passed) + 1)
|
||||
catch
|
||||
_:Reason ->
|
||||
io:format(" ✗ ~s~n", [Name]),
|
||||
io:format(" ~p~n", [Reason]),
|
||||
put(failed, get(failed) + 1)
|
||||
end.
|
||||
|
||||
assert_equal(Actual, Expected) ->
|
||||
case Actual =:= Expected of
|
||||
true -> ok;
|
||||
false -> throw({expected, Expected, got, Actual})
|
||||
end.
|
||||
|
||||
assert_not_equal(A, B) ->
|
||||
case A =/= B of
|
||||
true -> ok;
|
||||
false -> throw({expected_different, A})
|
||||
end.
|
||||
|
||||
assert_contains(Str, Substr) ->
|
||||
case string:find(Str, Substr) of
|
||||
nomatch -> throw({expected_to_contain, Str, Substr});
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
assert_true(Val) ->
|
||||
case Val of
|
||||
true -> ok;
|
||||
_ -> throw({expected_true, got, Val})
|
||||
end.
|
||||
|
||||
binary_to_hex(Bin) ->
|
||||
lists:flatten([io_lib:format("~2.16.0b", [X]) || <<X:8>> <= Bin]).
|
||||
178
tests/unit/test_julia.jl
Executable file
178
tests/unit/test_julia.jl
Executable file
|
|
@ -0,0 +1,178 @@
|
|||
#!/usr/bin/env julia
|
||||
# Unit tests for un.jl - tests internal functions without API calls
|
||||
|
||||
using SHA
|
||||
using Base64
|
||||
|
||||
passed = Ref(0)
|
||||
failed = Ref(0)
|
||||
|
||||
function test(name, fn)
|
||||
try
|
||||
fn()
|
||||
println(" ✓ $name")
|
||||
passed[] += 1
|
||||
catch e
|
||||
println(" ✗ $name")
|
||||
println(" $(sprint(showerror, e))")
|
||||
failed[] += 1
|
||||
end
|
||||
end
|
||||
|
||||
function assert_equal(actual, expected)
|
||||
if actual != expected
|
||||
error("Expected '$expected' but got '$actual'")
|
||||
end
|
||||
end
|
||||
|
||||
function assert_not_equal(a, b)
|
||||
if a == b
|
||||
error("Expected values to be different but both were '$a'")
|
||||
end
|
||||
end
|
||||
|
||||
function assert_contains(str, substr)
|
||||
if !occursin(substr, str)
|
||||
error("Expected '$str' to contain '$substr'")
|
||||
end
|
||||
end
|
||||
|
||||
function assert_true(val)
|
||||
if !val
|
||||
error("Expected true but got false")
|
||||
end
|
||||
end
|
||||
|
||||
# Extension mapping
|
||||
EXT_MAP = Dict(
|
||||
".py" => "python", ".js" => "javascript", ".ts" => "typescript",
|
||||
".rb" => "ruby", ".php" => "php", ".pl" => "perl", ".lua" => "lua",
|
||||
".sh" => "bash", ".go" => "go", ".rs" => "rust", ".c" => "c",
|
||||
".cpp" => "cpp", ".java" => "java", ".kt" => "kotlin",
|
||||
".hs" => "haskell", ".clj" => "clojure", ".erl" => "erlang",
|
||||
".ex" => "elixir", ".jl" => "julia", ".r" => "r", ".R" => "r"
|
||||
)
|
||||
|
||||
println("\n=== Extension Mapping Tests ===")
|
||||
|
||||
test("Python extension maps correctly") do
|
||||
assert_equal(EXT_MAP[".py"], "python")
|
||||
end
|
||||
|
||||
test("Julia extension maps correctly") do
|
||||
assert_equal(EXT_MAP[".jl"], "julia")
|
||||
end
|
||||
|
||||
test("JavaScript extension maps correctly") do
|
||||
assert_equal(EXT_MAP[".js"], "javascript")
|
||||
end
|
||||
|
||||
test("Go extension maps correctly") do
|
||||
assert_equal(EXT_MAP[".go"], "go")
|
||||
end
|
||||
|
||||
test("R extensions map correctly") do
|
||||
assert_equal(EXT_MAP[".r"], "r")
|
||||
assert_equal(EXT_MAP[".R"], "r")
|
||||
end
|
||||
|
||||
println("\n=== HMAC Signature Tests ===")
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string") do
|
||||
sig = bytes2hex(hmac_sha256("test-secret", "test-message"))
|
||||
assert_equal(length(sig), 64)
|
||||
end
|
||||
|
||||
test("Same input produces same signature") do
|
||||
sig1 = bytes2hex(hmac_sha256("key", "msg"))
|
||||
sig2 = bytes2hex(hmac_sha256("key", "msg"))
|
||||
assert_equal(sig1, sig2)
|
||||
end
|
||||
|
||||
test("Different secrets produce different signatures") do
|
||||
sig1 = bytes2hex(hmac_sha256("key1", "msg"))
|
||||
sig2 = bytes2hex(hmac_sha256("key2", "msg"))
|
||||
assert_not_equal(sig1, sig2)
|
||||
end
|
||||
|
||||
test("Signature format verification") do
|
||||
timestamp = "1704067200"
|
||||
method = "POST"
|
||||
endpoint = "/execute"
|
||||
body = """{"language":"python"}"""
|
||||
|
||||
message = "$timestamp:$method:$endpoint:$body"
|
||||
|
||||
assert_true(startswith(message, timestamp))
|
||||
assert_contains(message, ":POST:")
|
||||
assert_contains(message, ":/execute:")
|
||||
end
|
||||
|
||||
println("\n=== Language Detection Tests ===")
|
||||
|
||||
test("Detect language from .jl extension") do
|
||||
ext = splitext("script.jl")[2]
|
||||
assert_equal(EXT_MAP[ext], "julia")
|
||||
end
|
||||
|
||||
test("Python shebang detection") do
|
||||
content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
first_line = split(content, "\n")[1]
|
||||
assert_true(startswith(first_line, "#!"))
|
||||
assert_contains(first_line, "python")
|
||||
end
|
||||
|
||||
println("\n=== Argument Parsing Tests ===")
|
||||
|
||||
test("Parse -e KEY=VALUE format") do
|
||||
arg = "DEBUG=1"
|
||||
parts = split(arg, "=", limit=2)
|
||||
key = parts[1]
|
||||
value = parts[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"
|
||||
parts = split(arg, "=", limit=2)
|
||||
key = parts[1]
|
||||
value = parts[2]
|
||||
assert_equal(key, "URL")
|
||||
assert_equal(value, "https://example.com?foo=bar")
|
||||
end
|
||||
|
||||
println("\n=== File Operations Tests ===")
|
||||
|
||||
test("Base64 encoding/decoding") do
|
||||
content = "print('hello world')"
|
||||
encoded = base64encode(content)
|
||||
decoded = String(base64decode(encoded))
|
||||
assert_equal(decoded, content)
|
||||
end
|
||||
|
||||
test("Extract file basename") do
|
||||
path = "/home/user/project/script.jl"
|
||||
assert_equal(basename(path), "script.jl")
|
||||
end
|
||||
|
||||
test("Extract file extension") do
|
||||
path = "/home/user/project/script.jl"
|
||||
assert_equal(splitext(path)[2], ".jl")
|
||||
end
|
||||
|
||||
println("\n=== API Constants Tests ===")
|
||||
|
||||
test("API base URL format") do
|
||||
api_base = "https://api.unsandbox.com"
|
||||
assert_true(startswith(api_base, "https://"))
|
||||
assert_contains(api_base, "unsandbox.com")
|
||||
end
|
||||
|
||||
# Summary
|
||||
println("\n=== Summary ===")
|
||||
println("Passed: $(passed[])")
|
||||
println("Failed: $(failed[])")
|
||||
println("Total: $(passed[] + failed[])")
|
||||
|
||||
exit(failed[] > 0 ? 1 : 0)
|
||||
212
tests/unit/test_perl.pl
Executable file
212
tests/unit/test_perl.pl
Executable file
|
|
@ -0,0 +1,212 @@
|
|||
#!/usr/bin/env perl
|
||||
# Unit tests for un.pl - tests internal functions without API calls
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Digest::SHA qw(hmac_sha256_hex);
|
||||
use MIME::Base64;
|
||||
use File::Basename;
|
||||
use File::Temp qw(tempfile);
|
||||
|
||||
my $passed = 0;
|
||||
my $failed = 0;
|
||||
|
||||
sub test {
|
||||
my ($name, $fn) = @_;
|
||||
eval { $fn->(); };
|
||||
if ($@) {
|
||||
print " ✗ $name\n";
|
||||
print " $@";
|
||||
$failed++;
|
||||
} else {
|
||||
print " ✓ $name\n";
|
||||
$passed++;
|
||||
}
|
||||
}
|
||||
|
||||
sub assert_equal {
|
||||
my ($actual, $expected) = @_;
|
||||
die "Expected '$expected' but got '$actual'\n" unless $actual eq $expected;
|
||||
}
|
||||
|
||||
sub assert_not_equal {
|
||||
my ($a, $b) = @_;
|
||||
die "Expected values to be different but both were '$a'\n" if $a eq $b;
|
||||
}
|
||||
|
||||
sub assert_contains {
|
||||
my ($str, $substr) = @_;
|
||||
die "Expected '$str' to contain '$substr'\n" unless index($str, $substr) != -1;
|
||||
}
|
||||
|
||||
sub assert_true {
|
||||
my ($val) = @_;
|
||||
die "Expected true but got false\n" unless $val;
|
||||
}
|
||||
|
||||
# Extension mapping
|
||||
my %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",
|
||||
);
|
||||
|
||||
print "\n=== Extension Mapping Tests ===\n";
|
||||
|
||||
test("Python extension maps correctly", sub {
|
||||
assert_equal($EXT_MAP{".py"}, "python");
|
||||
});
|
||||
|
||||
test("JavaScript extensions map correctly", sub {
|
||||
assert_equal($EXT_MAP{".js"}, "javascript");
|
||||
assert_equal($EXT_MAP{".ts"}, "typescript");
|
||||
});
|
||||
|
||||
test("Perl extension maps correctly", sub {
|
||||
assert_equal($EXT_MAP{".pl"}, "perl");
|
||||
});
|
||||
|
||||
test("Ruby extension maps correctly", sub {
|
||||
assert_equal($EXT_MAP{".rb"}, "ruby");
|
||||
});
|
||||
|
||||
test("Go extension maps correctly", sub {
|
||||
assert_equal($EXT_MAP{".go"}, "go");
|
||||
});
|
||||
|
||||
test("C/C++ extensions map correctly", sub {
|
||||
assert_equal($EXT_MAP{".c"}, "c");
|
||||
assert_equal($EXT_MAP{".cpp"}, "cpp");
|
||||
});
|
||||
|
||||
test("JVM extensions map correctly", sub {
|
||||
assert_equal($EXT_MAP{".java"}, "java");
|
||||
assert_equal($EXT_MAP{".kt"}, "kotlin");
|
||||
});
|
||||
|
||||
test("Functional extensions map correctly", sub {
|
||||
assert_equal($EXT_MAP{".hs"}, "haskell");
|
||||
assert_equal($EXT_MAP{".clj"}, "clojure");
|
||||
assert_equal($EXT_MAP{".erl"}, "erlang");
|
||||
});
|
||||
|
||||
print "\n=== HMAC Signature Tests ===\n";
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string", sub {
|
||||
my $sig = hmac_sha256_hex("test-message", "test-secret");
|
||||
assert_equal(length($sig), 64);
|
||||
});
|
||||
|
||||
test("Same input produces same signature", sub {
|
||||
my $sig1 = hmac_sha256_hex("message", "key");
|
||||
my $sig2 = hmac_sha256_hex("message", "key");
|
||||
assert_equal($sig1, $sig2);
|
||||
});
|
||||
|
||||
test("Different secrets produce different signatures", sub {
|
||||
my $sig1 = hmac_sha256_hex("message", "key1");
|
||||
my $sig2 = hmac_sha256_hex("message", "key2");
|
||||
assert_not_equal($sig1, $sig2);
|
||||
});
|
||||
|
||||
test("Different messages produce different signatures", sub {
|
||||
my $sig1 = hmac_sha256_hex("message1", "key");
|
||||
my $sig2 = hmac_sha256_hex("message2", "key");
|
||||
assert_not_equal($sig1, $sig2);
|
||||
});
|
||||
|
||||
test("Signature format is timestamp:METHOD:path:body", sub {
|
||||
my $timestamp = "1704067200";
|
||||
my $method = "POST";
|
||||
my $endpoint = "/execute";
|
||||
my $body = '{"language":"python"}';
|
||||
|
||||
my $message = "$timestamp:$method:$endpoint:$body";
|
||||
|
||||
assert_true(index($message, $timestamp) == 0);
|
||||
assert_contains($message, ":POST:");
|
||||
assert_contains($message, ":/execute:");
|
||||
});
|
||||
|
||||
print "\n=== Language Detection Tests ===\n";
|
||||
|
||||
test("Detect language from .pl extension", sub {
|
||||
my ($name, $path, $suffix) = fileparse("script.pl", qr/\.[^.]*/);
|
||||
assert_equal($EXT_MAP{$suffix}, "perl");
|
||||
});
|
||||
|
||||
test("Python shebang detection", sub {
|
||||
my $content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
my ($firstLine) = split(/\n/, $content);
|
||||
assert_true(index($firstLine, "#!") == 0);
|
||||
assert_contains($firstLine, "python");
|
||||
});
|
||||
|
||||
test("Perl shebang detection", sub {
|
||||
my $content = "#!/usr/bin/env perl\nprint 'hello'";
|
||||
my ($firstLine) = split(/\n/, $content);
|
||||
assert_true(index($firstLine, "#!") == 0);
|
||||
assert_contains($firstLine, "perl");
|
||||
});
|
||||
|
||||
print "\n=== Argument Parsing Tests ===\n";
|
||||
|
||||
test("Parse -e KEY=VALUE format", sub {
|
||||
my $arg = "DEBUG=1";
|
||||
my ($key, $value) = split(/=/, $arg, 2);
|
||||
assert_equal($key, "DEBUG");
|
||||
assert_equal($value, "1");
|
||||
});
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value", sub {
|
||||
my $arg = "URL=https://example.com?foo=bar";
|
||||
my ($key, $value) = split(/=/, $arg, 2);
|
||||
assert_equal($key, "URL");
|
||||
assert_equal($value, "https://example.com?foo=bar");
|
||||
});
|
||||
|
||||
test("Valid network modes", sub {
|
||||
my %valid = (zerotrust => 1, semitrusted => 1);
|
||||
assert_true(exists $valid{zerotrust});
|
||||
assert_true(exists $valid{semitrusted});
|
||||
assert_true(!exists $valid{invalid});
|
||||
});
|
||||
|
||||
print "\n=== File Operations Tests ===\n";
|
||||
|
||||
test("Base64 encoding/decoding", sub {
|
||||
my $content = "print('hello world')";
|
||||
my $encoded = encode_base64($content, '');
|
||||
my $decoded = decode_base64($encoded);
|
||||
assert_equal($decoded, $content);
|
||||
});
|
||||
|
||||
test("Extract file basename", sub {
|
||||
my $path = "/home/user/project/script.pl";
|
||||
assert_equal(basename($path), "script.pl");
|
||||
});
|
||||
|
||||
print "\n=== API Constants Tests ===\n";
|
||||
|
||||
test("API base URL format", sub {
|
||||
my $apiBase = "https://api.unsandbox.com";
|
||||
assert_true(index($apiBase, "https://") == 0);
|
||||
assert_contains($apiBase, "unsandbox.com");
|
||||
});
|
||||
|
||||
print "\n=== Summary ===\n";
|
||||
print "Passed: $passed\n";
|
||||
print "Failed: $failed\n";
|
||||
print "Total: " . ($passed + $failed) . "\n";
|
||||
|
||||
exit($failed > 0 ? 1 : 0);
|
||||
210
tests/unit/test_php.php
Executable file
210
tests/unit/test_php.php
Executable file
|
|
@ -0,0 +1,210 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Unit tests for un.php - tests internal functions without API calls
|
||||
*/
|
||||
|
||||
$passed = 0;
|
||||
$failed = 0;
|
||||
|
||||
function test($name, $fn) {
|
||||
global $passed, $failed;
|
||||
try {
|
||||
$fn();
|
||||
echo " ✓ $name\n";
|
||||
$passed++;
|
||||
} catch (Exception $e) {
|
||||
echo " ✗ $name\n";
|
||||
echo " " . $e->getMessage() . "\n";
|
||||
$failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual($actual, $expected) {
|
||||
if ($actual !== $expected) {
|
||||
throw new Exception("Expected '$expected' but got '$actual'");
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotEqual($a, $b) {
|
||||
if ($a === $b) {
|
||||
throw new Exception("Expected values to be different but both were '$a'");
|
||||
}
|
||||
}
|
||||
|
||||
function assertContains($str, $substr) {
|
||||
if (strpos($str, $substr) === false) {
|
||||
throw new Exception("Expected '$str' to contain '$substr'");
|
||||
}
|
||||
}
|
||||
|
||||
function assertTrue($val) {
|
||||
if (!$val) {
|
||||
throw new Exception("Expected true but got false");
|
||||
}
|
||||
}
|
||||
|
||||
// Extension mapping
|
||||
$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",
|
||||
];
|
||||
|
||||
echo "\n=== Extension Mapping Tests ===\n";
|
||||
|
||||
test("Python extension maps correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".py"], "python");
|
||||
});
|
||||
|
||||
test("JavaScript extensions map correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".js"], "javascript");
|
||||
assertEqual($EXT_MAP[".ts"], "typescript");
|
||||
});
|
||||
|
||||
test("PHP extension maps correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".php"], "php");
|
||||
});
|
||||
|
||||
test("Ruby extension maps correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".rb"], "ruby");
|
||||
});
|
||||
|
||||
test("Go extension maps correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".go"], "go");
|
||||
});
|
||||
|
||||
test("C/C++ extensions map correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".c"], "c");
|
||||
assertEqual($EXT_MAP[".cpp"], "cpp");
|
||||
});
|
||||
|
||||
test("JVM extensions map correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".java"], "java");
|
||||
assertEqual($EXT_MAP[".kt"], "kotlin");
|
||||
});
|
||||
|
||||
test("Functional extensions map correctly", function() use ($EXT_MAP) {
|
||||
assertEqual($EXT_MAP[".hs"], "haskell");
|
||||
assertEqual($EXT_MAP[".clj"], "clojure");
|
||||
assertEqual($EXT_MAP[".erl"], "erlang");
|
||||
});
|
||||
|
||||
echo "\n=== HMAC Signature Tests ===\n";
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string", function() {
|
||||
$sig = hash_hmac('sha256', 'test-message', 'test-secret');
|
||||
assertEqual(strlen($sig), 64);
|
||||
});
|
||||
|
||||
test("Same input produces same signature", function() {
|
||||
$sig1 = hash_hmac('sha256', 'message', 'key');
|
||||
$sig2 = hash_hmac('sha256', 'message', 'key');
|
||||
assertEqual($sig1, $sig2);
|
||||
});
|
||||
|
||||
test("Different secrets produce different signatures", function() {
|
||||
$sig1 = hash_hmac('sha256', 'message', 'key1');
|
||||
$sig2 = hash_hmac('sha256', 'message', 'key2');
|
||||
assertNotEqual($sig1, $sig2);
|
||||
});
|
||||
|
||||
test("Different messages produce different signatures", function() {
|
||||
$sig1 = hash_hmac('sha256', 'message1', 'key');
|
||||
$sig2 = hash_hmac('sha256', 'message2', 'key');
|
||||
assertNotEqual($sig1, $sig2);
|
||||
});
|
||||
|
||||
test("Signature format is timestamp:METHOD:path:body", function() {
|
||||
$timestamp = "1704067200";
|
||||
$method = "POST";
|
||||
$endpoint = "/execute";
|
||||
$body = '{"language":"python"}';
|
||||
|
||||
$message = "$timestamp:$method:$endpoint:$body";
|
||||
|
||||
assertTrue(strpos($message, $timestamp) === 0);
|
||||
assertContains($message, ":POST:");
|
||||
assertContains($message, ":/execute:");
|
||||
});
|
||||
|
||||
echo "\n=== Language Detection Tests ===\n";
|
||||
|
||||
test("Detect language from .php extension", function() use ($EXT_MAP) {
|
||||
$ext = "." . pathinfo("script.php", PATHINFO_EXTENSION);
|
||||
assertEqual($EXT_MAP[$ext], "php");
|
||||
});
|
||||
|
||||
test("Python shebang detection", function() {
|
||||
$content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
$firstLine = explode("\n", $content)[0];
|
||||
assertTrue(strpos($firstLine, "#!") === 0);
|
||||
assertContains($firstLine, "python");
|
||||
});
|
||||
|
||||
echo "\n=== Argument Parsing Tests ===\n";
|
||||
|
||||
test("Parse -e KEY=VALUE format", function() {
|
||||
$arg = "DEBUG=1";
|
||||
$parts = explode("=", $arg, 2);
|
||||
assertEqual($parts[0], "DEBUG");
|
||||
assertEqual($parts[1], "1");
|
||||
});
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value", function() {
|
||||
$arg = "URL=https://example.com?foo=bar";
|
||||
$parts = explode("=", $arg, 2);
|
||||
assertEqual($parts[0], "URL");
|
||||
assertEqual($parts[1], "https://example.com?foo=bar");
|
||||
});
|
||||
|
||||
test("Valid network modes", function() {
|
||||
$validModes = ["zerotrust", "semitrusted"];
|
||||
assertTrue(in_array("zerotrust", $validModes));
|
||||
assertTrue(in_array("semitrusted", $validModes));
|
||||
assertTrue(!in_array("invalid", $validModes));
|
||||
});
|
||||
|
||||
echo "\n=== File Operations Tests ===\n";
|
||||
|
||||
test("Base64 encoding/decoding", function() {
|
||||
$content = "print('hello world')";
|
||||
$encoded = base64_encode($content);
|
||||
$decoded = base64_decode($encoded);
|
||||
assertEqual($decoded, $content);
|
||||
});
|
||||
|
||||
test("Extract file basename", function() {
|
||||
$path = "/home/user/project/script.php";
|
||||
assertEqual(basename($path), "script.php");
|
||||
});
|
||||
|
||||
test("Extract file extension", function() {
|
||||
$path = "/home/user/project/script.php";
|
||||
assertEqual(pathinfo($path, PATHINFO_EXTENSION), "php");
|
||||
});
|
||||
|
||||
echo "\n=== API Constants Tests ===\n";
|
||||
|
||||
test("API base URL format", function() {
|
||||
$apiBase = "https://api.unsandbox.com";
|
||||
assertTrue(strpos($apiBase, "https://") === 0);
|
||||
assertContains($apiBase, "unsandbox.com");
|
||||
});
|
||||
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "Passed: $passed\n";
|
||||
echo "Failed: $failed\n";
|
||||
echo "Total: " . ($passed + $failed) . "\n";
|
||||
|
||||
exit($failed > 0 ? 1 : 0);
|
||||
192
tests/unit/test_powershell.ps1
Executable file
192
tests/unit/test_powershell.ps1
Executable file
|
|
@ -0,0 +1,192 @@
|
|||
#!/usr/bin/env pwsh
|
||||
# Unit tests for un.ps1 - tests internal functions without API calls
|
||||
|
||||
$passed = 0
|
||||
$failed = 0
|
||||
|
||||
function Test-Case {
|
||||
param([string]$Name, [scriptblock]$Code)
|
||||
|
||||
try {
|
||||
& $Code
|
||||
Write-Host " ✓ $Name"
|
||||
$script:passed++
|
||||
}
|
||||
catch {
|
||||
Write-Host " ✗ $Name"
|
||||
Write-Host " $_"
|
||||
$script:failed++
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-Equal {
|
||||
param($Actual, $Expected)
|
||||
if ($Actual -ne $Expected) {
|
||||
throw "Expected '$Expected' but got '$Actual'"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-NotEqual {
|
||||
param($A, $B)
|
||||
if ($A -eq $B) {
|
||||
throw "Expected values to be different but both were '$A'"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-Contains {
|
||||
param([string]$Str, [string]$Substr)
|
||||
if (-not $Str.Contains($Substr)) {
|
||||
throw "Expected '$Str' to contain '$Substr'"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-True {
|
||||
param($Val)
|
||||
if (-not $Val) {
|
||||
throw "Expected true but got false"
|
||||
}
|
||||
}
|
||||
|
||||
# Extension mapping
|
||||
$ExtMap = @{
|
||||
".py" = "python"; ".js" = "javascript"; ".ts" = "typescript"
|
||||
".rb" = "ruby"; ".php" = "php"; ".pl" = "perl"; ".lua" = "lua"
|
||||
".sh" = "bash"; ".go" = "go"; ".rs" = "rust"; ".c" = "c"
|
||||
".cpp" = "cpp"; ".java" = "java"; ".kt" = "kotlin"
|
||||
".hs" = "haskell"; ".clj" = "clojure"; ".erl" = "erlang"
|
||||
".ex" = "elixir"; ".jl" = "julia"; ".ps1" = "powershell"
|
||||
}
|
||||
|
||||
Write-Host "`n=== Extension Mapping Tests ==="
|
||||
|
||||
Test-Case "Python extension maps correctly" {
|
||||
Assert-Equal $ExtMap[".py"] "python"
|
||||
}
|
||||
|
||||
Test-Case "PowerShell extension maps correctly" {
|
||||
Assert-Equal $ExtMap[".ps1"] "powershell"
|
||||
}
|
||||
|
||||
Test-Case "JavaScript extension maps correctly" {
|
||||
Assert-Equal $ExtMap[".js"] "javascript"
|
||||
}
|
||||
|
||||
Test-Case "Go extension maps correctly" {
|
||||
Assert-Equal $ExtMap[".go"] "go"
|
||||
}
|
||||
|
||||
Test-Case "Ruby extension maps correctly" {
|
||||
Assert-Equal $ExtMap[".rb"] "ruby"
|
||||
}
|
||||
|
||||
Write-Host "`n=== HMAC Signature Tests ==="
|
||||
|
||||
function Get-HmacSha256 {
|
||||
param([string]$Secret, [string]$Message)
|
||||
|
||||
$hmac = New-Object System.Security.Cryptography.HMACSHA256
|
||||
$hmac.Key = [System.Text.Encoding]::UTF8.GetBytes($Secret)
|
||||
$hash = $hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Message))
|
||||
return [BitConverter]::ToString($hash).Replace("-", "").ToLower()
|
||||
}
|
||||
|
||||
Test-Case "HMAC-SHA256 generates 64 character hex string" {
|
||||
$sig = Get-HmacSha256 "test-secret" "test-message"
|
||||
Assert-Equal $sig.Length 64
|
||||
}
|
||||
|
||||
Test-Case "Same input produces same signature" {
|
||||
$sig1 = Get-HmacSha256 "key" "msg"
|
||||
$sig2 = Get-HmacSha256 "key" "msg"
|
||||
Assert-Equal $sig1 $sig2
|
||||
}
|
||||
|
||||
Test-Case "Different secrets produce different signatures" {
|
||||
$sig1 = Get-HmacSha256 "key1" "msg"
|
||||
$sig2 = Get-HmacSha256 "key2" "msg"
|
||||
Assert-NotEqual $sig1 $sig2
|
||||
}
|
||||
|
||||
Test-Case "Signature format verification" {
|
||||
$timestamp = "1704067200"
|
||||
$method = "POST"
|
||||
$endpoint = "/execute"
|
||||
$body = '{"language":"python"}'
|
||||
|
||||
$message = "$timestamp`:$method`:$endpoint`:$body"
|
||||
|
||||
Assert-True $message.StartsWith($timestamp)
|
||||
Assert-Contains $message ":POST:"
|
||||
Assert-Contains $message ":/execute:"
|
||||
}
|
||||
|
||||
Write-Host "`n=== Language Detection Tests ==="
|
||||
|
||||
Test-Case "Detect language from .ps1 extension" {
|
||||
$filename = "script.ps1"
|
||||
$ext = [System.IO.Path]::GetExtension($filename)
|
||||
Assert-Equal $ExtMap[$ext] "powershell"
|
||||
}
|
||||
|
||||
Test-Case "Python shebang detection" {
|
||||
$content = "#!/usr/bin/env python3`nprint('hello')"
|
||||
$firstLine = $content.Split("`n")[0]
|
||||
Assert-True $firstLine.StartsWith("#!")
|
||||
Assert-Contains $firstLine "python"
|
||||
}
|
||||
|
||||
Write-Host "`n=== Argument Parsing Tests ==="
|
||||
|
||||
Test-Case "Parse -e KEY=VALUE format" {
|
||||
$arg = "DEBUG=1"
|
||||
$parts = $arg.Split("=", 2)
|
||||
$key = $parts[0]
|
||||
$value = $parts[1]
|
||||
Assert-Equal $key "DEBUG"
|
||||
Assert-Equal $value "1"
|
||||
}
|
||||
|
||||
Test-Case "Parse -e KEY=VALUE with equals in value" {
|
||||
$arg = "URL=https://example.com?foo=bar"
|
||||
$parts = $arg.Split("=", 2)
|
||||
$key = $parts[0]
|
||||
$value = $parts[1]
|
||||
Assert-Equal $key "URL"
|
||||
Assert-Equal $value "https://example.com?foo=bar"
|
||||
}
|
||||
|
||||
Write-Host "`n=== File Operations Tests ==="
|
||||
|
||||
Test-Case "Base64 encoding/decoding" {
|
||||
$content = "print('hello world')"
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($content)
|
||||
$encoded = [Convert]::ToBase64String($bytes)
|
||||
$decoded = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($encoded))
|
||||
Assert-Equal $decoded $content
|
||||
}
|
||||
|
||||
Test-Case "Extract file basename" {
|
||||
$path = "/home/user/project/script.ps1"
|
||||
Assert-Equal ([System.IO.Path]::GetFileName($path)) "script.ps1"
|
||||
}
|
||||
|
||||
Test-Case "Extract file extension" {
|
||||
$path = "/home/user/project/script.ps1"
|
||||
Assert-Equal ([System.IO.Path]::GetExtension($path)) ".ps1"
|
||||
}
|
||||
|
||||
Write-Host "`n=== API Constants Tests ==="
|
||||
|
||||
Test-Case "API base URL format" {
|
||||
$apiBase = "https://api.unsandbox.com"
|
||||
Assert-True $apiBase.StartsWith("https://")
|
||||
Assert-Contains $apiBase "unsandbox.com"
|
||||
}
|
||||
|
||||
# Summary
|
||||
Write-Host "`n=== Summary ==="
|
||||
Write-Host "Passed: $passed"
|
||||
Write-Host "Failed: $failed"
|
||||
Write-Host "Total: $($passed + $failed)"
|
||||
|
||||
exit $(if ($failed -gt 0) { 1 } else { 0 })
|
||||
179
tests/unit/test_r.R
Executable file
179
tests/unit/test_r.R
Executable file
|
|
@ -0,0 +1,179 @@
|
|||
#!/usr/bin/env Rscript
|
||||
# Unit tests for un.r - tests internal functions without API calls
|
||||
|
||||
library(openssl)
|
||||
library(base64enc)
|
||||
|
||||
passed <- 0
|
||||
failed <- 0
|
||||
|
||||
test <- function(name, fn) {
|
||||
tryCatch({
|
||||
fn()
|
||||
cat(sprintf(" ✓ %s\n", name))
|
||||
passed <<- passed + 1
|
||||
}, error = function(e) {
|
||||
cat(sprintf(" ✗ %s\n", name))
|
||||
cat(sprintf(" %s\n", e$message))
|
||||
failed <<- failed + 1
|
||||
})
|
||||
}
|
||||
|
||||
assert_equal <- function(actual, expected) {
|
||||
if (actual != expected) {
|
||||
stop(sprintf("Expected '%s' but got '%s'", expected, actual))
|
||||
}
|
||||
}
|
||||
|
||||
assert_not_equal <- function(a, b) {
|
||||
if (a == b) {
|
||||
stop(sprintf("Expected values to be different but both were '%s'", a))
|
||||
}
|
||||
}
|
||||
|
||||
assert_contains <- function(str, substr) {
|
||||
if (!grepl(substr, str, fixed = TRUE)) {
|
||||
stop(sprintf("Expected '%s' to contain '%s'", str, substr))
|
||||
}
|
||||
}
|
||||
|
||||
assert_true <- function(val) {
|
||||
if (!val) {
|
||||
stop("Expected true but got false")
|
||||
}
|
||||
}
|
||||
|
||||
# Extension mapping
|
||||
EXT_MAP <- list(
|
||||
".py" = "python", ".js" = "javascript", ".ts" = "typescript",
|
||||
".rb" = "ruby", ".php" = "php", ".pl" = "perl", ".lua" = "lua",
|
||||
".sh" = "bash", ".go" = "go", ".rs" = "rust", ".c" = "c",
|
||||
".cpp" = "cpp", ".java" = "java", ".kt" = "kotlin",
|
||||
".hs" = "haskell", ".clj" = "clojure", ".erl" = "erlang",
|
||||
".ex" = "elixir", ".jl" = "julia", ".r" = "r", ".R" = "r"
|
||||
)
|
||||
|
||||
cat("\n=== Extension Mapping Tests ===\n")
|
||||
|
||||
test("Python extension maps correctly", function() {
|
||||
assert_equal(EXT_MAP[[".py"]], "python")
|
||||
})
|
||||
|
||||
test("R extensions map correctly", function() {
|
||||
assert_equal(EXT_MAP[[".r"]], "r")
|
||||
assert_equal(EXT_MAP[[".R"]], "r")
|
||||
})
|
||||
|
||||
test("JavaScript extension maps correctly", function() {
|
||||
assert_equal(EXT_MAP[[".js"]], "javascript")
|
||||
})
|
||||
|
||||
test("Go extension maps correctly", function() {
|
||||
assert_equal(EXT_MAP[[".go"]], "go")
|
||||
})
|
||||
|
||||
test("Julia extension maps correctly", function() {
|
||||
assert_equal(EXT_MAP[[".jl"]], "julia")
|
||||
})
|
||||
|
||||
cat("\n=== HMAC Signature Tests ===\n")
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string", function() {
|
||||
sig <- sha256(charToRaw("test-message"), key = "test-secret")
|
||||
hex_sig <- paste0(as.character(sig), collapse = "")
|
||||
assert_equal(nchar(hex_sig), 64)
|
||||
})
|
||||
|
||||
test("Same input produces same signature", function() {
|
||||
sig1 <- sha256(charToRaw("msg"), key = "key")
|
||||
sig2 <- sha256(charToRaw("msg"), key = "key")
|
||||
assert_equal(paste0(sig1, collapse = ""), paste0(sig2, collapse = ""))
|
||||
})
|
||||
|
||||
test("Different secrets produce different signatures", function() {
|
||||
sig1 <- paste0(sha256(charToRaw("msg"), key = "key1"), collapse = "")
|
||||
sig2 <- paste0(sha256(charToRaw("msg"), key = "key2"), collapse = "")
|
||||
assert_not_equal(sig1, sig2)
|
||||
})
|
||||
|
||||
test("Signature format verification", function() {
|
||||
timestamp <- "1704067200"
|
||||
method <- "POST"
|
||||
endpoint <- "/execute"
|
||||
body <- '{"language":"python"}'
|
||||
|
||||
message <- paste(timestamp, method, endpoint, body, sep = ":")
|
||||
|
||||
assert_true(startsWith(message, timestamp))
|
||||
assert_contains(message, ":POST:")
|
||||
assert_contains(message, ":/execute:")
|
||||
})
|
||||
|
||||
cat("\n=== Language Detection Tests ===\n")
|
||||
|
||||
test("Detect language from .R extension", function() {
|
||||
ext <- paste0(".", tools::file_ext("script.R"))
|
||||
assert_equal(EXT_MAP[[ext]], "r")
|
||||
})
|
||||
|
||||
test("Python shebang detection", function() {
|
||||
content <- "#!/usr/bin/env python3\nprint('hello')"
|
||||
first_line <- strsplit(content, "\n")[[1]][1]
|
||||
assert_true(startsWith(first_line, "#!"))
|
||||
assert_contains(first_line, "python")
|
||||
})
|
||||
|
||||
cat("\n=== Argument Parsing Tests ===\n")
|
||||
|
||||
test("Parse -e KEY=VALUE format", function() {
|
||||
arg <- "DEBUG=1"
|
||||
parts <- strsplit(arg, "=", fixed = TRUE)[[1]]
|
||||
key <- parts[1]
|
||||
value <- paste(parts[-1], collapse = "=")
|
||||
assert_equal(key, "DEBUG")
|
||||
assert_equal(value, "1")
|
||||
})
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value", function() {
|
||||
arg <- "URL=https://example.com?foo=bar"
|
||||
parts <- strsplit(arg, "=", fixed = TRUE)[[1]]
|
||||
key <- parts[1]
|
||||
value <- paste(parts[-1], collapse = "=")
|
||||
assert_equal(key, "URL")
|
||||
assert_equal(value, "https://example.com?foo=bar")
|
||||
})
|
||||
|
||||
cat("\n=== File Operations Tests ===\n")
|
||||
|
||||
test("Base64 encoding/decoding", function() {
|
||||
content <- "print('hello world')"
|
||||
encoded <- base64encode(charToRaw(content))
|
||||
decoded <- rawToChar(base64decode(encoded))
|
||||
assert_equal(decoded, content)
|
||||
})
|
||||
|
||||
test("Extract file basename", function() {
|
||||
path <- "/home/user/project/script.R"
|
||||
assert_equal(basename(path), "script.R")
|
||||
})
|
||||
|
||||
test("Extract file extension", function() {
|
||||
path <- "/home/user/project/script.R"
|
||||
assert_equal(tools::file_ext(path), "R")
|
||||
})
|
||||
|
||||
cat("\n=== API Constants Tests ===\n")
|
||||
|
||||
test("API base URL format", function() {
|
||||
api_base <- "https://api.unsandbox.com"
|
||||
assert_true(startsWith(api_base, "https://"))
|
||||
assert_contains(api_base, "unsandbox.com")
|
||||
})
|
||||
|
||||
# Summary
|
||||
cat("\n=== Summary ===\n")
|
||||
cat(sprintf("Passed: %d\n", passed))
|
||||
cat(sprintf("Failed: %d\n", failed))
|
||||
cat(sprintf("Total: %d\n", passed + failed))
|
||||
|
||||
quit(status = if (failed > 0) 1 else 0)
|
||||
183
tests/unit/test_tcl.tcl
Executable file
183
tests/unit/test_tcl.tcl
Executable file
|
|
@ -0,0 +1,183 @@
|
|||
#!/usr/bin/env tclsh
|
||||
# Unit tests for un.tcl - tests internal functions without API calls
|
||||
|
||||
package require Tcl 8.5
|
||||
|
||||
set passed 0
|
||||
set failed 0
|
||||
|
||||
proc test {name body} {
|
||||
global passed failed
|
||||
if {[catch {uplevel 1 $body} err]} {
|
||||
puts " ✗ $name"
|
||||
puts " $err"
|
||||
incr failed
|
||||
} else {
|
||||
puts " ✓ $name"
|
||||
incr passed
|
||||
}
|
||||
}
|
||||
|
||||
proc assert_equal {actual expected} {
|
||||
if {$actual ne $expected} {
|
||||
error "Expected '$expected' but got '$actual'"
|
||||
}
|
||||
}
|
||||
|
||||
proc assert_not_equal {a b} {
|
||||
if {$a eq $b} {
|
||||
error "Expected values to be different but both were '$a'"
|
||||
}
|
||||
}
|
||||
|
||||
proc assert_contains {str substr} {
|
||||
if {[string first $substr $str] == -1} {
|
||||
error "Expected '$str' to contain '$substr'"
|
||||
}
|
||||
}
|
||||
|
||||
proc assert_true {val} {
|
||||
if {!$val} {
|
||||
error "Expected true but got false"
|
||||
}
|
||||
}
|
||||
|
||||
# Extension mapping
|
||||
array set 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 .java java .kt kotlin
|
||||
.hs haskell .clj clojure .erl erlang
|
||||
.ex elixir .jl julia .tcl tcl
|
||||
}
|
||||
|
||||
puts "\n=== Extension Mapping Tests ==="
|
||||
|
||||
test "Python extension maps correctly" {
|
||||
assert_equal $ext_map(.py) "python"
|
||||
}
|
||||
|
||||
test "TCL extension maps correctly" {
|
||||
assert_equal $ext_map(.tcl) "tcl"
|
||||
}
|
||||
|
||||
test "JavaScript extension maps correctly" {
|
||||
assert_equal $ext_map(.js) "javascript"
|
||||
}
|
||||
|
||||
test "Go extension maps correctly" {
|
||||
assert_equal $ext_map(.go) "go"
|
||||
}
|
||||
|
||||
test "Ruby extension maps correctly" {
|
||||
assert_equal $ext_map(.rb) "ruby"
|
||||
}
|
||||
|
||||
puts "\n=== HMAC Signature Tests ==="
|
||||
|
||||
# TCL needs external command for HMAC
|
||||
proc hmac_sha256 {secret message} {
|
||||
set cmd [list echo -n $message | openssl dgst -sha256 -hmac $secret]
|
||||
catch {exec sh -c "echo -n '$message' | openssl dgst -sha256 -hmac '$secret' 2>/dev/null | sed 's/^.* //'"} result
|
||||
return [string trim $result]
|
||||
}
|
||||
|
||||
test "HMAC-SHA256 generates 64 character hex string" {
|
||||
set sig [hmac_sha256 "test-secret" "test-message"]
|
||||
if {$sig ne ""} {
|
||||
assert_equal [string length $sig] 64
|
||||
}
|
||||
}
|
||||
|
||||
test "Same input produces same signature" {
|
||||
set sig1 [hmac_sha256 "key" "msg"]
|
||||
set sig2 [hmac_sha256 "key" "msg"]
|
||||
if {$sig1 ne "" && $sig2 ne ""} {
|
||||
assert_equal $sig1 $sig2
|
||||
}
|
||||
}
|
||||
|
||||
test "Different secrets produce different signatures" {
|
||||
set sig1 [hmac_sha256 "key1" "msg"]
|
||||
set sig2 [hmac_sha256 "key2" "msg"]
|
||||
if {$sig1 ne "" && $sig2 ne ""} {
|
||||
assert_not_equal $sig1 $sig2
|
||||
}
|
||||
}
|
||||
|
||||
test "Signature format verification" {
|
||||
set timestamp "1704067200"
|
||||
set method "POST"
|
||||
set endpoint "/execute"
|
||||
set body "{\"language\":\"python\"}"
|
||||
|
||||
set message "$timestamp:$method:$endpoint:$body"
|
||||
|
||||
assert_true [string match "$timestamp*" $message]
|
||||
assert_contains $message ":POST:"
|
||||
assert_contains $message ":/execute:"
|
||||
}
|
||||
|
||||
puts "\n=== Language Detection Tests ==="
|
||||
|
||||
test "Detect language from .tcl extension" {
|
||||
set filename "script.tcl"
|
||||
set ext ".[lindex [split $filename .] end]"
|
||||
assert_equal $ext_map($ext) "tcl"
|
||||
}
|
||||
|
||||
test "Python shebang detection" {
|
||||
set content "#!/usr/bin/env python3\nprint('hello')"
|
||||
set first_line [lindex [split $content "\n"] 0]
|
||||
assert_true [string match "#!*" $first_line]
|
||||
assert_contains $first_line "python"
|
||||
}
|
||||
|
||||
puts "\n=== Argument Parsing Tests ==="
|
||||
|
||||
test "Parse -e KEY=VALUE format" {
|
||||
set arg "DEBUG=1"
|
||||
set parts [split $arg "="]
|
||||
set key [lindex $parts 0]
|
||||
set value [join [lrange $parts 1 end] "="]
|
||||
assert_equal $key "DEBUG"
|
||||
assert_equal $value "1"
|
||||
}
|
||||
|
||||
test "Parse -e KEY=VALUE with equals in value" {
|
||||
set arg "URL=https://example.com?foo=bar"
|
||||
set parts [split $arg "="]
|
||||
set key [lindex $parts 0]
|
||||
set value [join [lrange $parts 1 end] "="]
|
||||
assert_equal $key "URL"
|
||||
assert_equal $value "https://example.com?foo=bar"
|
||||
}
|
||||
|
||||
puts "\n=== File Operations Tests ==="
|
||||
|
||||
test "Extract file basename" {
|
||||
set path "/home/user/project/script.tcl"
|
||||
assert_equal [file tail $path] "script.tcl"
|
||||
}
|
||||
|
||||
test "Extract file extension" {
|
||||
set path "/home/user/project/script.tcl"
|
||||
assert_equal [file extension $path] ".tcl"
|
||||
}
|
||||
|
||||
puts "\n=== API Constants Tests ==="
|
||||
|
||||
test "API base URL format" {
|
||||
set api_base "https://api.unsandbox.com"
|
||||
assert_true [string match "https://*" $api_base]
|
||||
assert_contains $api_base "unsandbox.com"
|
||||
}
|
||||
|
||||
# Summary
|
||||
puts "\n=== Summary ==="
|
||||
puts "Passed: $passed"
|
||||
puts "Failed: $failed"
|
||||
puts "Total: [expr {$passed + $failed}]"
|
||||
|
||||
exit [expr {$failed > 0 ? 1 : 0}]
|
||||
191
tests/unit/test_typescript.ts
Executable file
191
tests/unit/test_typescript.ts
Executable file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env npx ts-node
|
||||
/**
|
||||
* Unit tests for un.ts - tests internal functions without API calls
|
||||
*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(name: string, fn: () => void): void {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
passed++;
|
||||
} catch (e: any) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` ${e.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual(actual: any, expected: any): void {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`Expected "${expected}" but got "${actual}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotEqual(a: any, b: any): void {
|
||||
if (a === b) {
|
||||
throw new Error(`Expected values to be different but both were "${a}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertIncludes(str: string, substr: string): void {
|
||||
if (!str.includes(substr)) {
|
||||
throw new Error(`Expected "${str}" to include "${substr}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertTrue(val: boolean): void {
|
||||
if (!val) {
|
||||
throw new Error(`Expected true but got false`);
|
||||
}
|
||||
}
|
||||
|
||||
const EXT_MAP: Record<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",
|
||||
};
|
||||
|
||||
console.log('\n=== Extension Mapping Tests ===');
|
||||
|
||||
test('Python extension maps correctly', () => {
|
||||
assertEqual(EXT_MAP['.py'], 'python');
|
||||
});
|
||||
|
||||
test('TypeScript extension maps correctly', () => {
|
||||
assertEqual(EXT_MAP['.ts'], 'typescript');
|
||||
});
|
||||
|
||||
test('JavaScript extension maps correctly', () => {
|
||||
assertEqual(EXT_MAP['.js'], 'javascript');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
console.log('\n=== HMAC Signature Tests ===');
|
||||
|
||||
test('HMAC-SHA256 generates 64 character hex string', () => {
|
||||
const sig = crypto.createHmac('sha256', 'test-secret')
|
||||
.update('test-message')
|
||||
.digest('hex');
|
||||
assertEqual(sig.length, 64);
|
||||
});
|
||||
|
||||
test('Same input produces same signature', () => {
|
||||
const sig1 = crypto.createHmac('sha256', 'key').update('msg').digest('hex');
|
||||
const sig2 = crypto.createHmac('sha256', 'key').update('msg').digest('hex');
|
||||
assertEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
test('Different secrets produce different signatures', () => {
|
||||
const sig1 = crypto.createHmac('sha256', 'key1').update('msg').digest('hex');
|
||||
const sig2 = crypto.createHmac('sha256', 'key2').update('msg').digest('hex');
|
||||
assertNotEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
test('Signature format verification', () => {
|
||||
const timestamp = '1704067200';
|
||||
const method = 'POST';
|
||||
const endpoint = '/execute';
|
||||
const body = '{"language":"python"}';
|
||||
|
||||
const message = `${timestamp}:${method}:${endpoint}:${body}`;
|
||||
|
||||
assertTrue(message.startsWith(timestamp));
|
||||
assertIncludes(message, ':POST:');
|
||||
assertIncludes(message, ':/execute:');
|
||||
});
|
||||
|
||||
console.log('\n=== Language Detection Tests ===');
|
||||
|
||||
test('Detect language from .ts extension', () => {
|
||||
const ext = path.extname('script.ts').toLowerCase();
|
||||
assertEqual(EXT_MAP[ext], 'typescript');
|
||||
});
|
||||
|
||||
test('Python shebang detection', () => {
|
||||
const content = '#!/usr/bin/env python3\nprint("hello")';
|
||||
const firstLine = content.split('\n')[0];
|
||||
assertTrue(firstLine.startsWith('#!'));
|
||||
assertIncludes(firstLine, 'python');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
console.log('\n=== File Operations Tests ===');
|
||||
|
||||
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.ts';
|
||||
assertEqual(path.basename(filepath), 'script.ts');
|
||||
});
|
||||
|
||||
test('Extract file extension', () => {
|
||||
const filepath = '/home/user/project/script.ts';
|
||||
assertEqual(path.extname(filepath), '.ts');
|
||||
});
|
||||
|
||||
console.log('\n=== API Constants Tests ===');
|
||||
|
||||
test('API base URL format', () => {
|
||||
const API_BASE = 'https://api.unsandbox.com';
|
||||
assertTrue(API_BASE.startsWith('https://'));
|
||||
assertIncludes(API_BASE, 'unsandbox.com');
|
||||
});
|
||||
|
||||
console.log('\n=== Summary ===');
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
console.log(`Total: ${passed + failed}`);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
Loading…
Add table
Add a link
Reference in a new issue