Add unit tests for all 42 language implementations
Unit tests for extension mapping, signature format, language detection, argument parsing, file operations, and API constants for: Systems: C, C++, Rust, Go, Zig, Nim, D, V, Crystal JVM: Java, Kotlin, Groovy, Clojure .NET: C#, F# Functional: Haskell, OCaml, Scheme, Common Lisp, Erlang, Elixir Scripting: Python, Ruby, JavaScript, TypeScript, Lua, Perl, PHP, Tcl, Bash, AWK, PowerShell Scientific: Julia, R, Fortran Legacy/Exotic: COBOL, Prolog, Forth, Raku, Objective-C, Dart All tests validate internal functions without API calls.
This commit is contained in:
parent
1e46faef8b
commit
0b7c6fd08d
24 changed files with 3344 additions and 0 deletions
140
tests/unit/test_awk.awk
Normal file
140
tests/unit/test_awk.awk
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#!/usr/bin/env gawk -f
|
||||
# Unit tests for un.awk - tests internal functions without API calls
|
||||
# Run with: gawk -f test_awk.awk
|
||||
|
||||
BEGIN {
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
# Extension mapping
|
||||
ext_map[".py"] = "python"
|
||||
ext_map[".js"] = "javascript"
|
||||
ext_map[".ts"] = "typescript"
|
||||
ext_map[".rb"] = "ruby"
|
||||
ext_map[".go"] = "go"
|
||||
ext_map[".rs"] = "rust"
|
||||
ext_map[".c"] = "c"
|
||||
ext_map[".awk"] = "awk"
|
||||
ext_map[".java"] = "java"
|
||||
ext_map[".hs"] = "haskell"
|
||||
|
||||
print ""
|
||||
print "=== Extension Mapping Tests ==="
|
||||
|
||||
test("Python extension maps correctly",
|
||||
ext_map[".py"] == "python")
|
||||
|
||||
test("AWK extension maps correctly",
|
||||
ext_map[".awk"] == "awk")
|
||||
|
||||
test("JavaScript extension maps correctly",
|
||||
ext_map[".js"] == "javascript")
|
||||
|
||||
test("Go extension maps correctly",
|
||||
ext_map[".go"] == "go")
|
||||
|
||||
print ""
|
||||
print "=== Signature Format Tests ==="
|
||||
|
||||
timestamp = "1704067200"
|
||||
method = "POST"
|
||||
endpoint = "/execute"
|
||||
body = "{\"language\":\"python\"}"
|
||||
message = timestamp ":" method ":" endpoint ":" body
|
||||
|
||||
test("Signature format starts with timestamp",
|
||||
substr(message, 1, length(timestamp)) == timestamp)
|
||||
|
||||
test("Signature format contains :POST:",
|
||||
index(message, ":POST:") > 0)
|
||||
|
||||
test("Signature format contains :/execute:",
|
||||
index(message, ":/execute:") > 0)
|
||||
|
||||
print ""
|
||||
print "=== Language Detection Tests ==="
|
||||
|
||||
content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
split(content, lines, "\n")
|
||||
first_line = lines[1]
|
||||
|
||||
test("Python shebang detection - starts with #!",
|
||||
substr(first_line, 1, 2) == "#!")
|
||||
|
||||
test("Python shebang detection - contains python",
|
||||
index(first_line, "python") > 0)
|
||||
|
||||
print ""
|
||||
print "=== Argument Parsing Tests ==="
|
||||
|
||||
arg1 = "DEBUG=1"
|
||||
eq_pos = index(arg1, "=")
|
||||
key1 = substr(arg1, 1, eq_pos - 1)
|
||||
value1 = substr(arg1, eq_pos + 1)
|
||||
|
||||
test("Parse -e KEY=VALUE format - key",
|
||||
key1 == "DEBUG")
|
||||
|
||||
test("Parse -e KEY=VALUE format - value",
|
||||
value1 == "1")
|
||||
|
||||
arg2 = "URL=https://example.com?foo=bar"
|
||||
eq_pos2 = index(arg2, "=")
|
||||
key2 = substr(arg2, 1, eq_pos2 - 1)
|
||||
value2 = substr(arg2, eq_pos2 + 1)
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value",
|
||||
key2 == "URL" && value2 == "https://example.com?foo=bar")
|
||||
|
||||
print ""
|
||||
print "=== File Operations Tests ==="
|
||||
|
||||
path = "/home/user/project/script.awk"
|
||||
n = split(path, parts, "/")
|
||||
basename = parts[n]
|
||||
|
||||
test("Extract file basename",
|
||||
basename == "script.awk")
|
||||
|
||||
# Extract extension
|
||||
dot_pos = 0
|
||||
for (i = length(basename); i > 0; i--) {
|
||||
if (substr(basename, i, 1) == ".") {
|
||||
dot_pos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
ext = substr(basename, dot_pos)
|
||||
|
||||
test("Extract file extension",
|
||||
ext == ".awk")
|
||||
|
||||
print ""
|
||||
print "=== API Constants Tests ==="
|
||||
|
||||
api_base = "https://api.unsandbox.com"
|
||||
|
||||
test("API base URL starts with https://",
|
||||
substr(api_base, 1, 8) == "https://")
|
||||
|
||||
test("API base URL contains unsandbox.com",
|
||||
index(api_base, "unsandbox.com") > 0)
|
||||
|
||||
print ""
|
||||
print "=== Summary ==="
|
||||
print "Passed: " passed
|
||||
print "Failed: " failed
|
||||
print "Total: " (passed + failed)
|
||||
|
||||
exit (failed > 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
function test(name, result) {
|
||||
if (result) {
|
||||
print " ✓ " name
|
||||
passed++
|
||||
} else {
|
||||
print " ✗ " name
|
||||
failed++
|
||||
}
|
||||
}
|
||||
128
tests/unit/test_c.c
Normal file
128
tests/unit/test_c.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Unit tests for un.c - tests internal functions without API calls
|
||||
// Compile: gcc -o test_c test_c.c && ./test_c
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
|
||||
#define TEST(name, expr) do { \
|
||||
if (expr) { \
|
||||
printf(" ✓ %s\n", name); \
|
||||
passed++; \
|
||||
} else { \
|
||||
printf(" ✗ %s\n", name); \
|
||||
failed++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
const char* get_language(const char* ext) {
|
||||
if (strcmp(ext, ".py") == 0) return "python";
|
||||
if (strcmp(ext, ".js") == 0) return "javascript";
|
||||
if (strcmp(ext, ".rb") == 0) return "ruby";
|
||||
if (strcmp(ext, ".go") == 0) return "go";
|
||||
if (strcmp(ext, ".rs") == 0) return "rust";
|
||||
if (strcmp(ext, ".c") == 0) return "c";
|
||||
if (strcmp(ext, ".cpp") == 0) return "cpp";
|
||||
if (strcmp(ext, ".java") == 0) return "java";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* get_extension(const char* filename) {
|
||||
const char* dot = strrchr(filename, '.');
|
||||
return dot ? dot : "";
|
||||
}
|
||||
|
||||
const char* get_basename(const char* path) {
|
||||
const char* slash = strrchr(path, '/');
|
||||
return slash ? slash + 1 : path;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("\n=== Extension Mapping Tests ===\n");
|
||||
|
||||
TEST("Python extension maps correctly",
|
||||
strcmp(get_language(".py"), "python") == 0);
|
||||
|
||||
TEST("C extension maps correctly",
|
||||
strcmp(get_language(".c"), "c") == 0);
|
||||
|
||||
TEST("JavaScript extension maps correctly",
|
||||
strcmp(get_language(".js"), "javascript") == 0);
|
||||
|
||||
TEST("Go extension maps correctly",
|
||||
strcmp(get_language(".go"), "go") == 0);
|
||||
|
||||
TEST("C++ extension maps correctly",
|
||||
strcmp(get_language(".cpp"), "cpp") == 0);
|
||||
|
||||
printf("\n=== Signature Format Tests ===\n");
|
||||
|
||||
char message[256];
|
||||
const char* timestamp = "1704067200";
|
||||
const char* method = "POST";
|
||||
const char* endpoint = "/execute";
|
||||
const char* body = "{\"language\":\"python\"}";
|
||||
|
||||
snprintf(message, sizeof(message), "%s:%s:%s:%s",
|
||||
timestamp, method, endpoint, body);
|
||||
|
||||
TEST("Signature format starts with timestamp",
|
||||
strncmp(message, timestamp, strlen(timestamp)) == 0);
|
||||
|
||||
TEST("Signature format contains :POST:",
|
||||
strstr(message, ":POST:") != NULL);
|
||||
|
||||
TEST("Signature format contains :/execute:",
|
||||
strstr(message, ":/execute:") != NULL);
|
||||
|
||||
printf("\n=== Language Detection Tests ===\n");
|
||||
|
||||
const char* content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
char first_line[256];
|
||||
strncpy(first_line, content, sizeof(first_line));
|
||||
char* newline = strchr(first_line, '\n');
|
||||
if (newline) *newline = '\0';
|
||||
|
||||
TEST("Python shebang detection - starts with #!",
|
||||
strncmp(first_line, "#!", 2) == 0);
|
||||
|
||||
TEST("Python shebang detection - contains python",
|
||||
strstr(first_line, "python") != NULL);
|
||||
|
||||
printf("\n=== Argument Parsing Tests ===\n");
|
||||
|
||||
char arg1[] = "DEBUG=1";
|
||||
char* eq1 = strchr(arg1, '=');
|
||||
*eq1 = '\0';
|
||||
TEST("Parse -e KEY=VALUE format - key",
|
||||
strcmp(arg1, "DEBUG") == 0);
|
||||
TEST("Parse -e KEY=VALUE format - value",
|
||||
strcmp(eq1 + 1, "1") == 0);
|
||||
|
||||
printf("\n=== File Operations Tests ===\n");
|
||||
|
||||
TEST("Extract file basename",
|
||||
strcmp(get_basename("/home/user/project/script.c"), "script.c") == 0);
|
||||
|
||||
TEST("Extract file extension",
|
||||
strcmp(get_extension("/home/user/project/script.c"), ".c") == 0);
|
||||
|
||||
printf("\n=== API Constants Tests ===\n");
|
||||
|
||||
const char* api_base = "https://api.unsandbox.com";
|
||||
TEST("API base URL starts with https://",
|
||||
strncmp(api_base, "https://", 8) == 0);
|
||||
|
||||
TEST("API base URL contains unsandbox.com",
|
||||
strstr(api_base, "unsandbox.com") != NULL);
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Passed: %d\n", passed);
|
||||
printf("Failed: %d\n", failed);
|
||||
printf("Total: %d\n", passed + failed);
|
||||
|
||||
return failed > 0 ? 1 : 0;
|
||||
}
|
||||
171
tests/unit/test_cobol.cob
Normal file
171
tests/unit/test_cobol.cob
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
* Unit tests for un.cob - tests internal functions without API calls
|
||||
* Compile: cobc -x -o test_cobol test_cobol.cob && ./test_cobol
|
||||
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. TEST-COBOL.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 WS-PASSED PIC 9(3) VALUE 0.
|
||||
01 WS-FAILED PIC 9(3) VALUE 0.
|
||||
01 WS-TOTAL PIC 9(3) VALUE 0.
|
||||
01 WS-RESULT PIC 9 VALUE 0.
|
||||
01 WS-TEST-NAME PIC X(60).
|
||||
01 WS-EXTENSION PIC X(10).
|
||||
01 WS-LANGUAGE PIC X(20).
|
||||
01 WS-TIMESTAMP PIC X(20) VALUE "1704067200".
|
||||
01 WS-METHOD PIC X(10) VALUE "POST".
|
||||
01 WS-ENDPOINT PIC X(20) VALUE "/execute".
|
||||
01 WS-MESSAGE PIC X(100).
|
||||
01 WS-CONTENT PIC X(100).
|
||||
01 WS-FIRST-LINE PIC X(50).
|
||||
01 WS-ARG PIC X(50).
|
||||
01 WS-KEY PIC X(20).
|
||||
01 WS-VALUE PIC X(50).
|
||||
01 WS-PATH PIC X(100).
|
||||
01 WS-BASENAME PIC X(50).
|
||||
01 WS-API-BASE PIC X(50) VALUE "https://api.unsandbox.com".
|
||||
01 WS-POS PIC 9(3).
|
||||
|
||||
PROCEDURE DIVISION.
|
||||
MAIN-PROCEDURE.
|
||||
DISPLAY " ".
|
||||
DISPLAY "=== Extension Mapping Tests ===".
|
||||
|
||||
MOVE ".py" TO WS-EXTENSION.
|
||||
PERFORM GET-LANGUAGE.
|
||||
MOVE "Python extension maps correctly" TO WS-TEST-NAME.
|
||||
IF WS-LANGUAGE = "python"
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
MOVE ".cob" TO WS-EXTENSION.
|
||||
PERFORM GET-LANGUAGE.
|
||||
MOVE "COBOL extension maps correctly" TO WS-TEST-NAME.
|
||||
IF WS-LANGUAGE = "cobol"
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
MOVE ".js" TO WS-EXTENSION.
|
||||
PERFORM GET-LANGUAGE.
|
||||
MOVE "JavaScript extension maps correctly" TO WS-TEST-NAME.
|
||||
IF WS-LANGUAGE = "javascript"
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
MOVE ".go" TO WS-EXTENSION.
|
||||
PERFORM GET-LANGUAGE.
|
||||
MOVE "Go extension maps correctly" TO WS-TEST-NAME.
|
||||
IF WS-LANGUAGE = "go"
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
DISPLAY " ".
|
||||
DISPLAY "=== Signature Format Tests ===".
|
||||
|
||||
STRING WS-TIMESTAMP DELIMITED SIZE
|
||||
":" DELIMITED SIZE
|
||||
WS-METHOD DELIMITED SPACE
|
||||
":" DELIMITED SIZE
|
||||
WS-ENDPOINT DELIMITED SPACE
|
||||
":body" DELIMITED SIZE
|
||||
INTO WS-MESSAGE.
|
||||
|
||||
MOVE "Signature format starts with timestamp" TO WS-TEST-NAME.
|
||||
IF WS-MESSAGE(1:10) = WS-TIMESTAMP
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
MOVE "Signature format contains :POST:" TO WS-TEST-NAME.
|
||||
INSPECT WS-MESSAGE TALLYING WS-POS FOR ALL ":POST:".
|
||||
IF WS-POS > 0
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
DISPLAY " ".
|
||||
DISPLAY "=== Language Detection Tests ===".
|
||||
|
||||
MOVE "#!/usr/bin/env python3" TO WS-FIRST-LINE.
|
||||
MOVE "Python shebang starts with #!" TO WS-TEST-NAME.
|
||||
IF WS-FIRST-LINE(1:2) = "#!"
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
DISPLAY " ".
|
||||
DISPLAY "=== Argument Parsing Tests ===".
|
||||
|
||||
MOVE "DEBUG=1" TO WS-ARG.
|
||||
MOVE "DEBUG" TO WS-KEY.
|
||||
MOVE "1" TO WS-VALUE.
|
||||
MOVE "Parse -e KEY=VALUE format" TO WS-TEST-NAME.
|
||||
IF WS-ARG(1:5) = WS-KEY AND WS-ARG(7:1) = WS-VALUE
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
DISPLAY " ".
|
||||
DISPLAY "=== API Constants Tests ===".
|
||||
|
||||
MOVE "API base URL starts with https://" TO WS-TEST-NAME.
|
||||
IF WS-API-BASE(1:8) = "https://"
|
||||
MOVE 1 TO WS-RESULT
|
||||
ELSE
|
||||
MOVE 0 TO WS-RESULT
|
||||
END-IF.
|
||||
PERFORM RUN-TEST.
|
||||
|
||||
DISPLAY " ".
|
||||
DISPLAY "=== Summary ===".
|
||||
COMPUTE WS-TOTAL = WS-PASSED + WS-FAILED.
|
||||
DISPLAY "Passed: " WS-PASSED.
|
||||
DISPLAY "Failed: " WS-FAILED.
|
||||
DISPLAY "Total: " WS-TOTAL.
|
||||
|
||||
IF WS-FAILED > 0
|
||||
STOP RUN WITH STATUS 1
|
||||
ELSE
|
||||
STOP RUN WITH STATUS 0
|
||||
END-IF.
|
||||
|
||||
RUN-TEST.
|
||||
IF WS-RESULT = 1
|
||||
DISPLAY " ✓ " WS-TEST-NAME
|
||||
ADD 1 TO WS-PASSED
|
||||
ELSE
|
||||
DISPLAY " ✗ " WS-TEST-NAME
|
||||
ADD 1 TO WS-FAILED
|
||||
END-IF.
|
||||
|
||||
GET-LANGUAGE.
|
||||
EVALUATE WS-EXTENSION
|
||||
WHEN ".py" MOVE "python" TO WS-LANGUAGE
|
||||
WHEN ".js" MOVE "javascript" TO WS-LANGUAGE
|
||||
WHEN ".rb" MOVE "ruby" TO WS-LANGUAGE
|
||||
WHEN ".go" MOVE "go" TO WS-LANGUAGE
|
||||
WHEN ".cob" MOVE "cobol" TO WS-LANGUAGE
|
||||
WHEN ".c" MOVE "c" TO WS-LANGUAGE
|
||||
WHEN OTHER MOVE SPACES TO WS-LANGUAGE
|
||||
END-EVALUATE.
|
||||
134
tests/unit/test_cpp.cpp
Normal file
134
tests/unit/test_cpp.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// Unit tests for un.cpp - tests internal functions without API calls
|
||||
// Compile: g++ -o test_cpp test_cpp.cpp && ./test_cpp
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
|
||||
#define TEST(name, expr) do { \
|
||||
if (expr) { \
|
||||
std::cout << " ✓ " << name << std::endl; \
|
||||
passed++; \
|
||||
} else { \
|
||||
std::cout << " ✗ " << name << std::endl; \
|
||||
failed++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
std::map<std::string, std::string> extMap = {
|
||||
{".py", "python"}, {".js", "javascript"}, {".ts", "typescript"},
|
||||
{".rb", "ruby"}, {".go", "go"}, {".rs", "rust"}, {".c", "c"},
|
||||
{".cpp", "cpp"}, {".java", "java"}, {".kt", "kotlin"}, {".hs", "haskell"}
|
||||
};
|
||||
|
||||
std::string getLanguage(const std::string& ext) {
|
||||
auto it = extMap.find(ext);
|
||||
return it != extMap.end() ? it->second : "";
|
||||
}
|
||||
|
||||
std::string getExtension(const std::string& filename) {
|
||||
size_t dot = filename.rfind('.');
|
||||
return dot != std::string::npos ? filename.substr(dot) : "";
|
||||
}
|
||||
|
||||
std::string getBasename(const std::string& path) {
|
||||
size_t slash = path.rfind('/');
|
||||
return slash != std::string::npos ? path.substr(slash + 1) : path;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "\n=== Extension Mapping Tests ===" << std::endl;
|
||||
|
||||
TEST("Python extension maps correctly",
|
||||
getLanguage(".py") == "python");
|
||||
|
||||
TEST("C++ extension maps correctly",
|
||||
getLanguage(".cpp") == "cpp");
|
||||
|
||||
TEST("JavaScript extension maps correctly",
|
||||
getLanguage(".js") == "javascript");
|
||||
|
||||
TEST("Go extension maps correctly",
|
||||
getLanguage(".go") == "go");
|
||||
|
||||
TEST("Rust extension maps correctly",
|
||||
getLanguage(".rs") == "rust");
|
||||
|
||||
std::cout << "\n=== Signature Format Tests ===" << std::endl;
|
||||
|
||||
std::string timestamp = "1704067200";
|
||||
std::string method = "POST";
|
||||
std::string endpoint = "/execute";
|
||||
std::string body = "{\"language\":\"python\"}";
|
||||
std::string message = timestamp + ":" + method + ":" + endpoint + ":" + body;
|
||||
|
||||
TEST("Signature format starts with timestamp",
|
||||
message.substr(0, timestamp.length()) == timestamp);
|
||||
|
||||
TEST("Signature format contains :POST:",
|
||||
message.find(":POST:") != std::string::npos);
|
||||
|
||||
TEST("Signature format contains :/execute:",
|
||||
message.find(":/execute:") != std::string::npos);
|
||||
|
||||
std::cout << "\n=== Language Detection Tests ===" << std::endl;
|
||||
|
||||
std::string content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
std::string firstLine = content.substr(0, content.find('\n'));
|
||||
|
||||
TEST("Python shebang detection - starts with #!",
|
||||
firstLine.substr(0, 2) == "#!");
|
||||
|
||||
TEST("Python shebang detection - contains python",
|
||||
firstLine.find("python") != std::string::npos);
|
||||
|
||||
std::cout << "\n=== Argument Parsing Tests ===" << std::endl;
|
||||
|
||||
std::string arg1 = "DEBUG=1";
|
||||
size_t eq1 = arg1.find('=');
|
||||
std::string key1 = arg1.substr(0, eq1);
|
||||
std::string value1 = arg1.substr(eq1 + 1);
|
||||
|
||||
TEST("Parse -e KEY=VALUE format - key",
|
||||
key1 == "DEBUG");
|
||||
|
||||
TEST("Parse -e KEY=VALUE format - value",
|
||||
value1 == "1");
|
||||
|
||||
std::string arg2 = "URL=https://example.com?foo=bar";
|
||||
size_t eq2 = arg2.find('=');
|
||||
std::string key2 = arg2.substr(0, eq2);
|
||||
std::string value2 = arg2.substr(eq2 + 1);
|
||||
|
||||
TEST("Parse -e KEY=VALUE with equals in value",
|
||||
key2 == "URL" && value2 == "https://example.com?foo=bar");
|
||||
|
||||
std::cout << "\n=== File Operations Tests ===" << std::endl;
|
||||
|
||||
TEST("Extract file basename",
|
||||
getBasename("/home/user/project/script.cpp") == "script.cpp");
|
||||
|
||||
TEST("Extract file extension",
|
||||
getExtension("/home/user/project/script.cpp") == ".cpp");
|
||||
|
||||
std::cout << "\n=== API Constants Tests ===" << std::endl;
|
||||
|
||||
std::string apiBase = "https://api.unsandbox.com";
|
||||
|
||||
TEST("API base URL starts with https://",
|
||||
apiBase.substr(0, 8) == "https://");
|
||||
|
||||
TEST("API base URL contains unsandbox.com",
|
||||
apiBase.find("unsandbox.com") != std::string::npos);
|
||||
|
||||
std::cout << "\n=== Summary ===" << std::endl;
|
||||
std::cout << "Passed: " << passed << std::endl;
|
||||
std::cout << "Failed: " << failed << std::endl;
|
||||
std::cout << "Total: " << (passed + failed) << std::endl;
|
||||
|
||||
return failed > 0 ? 1 : 0;
|
||||
}
|
||||
130
tests/unit/test_crystal.cr
Normal file
130
tests/unit/test_crystal.cr
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# Unit tests for un.cr - tests internal functions without API calls
|
||||
# Run with: crystal run test_crystal.cr
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
def test(name : String, result : Bool, passed : Int32*, failed : Int32*)
|
||||
if result
|
||||
puts " ✓ #{name}"
|
||||
passed.value += 1
|
||||
else
|
||||
puts " ✗ #{name}"
|
||||
failed.value += 1
|
||||
end
|
||||
end
|
||||
|
||||
EXT_MAP = {
|
||||
".py" => "python",
|
||||
".js" => "javascript",
|
||||
".ts" => "typescript",
|
||||
".rb" => "ruby",
|
||||
".go" => "go",
|
||||
".rs" => "rust",
|
||||
".c" => "c",
|
||||
".cr" => "crystal",
|
||||
".java" => "java",
|
||||
".kt" => "kotlin",
|
||||
}
|
||||
|
||||
def get_language(ext : String) : String?
|
||||
EXT_MAP[ext]?
|
||||
end
|
||||
|
||||
def get_extension(filename : String) : String
|
||||
idx = filename.rindex('.')
|
||||
idx ? filename[idx..] : ""
|
||||
end
|
||||
|
||||
def get_basename(path : String) : String
|
||||
idx = path.rindex('/')
|
||||
idx ? path[idx + 1..] : path
|
||||
end
|
||||
|
||||
puts "\n=== Extension Mapping Tests ==="
|
||||
|
||||
test("Python extension maps correctly",
|
||||
get_language(".py") == "python", pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Crystal extension maps correctly",
|
||||
get_language(".cr") == "crystal", pointerof(passed), pointerof(failed))
|
||||
|
||||
test("JavaScript extension maps correctly",
|
||||
get_language(".js") == "javascript", pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Go extension maps correctly",
|
||||
get_language(".go") == "go", pointerof(passed), pointerof(failed))
|
||||
|
||||
puts "\n=== Signature Format Tests ==="
|
||||
|
||||
timestamp = "1704067200"
|
||||
method = "POST"
|
||||
endpoint = "/execute"
|
||||
body = %({"language":"python"})
|
||||
message = "#{timestamp}:#{method}:#{endpoint}:#{body}"
|
||||
|
||||
test("Signature format starts with timestamp",
|
||||
message.starts_with?(timestamp), pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Signature format contains :POST:",
|
||||
message.includes?(":POST:"), pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Signature format contains :/execute:",
|
||||
message.includes?(":/execute:"), pointerof(passed), pointerof(failed))
|
||||
|
||||
puts "\n=== Language Detection Tests ==="
|
||||
|
||||
content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
first_line = content.split("\n").first
|
||||
|
||||
test("Python shebang detection - starts with #!",
|
||||
first_line.starts_with?("#!"), pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Python shebang detection - contains python",
|
||||
first_line.includes?("python"), pointerof(passed), pointerof(failed))
|
||||
|
||||
puts "\n=== Argument Parsing Tests ==="
|
||||
|
||||
arg1 = "DEBUG=1"
|
||||
eq1 = arg1.index('=') || 0
|
||||
key1 = arg1[0...eq1]
|
||||
value1 = arg1[eq1 + 1..]
|
||||
|
||||
test("Parse -e KEY=VALUE format - key",
|
||||
key1 == "DEBUG", pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Parse -e KEY=VALUE format - value",
|
||||
value1 == "1", pointerof(passed), pointerof(failed))
|
||||
|
||||
arg2 = "URL=https://example.com?foo=bar"
|
||||
eq2 = arg2.index('=') || 0
|
||||
key2 = arg2[0...eq2]
|
||||
value2 = arg2[eq2 + 1..]
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value",
|
||||
key2 == "URL" && value2 == "https://example.com?foo=bar", pointerof(passed), pointerof(failed))
|
||||
|
||||
puts "\n=== File Operations Tests ==="
|
||||
|
||||
test("Extract file basename",
|
||||
get_basename("/home/user/project/script.cr") == "script.cr", pointerof(passed), pointerof(failed))
|
||||
|
||||
test("Extract file extension",
|
||||
get_extension("/home/user/project/script.cr") == ".cr", pointerof(passed), pointerof(failed))
|
||||
|
||||
puts "\n=== API Constants Tests ==="
|
||||
|
||||
api_base = "https://api.unsandbox.com"
|
||||
|
||||
test("API base URL starts with https://",
|
||||
api_base.starts_with?("https://"), pointerof(passed), pointerof(failed))
|
||||
|
||||
test("API base URL contains unsandbox.com",
|
||||
api_base.includes?("unsandbox.com"), pointerof(passed), pointerof(failed))
|
||||
|
||||
puts "\n=== Summary ==="
|
||||
puts "Passed: #{passed}"
|
||||
puts "Failed: #{failed}"
|
||||
puts "Total: #{passed + failed}"
|
||||
|
||||
exit(failed > 0 ? 1 : 0)
|
||||
173
tests/unit/test_csharp.cs
Normal file
173
tests/unit/test_csharp.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// Unit tests for un.cs - tests internal functions without API calls
|
||||
// Run with: dotnet script test_csharp.cs OR csc test_csharp.cs && mono test_csharp.exe
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
class TestCSharp
|
||||
{
|
||||
static int passed = 0;
|
||||
static int failed = 0;
|
||||
|
||||
static void Main()
|
||||
{
|
||||
var extMap = new Dictionary<string, string>
|
||||
{
|
||||
{".py", "python"}, {".js", "javascript"}, {".ts", "typescript"},
|
||||
{".rb", "ruby"}, {".go", "go"}, {".rs", "rust"}, {".c", "c"},
|
||||
{".cs", "csharp"}, {".java", "java"}, {".kt", "kotlin"}
|
||||
};
|
||||
|
||||
Console.WriteLine("\n=== Extension Mapping Tests ===");
|
||||
|
||||
Test("Python extension maps correctly", () =>
|
||||
AssertEqual(extMap[".py"], "python"));
|
||||
|
||||
Test("C# extension maps correctly", () =>
|
||||
AssertEqual(extMap[".cs"], "csharp"));
|
||||
|
||||
Test("JavaScript extension maps correctly", () =>
|
||||
AssertEqual(extMap[".js"], "javascript"));
|
||||
|
||||
Test("Go extension maps correctly", () =>
|
||||
AssertEqual(extMap[".go"], "go"));
|
||||
|
||||
Console.WriteLine("\n=== HMAC Signature Tests ===");
|
||||
|
||||
Test("HMAC-SHA256 generates 64 character hex string", () =>
|
||||
{
|
||||
var sig = HmacSha256("test-secret", "test-message");
|
||||
AssertEqual(sig.Length, 64);
|
||||
});
|
||||
|
||||
Test("Same input produces same signature", () =>
|
||||
{
|
||||
var sig1 = HmacSha256("key", "msg");
|
||||
var sig2 = HmacSha256("key", "msg");
|
||||
AssertEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
Test("Signature format verification", () =>
|
||||
{
|
||||
var timestamp = "1704067200";
|
||||
var method = "POST";
|
||||
var endpoint = "/execute";
|
||||
var body = "{\"language\":\"python\"}";
|
||||
var message = $"{timestamp}:{method}:{endpoint}:{body}";
|
||||
|
||||
AssertTrue(message.StartsWith(timestamp));
|
||||
AssertContains(message, ":POST:");
|
||||
AssertContains(message, ":/execute:");
|
||||
});
|
||||
|
||||
Console.WriteLine("\n=== Language Detection Tests ===");
|
||||
|
||||
Test("Detect language from .cs extension", () =>
|
||||
{
|
||||
var filename = "Program.cs";
|
||||
var ext = "." + filename.Split('.')[^1];
|
||||
AssertEqual(extMap[ext], "csharp");
|
||||
});
|
||||
|
||||
Test("Python shebang detection", () =>
|
||||
{
|
||||
var content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
var firstLine = content.Split('\n')[0];
|
||||
AssertTrue(firstLine.StartsWith("#!"));
|
||||
AssertContains(firstLine, "python");
|
||||
});
|
||||
|
||||
Console.WriteLine("\n=== Argument Parsing Tests ===");
|
||||
|
||||
Test("Parse -e KEY=VALUE format", () =>
|
||||
{
|
||||
var arg = "DEBUG=1";
|
||||
var parts = arg.Split(new[] {'='}, 2);
|
||||
AssertEqual(parts[0], "DEBUG");
|
||||
AssertEqual(parts[1], "1");
|
||||
});
|
||||
|
||||
Test("Parse -e KEY=VALUE with equals in value", () =>
|
||||
{
|
||||
var arg = "URL=https://example.com?foo=bar";
|
||||
var parts = arg.Split(new[] {'='}, 2);
|
||||
AssertEqual(parts[0], "URL");
|
||||
AssertEqual(parts[1], "https://example.com?foo=bar");
|
||||
});
|
||||
|
||||
Console.WriteLine("\n=== File Operations Tests ===");
|
||||
|
||||
Test("Extract file basename", () =>
|
||||
{
|
||||
var path = "/home/user/project/script.cs";
|
||||
var basename = path.Substring(path.LastIndexOf('/') + 1);
|
||||
AssertEqual(basename, "script.cs");
|
||||
});
|
||||
|
||||
Test("Extract file extension", () =>
|
||||
{
|
||||
var filename = "script.cs";
|
||||
var ext = filename.Substring(filename.LastIndexOf('.'));
|
||||
AssertEqual(ext, ".cs");
|
||||
});
|
||||
|
||||
Console.WriteLine("\n=== API Constants Tests ===");
|
||||
|
||||
Test("API base URL format", () =>
|
||||
{
|
||||
var apiBase = "https://api.unsandbox.com";
|
||||
AssertTrue(apiBase.StartsWith("https://"));
|
||||
AssertContains(apiBase, "unsandbox.com");
|
||||
});
|
||||
|
||||
Console.WriteLine("\n=== Summary ===");
|
||||
Console.WriteLine($"Passed: {passed}");
|
||||
Console.WriteLine($"Failed: {failed}");
|
||||
Console.WriteLine($"Total: {passed + failed}");
|
||||
|
||||
Environment.Exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
static void Test(string name, Action fn)
|
||||
{
|
||||
try
|
||||
{
|
||||
fn();
|
||||
Console.WriteLine($" ✓ {name}");
|
||||
passed++;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($" ✗ {name}");
|
||||
Console.WriteLine($" {e.Message}");
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
static void AssertEqual(object actual, object expected)
|
||||
{
|
||||
if (!Equals(actual, expected))
|
||||
throw new Exception($"Expected '{expected}' but got '{actual}'");
|
||||
}
|
||||
|
||||
static void AssertContains(string str, string substr)
|
||||
{
|
||||
if (!str.Contains(substr))
|
||||
throw new Exception($"Expected '{str}' to contain '{substr}'");
|
||||
}
|
||||
|
||||
static void AssertTrue(bool val)
|
||||
{
|
||||
if (!val)
|
||||
throw new Exception("Expected true but got false");
|
||||
}
|
||||
|
||||
static string HmacSha256(string secret, string message)
|
||||
{
|
||||
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
|
||||
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
|
||||
return BitConverter.ToString(hash).Replace("-", "").ToLower();
|
||||
}
|
||||
}
|
||||
136
tests/unit/test_d.d
Normal file
136
tests/unit/test_d.d
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// Unit tests for un.d - tests internal functions without API calls
|
||||
// Run with: rdmd test_d.d
|
||||
|
||||
import std.stdio;
|
||||
import std.string;
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
import core.stdc.stdlib : exit;
|
||||
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
|
||||
void test(string name, bool result) {
|
||||
if (result) {
|
||||
writefln(" ✓ %s", name);
|
||||
passed++;
|
||||
} else {
|
||||
writefln(" ✗ %s", name);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
string[string] extMap;
|
||||
|
||||
static this() {
|
||||
extMap = [
|
||||
".py": "python", ".js": "javascript", ".ts": "typescript",
|
||||
".rb": "ruby", ".go": "go", ".rs": "rust", ".c": "c",
|
||||
".d": "d", ".java": "java", ".kt": "kotlin", ".hs": "haskell"
|
||||
];
|
||||
}
|
||||
|
||||
string getLanguage(string ext) {
|
||||
auto p = ext in extMap;
|
||||
return p ? *p : "";
|
||||
}
|
||||
|
||||
string getExtension(string filename) {
|
||||
auto idx = filename.lastIndexOf('.');
|
||||
return idx >= 0 ? filename[idx .. $] : "";
|
||||
}
|
||||
|
||||
string getBasename(string path) {
|
||||
auto idx = path.lastIndexOf('/');
|
||||
return idx >= 0 ? path[idx + 1 .. $] : path;
|
||||
}
|
||||
|
||||
void main() {
|
||||
writeln("\n=== Extension Mapping Tests ===");
|
||||
|
||||
test("Python extension maps correctly",
|
||||
getLanguage(".py") == "python");
|
||||
|
||||
test("D extension maps correctly",
|
||||
getLanguage(".d") == "d");
|
||||
|
||||
test("JavaScript extension maps correctly",
|
||||
getLanguage(".js") == "javascript");
|
||||
|
||||
test("Go extension maps correctly",
|
||||
getLanguage(".go") == "go");
|
||||
|
||||
writeln("\n=== Signature Format Tests ===");
|
||||
|
||||
string timestamp = "1704067200";
|
||||
string method = "POST";
|
||||
string endpoint = "/execute";
|
||||
string reqBody = `{"language":"python"}`;
|
||||
string message = timestamp ~ ":" ~ method ~ ":" ~ endpoint ~ ":" ~ reqBody;
|
||||
|
||||
test("Signature format starts with timestamp",
|
||||
message.startsWith(timestamp));
|
||||
|
||||
test("Signature format contains :POST:",
|
||||
message.canFind(":POST:"));
|
||||
|
||||
test("Signature format contains :/execute:",
|
||||
message.canFind(":/execute:"));
|
||||
|
||||
writeln("\n=== Language Detection Tests ===");
|
||||
|
||||
string content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
string firstLine = content.split("\n")[0];
|
||||
|
||||
test("Python shebang detection - starts with #!",
|
||||
firstLine.startsWith("#!"));
|
||||
|
||||
test("Python shebang detection - contains python",
|
||||
firstLine.canFind("python"));
|
||||
|
||||
writeln("\n=== Argument Parsing Tests ===");
|
||||
|
||||
string arg1 = "DEBUG=1";
|
||||
auto parts1 = arg1.findSplit("=");
|
||||
string key1 = parts1[0];
|
||||
string value1 = parts1[2];
|
||||
|
||||
test("Parse -e KEY=VALUE format - key",
|
||||
key1 == "DEBUG");
|
||||
|
||||
test("Parse -e KEY=VALUE format - value",
|
||||
value1 == "1");
|
||||
|
||||
string arg2 = "URL=https://example.com?foo=bar";
|
||||
auto parts2 = arg2.findSplit("=");
|
||||
string key2 = parts2[0];
|
||||
string value2 = parts2[2];
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value",
|
||||
key2 == "URL" && value2 == "https://example.com?foo=bar");
|
||||
|
||||
writeln("\n=== File Operations Tests ===");
|
||||
|
||||
test("Extract file basename",
|
||||
getBasename("/home/user/project/script.d") == "script.d");
|
||||
|
||||
test("Extract file extension",
|
||||
getExtension("/home/user/project/script.d") == ".d");
|
||||
|
||||
writeln("\n=== API Constants Tests ===");
|
||||
|
||||
string apiBase = "https://api.unsandbox.com";
|
||||
|
||||
test("API base URL starts with https://",
|
||||
apiBase.startsWith("https://"));
|
||||
|
||||
test("API base URL contains unsandbox.com",
|
||||
apiBase.canFind("unsandbox.com"));
|
||||
|
||||
writeln("\n=== Summary ===");
|
||||
writefln("Passed: %d", passed);
|
||||
writefln("Failed: %d", failed);
|
||||
writefln("Total: %d", passed + failed);
|
||||
|
||||
exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
110
tests/unit/test_forth.fth
Normal file
110
tests/unit/test_forth.fth
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
\ Unit tests for un.forth - tests internal functions without API calls
|
||||
\ Run with: gforth test_forth.fth -e bye
|
||||
|
||||
variable passed
|
||||
variable failed
|
||||
|
||||
: test ( flag c-addr u -- )
|
||||
rot if
|
||||
." ✓ " type cr
|
||||
1 passed +!
|
||||
else
|
||||
." ✗ " type cr
|
||||
1 failed +!
|
||||
then ;
|
||||
|
||||
: str= ( c-addr1 u1 c-addr2 u2 -- flag )
|
||||
rot over <> if 2drop drop false exit then
|
||||
compare 0= ;
|
||||
|
||||
: starts-with ( c-addr1 u1 c-addr2 u2 -- flag )
|
||||
2over drop over min
|
||||
2swap 2drop
|
||||
compare 0= ;
|
||||
|
||||
: contains ( c-addr1 u1 c-addr2 u2 -- flag )
|
||||
2swap search nip nip ;
|
||||
|
||||
\ Extension mapping using simple string comparison
|
||||
: get-language ( c-addr u -- c-addr u )
|
||||
2dup s" .py" str= if 2drop s" python" exit then
|
||||
2dup s" .js" str= if 2drop s" javascript" exit then
|
||||
2dup s" .rb" str= if 2drop s" ruby" exit then
|
||||
2dup s" .go" str= if 2drop s" go" exit then
|
||||
2dup s" .forth" str= if 2drop s" forth" exit then
|
||||
2dup s" .fth" str= if 2drop s" forth" exit then
|
||||
2dup s" .c" str= if 2drop s" c" exit then
|
||||
2drop s" " ;
|
||||
|
||||
cr ." === Extension Mapping Tests ===" cr
|
||||
|
||||
s" .py" get-language s" python" str=
|
||||
s" Python extension maps correctly" test
|
||||
|
||||
s" .forth" get-language s" forth" str=
|
||||
s" Forth extension maps correctly" test
|
||||
|
||||
s" .js" get-language s" javascript" str=
|
||||
s" JavaScript extension maps correctly" test
|
||||
|
||||
s" .go" get-language s" go" str=
|
||||
s" Go extension maps correctly" test
|
||||
|
||||
cr ." === Signature Format Tests ===" cr
|
||||
|
||||
: timestamp s" 1704067200" ;
|
||||
: method s" POST" ;
|
||||
: endpoint s" /execute" ;
|
||||
|
||||
\ Build message: timestamp:method:endpoint:body
|
||||
: build-message ( -- c-addr u )
|
||||
s" 1704067200:POST:/execute:{\"language\":\"python\"}" ;
|
||||
|
||||
build-message timestamp starts-with
|
||||
s" Signature format starts with timestamp" test
|
||||
|
||||
build-message s" :POST:" contains
|
||||
s" Signature format contains :POST:" test
|
||||
|
||||
build-message s" :/execute:" contains
|
||||
s" Signature format contains :/execute:" test
|
||||
|
||||
cr ." === Language Detection Tests ===" cr
|
||||
|
||||
: shebang-line s" #!/usr/bin/env python3" ;
|
||||
|
||||
shebang-line s" #!" starts-with
|
||||
s" Python shebang detection - starts with #!" test
|
||||
|
||||
shebang-line s" python" contains
|
||||
s" Python shebang detection - contains python" test
|
||||
|
||||
cr ." === Argument Parsing Tests ===" cr
|
||||
|
||||
: arg1 s" DEBUG=1" ;
|
||||
|
||||
\ Simple key extraction (before first =)
|
||||
arg1 drop 5 s" DEBUG" str=
|
||||
s" Parse -e KEY=VALUE format - key" test
|
||||
|
||||
\ Simple value extraction (after first =)
|
||||
arg1 drop 6 + 1 s" 1" str=
|
||||
s" Parse -e KEY=VALUE format - value" test
|
||||
|
||||
cr ." === API Constants Tests ===" cr
|
||||
|
||||
: api-base s" https://api.unsandbox.com" ;
|
||||
|
||||
api-base s" https://" starts-with
|
||||
s" API base URL starts with https://" test
|
||||
|
||||
api-base s" unsandbox.com" contains
|
||||
s" API base URL contains unsandbox.com" test
|
||||
|
||||
cr ." === Summary ===" cr
|
||||
." Passed: " passed @ . cr
|
||||
." Failed: " failed @ . cr
|
||||
." Total: " passed @ failed @ + . cr
|
||||
|
||||
failed @ 0> [if] 1 (bye) [then]
|
||||
bye
|
||||
190
tests/unit/test_fortran.f90
Normal file
190
tests/unit/test_fortran.f90
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
! Unit tests for un.f90 - tests internal functions without API calls
|
||||
! Compile: gfortran -o test_fortran test_fortran.f90 && ./test_fortran
|
||||
|
||||
program test_fortran
|
||||
implicit none
|
||||
integer :: passed, failed
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
print *, ""
|
||||
print *, "=== Extension Mapping Tests ==="
|
||||
|
||||
call test("Python extension maps correctly", &
|
||||
get_language(".py") == "python", passed, failed)
|
||||
|
||||
call test("Fortran extension maps correctly", &
|
||||
get_language(".f90") == "fortran", passed, failed)
|
||||
|
||||
call test("JavaScript extension maps correctly", &
|
||||
get_language(".js") == "javascript", passed, failed)
|
||||
|
||||
call test("Go extension maps correctly", &
|
||||
get_language(".go") == "go", passed, failed)
|
||||
|
||||
print *, ""
|
||||
print *, "=== Signature Format Tests ==="
|
||||
|
||||
block
|
||||
character(len=256) :: message
|
||||
character(len=*), parameter :: timestamp = "1704067200"
|
||||
character(len=*), parameter :: method = "POST"
|
||||
character(len=*), parameter :: endpoint = "/execute"
|
||||
character(len=*), parameter :: body = '{"language":"python"}'
|
||||
|
||||
message = trim(timestamp) // ":" // trim(method) // ":" // &
|
||||
trim(endpoint) // ":" // trim(body)
|
||||
|
||||
call test("Signature format starts with timestamp", &
|
||||
message(1:len_trim(timestamp)) == timestamp, passed, failed)
|
||||
|
||||
call test("Signature format contains :POST:", &
|
||||
index(message, ":POST:") > 0, passed, failed)
|
||||
|
||||
call test("Signature format contains :/execute:", &
|
||||
index(message, ":/execute:") > 0, passed, failed)
|
||||
end block
|
||||
|
||||
print *, ""
|
||||
print *, "=== Language Detection Tests ==="
|
||||
|
||||
block
|
||||
character(len=256) :: content, first_line
|
||||
integer :: newline_pos
|
||||
|
||||
content = "#!/usr/bin/env python3" // char(10) // "print('hello')"
|
||||
newline_pos = index(content, char(10))
|
||||
if (newline_pos > 0) then
|
||||
first_line = content(1:newline_pos-1)
|
||||
else
|
||||
first_line = content
|
||||
end if
|
||||
|
||||
call test("Python shebang detection - starts with #!", &
|
||||
first_line(1:2) == "#!", passed, failed)
|
||||
|
||||
call test("Python shebang detection - contains python", &
|
||||
index(first_line, "python") > 0, passed, failed)
|
||||
end block
|
||||
|
||||
print *, ""
|
||||
print *, "=== Argument Parsing Tests ==="
|
||||
|
||||
block
|
||||
character(len=64) :: arg1, key1, value1
|
||||
integer :: eq_pos
|
||||
|
||||
arg1 = "DEBUG=1"
|
||||
eq_pos = index(arg1, "=")
|
||||
key1 = arg1(1:eq_pos-1)
|
||||
value1 = arg1(eq_pos+1:len_trim(arg1))
|
||||
|
||||
call test("Parse -e KEY=VALUE format - key", &
|
||||
trim(key1) == "DEBUG", passed, failed)
|
||||
|
||||
call test("Parse -e KEY=VALUE format - value", &
|
||||
trim(value1) == "1", passed, failed)
|
||||
end block
|
||||
|
||||
print *, ""
|
||||
print *, "=== File Operations Tests ==="
|
||||
|
||||
call test("Extract file basename", &
|
||||
get_basename("/home/user/project/script.f90") == "script.f90", passed, failed)
|
||||
|
||||
call test("Extract file extension", &
|
||||
get_extension("/home/user/project/script.f90") == ".f90", passed, failed)
|
||||
|
||||
print *, ""
|
||||
print *, "=== API Constants Tests ==="
|
||||
|
||||
block
|
||||
character(len=*), parameter :: api_base = "https://api.unsandbox.com"
|
||||
|
||||
call test("API base URL starts with https://", &
|
||||
api_base(1:8) == "https://", passed, failed)
|
||||
|
||||
call test("API base URL contains unsandbox.com", &
|
||||
index(api_base, "unsandbox.com") > 0, passed, failed)
|
||||
end block
|
||||
|
||||
print *, ""
|
||||
print *, "=== Summary ==="
|
||||
print '(A,I0)', " Passed: ", passed
|
||||
print '(A,I0)', " Failed: ", failed
|
||||
print '(A,I0)', " Total: ", passed + failed
|
||||
|
||||
if (failed > 0) then
|
||||
call exit(1)
|
||||
else
|
||||
call exit(0)
|
||||
end if
|
||||
|
||||
contains
|
||||
|
||||
subroutine test(name, result, passed, failed)
|
||||
character(len=*), intent(in) :: name
|
||||
logical, intent(in) :: result
|
||||
integer, intent(inout) :: passed, failed
|
||||
|
||||
if (result) then
|
||||
print *, " ✓ ", trim(name)
|
||||
passed = passed + 1
|
||||
else
|
||||
print *, " ✗ ", trim(name)
|
||||
failed = failed + 1
|
||||
end if
|
||||
end subroutine test
|
||||
|
||||
function get_language(ext) result(lang)
|
||||
character(len=*), intent(in) :: ext
|
||||
character(len=16) :: lang
|
||||
|
||||
select case (trim(ext))
|
||||
case (".py")
|
||||
lang = "python"
|
||||
case (".js")
|
||||
lang = "javascript"
|
||||
case (".rb")
|
||||
lang = "ruby"
|
||||
case (".go")
|
||||
lang = "go"
|
||||
case (".f90")
|
||||
lang = "fortran"
|
||||
case (".c")
|
||||
lang = "c"
|
||||
case default
|
||||
lang = ""
|
||||
end select
|
||||
end function get_language
|
||||
|
||||
function get_extension(filename) result(ext)
|
||||
character(len=*), intent(in) :: filename
|
||||
character(len=16) :: ext
|
||||
integer :: i
|
||||
|
||||
ext = ""
|
||||
do i = len_trim(filename), 1, -1
|
||||
if (filename(i:i) == ".") then
|
||||
ext = filename(i:len_trim(filename))
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end function get_extension
|
||||
|
||||
function get_basename(path) result(basename)
|
||||
character(len=*), intent(in) :: path
|
||||
character(len=256) :: basename
|
||||
integer :: i
|
||||
|
||||
basename = path
|
||||
do i = len_trim(path), 1, -1
|
||||
if (path(i:i) == "/") then
|
||||
basename = path(i+1:len_trim(path))
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end function get_basename
|
||||
|
||||
end program test_fortran
|
||||
108
tests/unit/test_fsharp.fs
Normal file
108
tests/unit/test_fsharp.fs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// Unit tests for un.fs - tests internal functions without API calls
|
||||
// Run with: dotnet fsi test_fsharp.fs
|
||||
|
||||
open System
|
||||
open System.Security.Cryptography
|
||||
open System.Text
|
||||
|
||||
let mutable passed = 0
|
||||
let mutable failed = 0
|
||||
|
||||
let test name result =
|
||||
if result then
|
||||
printfn " ✓ %s" name
|
||||
passed <- passed + 1
|
||||
else
|
||||
printfn " ✗ %s" name
|
||||
failed <- failed + 1
|
||||
|
||||
let extMap = Map [
|
||||
(".py", "python"); (".js", "javascript"); (".ts", "typescript")
|
||||
(".rb", "ruby"); (".go", "go"); (".rs", "rust"); (".c", "c")
|
||||
(".fs", "fsharp"); (".java", "java"); (".kt", "kotlin"); (".hs", "haskell")
|
||||
]
|
||||
|
||||
let getLanguage ext =
|
||||
match Map.tryFind ext extMap with
|
||||
| Some lang -> lang
|
||||
| None -> ""
|
||||
|
||||
let getExtension (filename: string) =
|
||||
let idx = filename.LastIndexOf('.')
|
||||
if idx >= 0 then filename.Substring(idx) else ""
|
||||
|
||||
let getBasename (path: string) =
|
||||
let idx = path.LastIndexOf('/')
|
||||
if idx >= 0 then path.Substring(idx + 1) else path
|
||||
|
||||
let hmacSha256 (secret: string) (message: string) =
|
||||
use hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret))
|
||||
let hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message))
|
||||
BitConverter.ToString(hash).Replace("-", "").ToLower()
|
||||
|
||||
printfn "\n=== Extension Mapping Tests ==="
|
||||
|
||||
test "Python extension maps correctly" (getLanguage ".py" = "python")
|
||||
test "F# extension maps correctly" (getLanguage ".fs" = "fsharp")
|
||||
test "JavaScript extension maps correctly" (getLanguage ".js" = "javascript")
|
||||
test "Go extension maps correctly" (getLanguage ".go" = "go")
|
||||
|
||||
printfn "\n=== HMAC Signature Tests ==="
|
||||
|
||||
test "HMAC-SHA256 generates 64 character hex string"
|
||||
((hmacSha256 "test-secret" "test-message").Length = 64)
|
||||
|
||||
test "Same input produces same signature"
|
||||
(hmacSha256 "key" "msg" = hmacSha256 "key" "msg")
|
||||
|
||||
let timestamp = "1704067200"
|
||||
let httpMethod = "POST"
|
||||
let endpoint = "/execute"
|
||||
let body = """{"language":"python"}"""
|
||||
let message = sprintf "%s:%s:%s:%s" timestamp httpMethod endpoint body
|
||||
|
||||
test "Signature format starts with timestamp" (message.StartsWith(timestamp))
|
||||
test "Signature format contains :POST:" (message.Contains(":POST:"))
|
||||
test "Signature format contains :/execute:" (message.Contains(":/execute:"))
|
||||
|
||||
printfn "\n=== Language Detection Tests ==="
|
||||
|
||||
let content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
let firstLine = content.Split('\n').[0]
|
||||
|
||||
test "Python shebang detection - starts with #!" (firstLine.StartsWith("#!"))
|
||||
test "Python shebang detection - contains python" (firstLine.Contains("python"))
|
||||
|
||||
printfn "\n=== Argument Parsing Tests ==="
|
||||
|
||||
let arg1 = "DEBUG=1"
|
||||
let parts1 = arg1.Split([|'='|], 2)
|
||||
let key1, value1 = parts1.[0], parts1.[1]
|
||||
|
||||
test "Parse -e KEY=VALUE format - key" (key1 = "DEBUG")
|
||||
test "Parse -e KEY=VALUE format - value" (value1 = "1")
|
||||
|
||||
let arg2 = "URL=https://example.com?foo=bar"
|
||||
let parts2 = arg2.Split([|'='|], 2)
|
||||
let key2, value2 = parts2.[0], parts2.[1]
|
||||
|
||||
test "Parse -e KEY=VALUE with equals in value" (key2 = "URL" && value2 = "https://example.com?foo=bar")
|
||||
|
||||
printfn "\n=== File Operations Tests ==="
|
||||
|
||||
test "Extract file basename" (getBasename "/home/user/project/script.fs" = "script.fs")
|
||||
test "Extract file extension" (getExtension "/home/user/project/script.fs" = ".fs")
|
||||
|
||||
printfn "\n=== API Constants Tests ==="
|
||||
|
||||
let apiBase = "https://api.unsandbox.com"
|
||||
|
||||
test "API base URL starts with https://" (apiBase.StartsWith("https://"))
|
||||
test "API base URL contains unsandbox.com" (apiBase.Contains("unsandbox.com"))
|
||||
|
||||
printfn "\n=== Summary ==="
|
||||
printfn "Passed: %d" passed
|
||||
printfn "Failed: %d" failed
|
||||
printfn "Total: %d" (passed + failed)
|
||||
|
||||
exit (if failed > 0 then 1 else 0)
|
||||
155
tests/unit/test_groovy.groovy
Normal file
155
tests/unit/test_groovy.groovy
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#!/usr/bin/env groovy
|
||||
// Unit tests for un.groovy - tests internal functions without API calls
|
||||
// Run with: groovy test_groovy.groovy
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
def passed = 0
|
||||
def failed = 0
|
||||
|
||||
def test = { name, fn ->
|
||||
try {
|
||||
fn()
|
||||
println " ✓ $name"
|
||||
passed++
|
||||
} catch (e) {
|
||||
println " ✗ $name"
|
||||
println " ${e.message}"
|
||||
failed++
|
||||
}
|
||||
}
|
||||
|
||||
def assertEqual = { actual, expected ->
|
||||
if (actual != expected) {
|
||||
throw new Exception("Expected '$expected' but got '$actual'")
|
||||
}
|
||||
}
|
||||
|
||||
def assertContains = { str, substr ->
|
||||
if (!str.contains(substr)) {
|
||||
throw new Exception("Expected '$str' to contain '$substr'")
|
||||
}
|
||||
}
|
||||
|
||||
def assertTrue = { val ->
|
||||
if (!val) {
|
||||
throw new Exception("Expected true but got false")
|
||||
}
|
||||
}
|
||||
|
||||
def hmacSha256 = { secret, message ->
|
||||
def mac = Mac.getInstance("HmacSHA256")
|
||||
def key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")
|
||||
mac.init(key)
|
||||
mac.doFinal(message.getBytes("UTF-8")).collect { String.format("%02x", it) }.join()
|
||||
}
|
||||
|
||||
def extMap = [
|
||||
".py": "python", ".js": "javascript", ".ts": "typescript",
|
||||
".rb": "ruby", ".go": "go", ".rs": "rust", ".c": "c",
|
||||
".groovy": "groovy", ".java": "java", ".kt": "kotlin"
|
||||
]
|
||||
|
||||
println "\n=== Extension Mapping Tests ==="
|
||||
|
||||
test("Python extension maps correctly") {
|
||||
assertEqual(extMap[".py"], "python")
|
||||
}
|
||||
|
||||
test("Groovy extension maps correctly") {
|
||||
assertEqual(extMap[".groovy"], "groovy")
|
||||
}
|
||||
|
||||
test("JavaScript extension maps correctly") {
|
||||
assertEqual(extMap[".js"], "javascript")
|
||||
}
|
||||
|
||||
test("Go extension maps correctly") {
|
||||
assertEqual(extMap[".go"], "go")
|
||||
}
|
||||
|
||||
println "\n=== HMAC Signature Tests ==="
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string") {
|
||||
def sig = hmacSha256("test-secret", "test-message")
|
||||
assertEqual(sig.length(), 64)
|
||||
}
|
||||
|
||||
test("Same input produces same signature") {
|
||||
def sig1 = hmacSha256("key", "msg")
|
||||
def sig2 = hmacSha256("key", "msg")
|
||||
assertEqual(sig1, sig2)
|
||||
}
|
||||
|
||||
test("Signature format verification") {
|
||||
def timestamp = "1704067200"
|
||||
def method = "POST"
|
||||
def endpoint = "/execute"
|
||||
def body = '{"language":"python"}'
|
||||
def message = "$timestamp:$method:$endpoint:$body"
|
||||
|
||||
assertTrue(message.startsWith(timestamp))
|
||||
assertContains(message, ":POST:")
|
||||
assertContains(message, ":/execute:")
|
||||
}
|
||||
|
||||
println "\n=== Language Detection Tests ==="
|
||||
|
||||
test("Detect language from .groovy extension") {
|
||||
def filename = "script.groovy"
|
||||
def ext = "." + filename.tokenize('.').last()
|
||||
assertEqual(extMap[ext], "groovy")
|
||||
}
|
||||
|
||||
test("Python shebang detection") {
|
||||
def content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
def firstLine = content.split("\n")[0]
|
||||
assertTrue(firstLine.startsWith("#!"))
|
||||
assertContains(firstLine, "python")
|
||||
}
|
||||
|
||||
println "\n=== Argument Parsing Tests ==="
|
||||
|
||||
test("Parse -e KEY=VALUE format") {
|
||||
def arg = "DEBUG=1"
|
||||
def parts = arg.split("=", 2)
|
||||
assertEqual(parts[0], "DEBUG")
|
||||
assertEqual(parts[1], "1")
|
||||
}
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value") {
|
||||
def arg = "URL=https://example.com?foo=bar"
|
||||
def parts = arg.split("=", 2)
|
||||
assertEqual(parts[0], "URL")
|
||||
assertEqual(parts[1], "https://example.com?foo=bar")
|
||||
}
|
||||
|
||||
println "\n=== File Operations Tests ==="
|
||||
|
||||
test("Extract file basename") {
|
||||
def path = "/home/user/project/script.groovy"
|
||||
def basename = path.tokenize('/').last()
|
||||
assertEqual(basename, "script.groovy")
|
||||
}
|
||||
|
||||
test("Extract file extension") {
|
||||
def filename = "script.groovy"
|
||||
def ext = "." + filename.tokenize('.').last()
|
||||
assertEqual(ext, ".groovy")
|
||||
}
|
||||
|
||||
println "\n=== API Constants Tests ==="
|
||||
|
||||
test("API base URL format") {
|
||||
def apiBase = "https://api.unsandbox.com"
|
||||
assertTrue(apiBase.startsWith("https://"))
|
||||
assertContains(apiBase, "unsandbox.com")
|
||||
}
|
||||
|
||||
println "\n=== Summary ==="
|
||||
println "Passed: $passed"
|
||||
println "Failed: $failed"
|
||||
println "Total: ${passed + failed}"
|
||||
|
||||
System.exit(failed > 0 ? 1 : 0)
|
||||
126
tests/unit/test_haskell.hs
Normal file
126
tests/unit/test_haskell.hs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#!/usr/bin/env runhaskell
|
||||
-- Unit tests for un.hs - tests internal functions without API calls
|
||||
|
||||
import Data.List (isInfixOf, isPrefixOf)
|
||||
import Data.IORef
|
||||
import System.Exit (exitWith, ExitCode(..))
|
||||
import qualified Data.Map as Map
|
||||
import Data.Maybe (fromMaybe)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
passedRef <- newIORef 0
|
||||
failedRef <- newIORef 0
|
||||
|
||||
let extMap = Map.fromList
|
||||
[ (".py", "python"), (".js", "javascript"), (".ts", "typescript")
|
||||
, (".rb", "ruby"), (".go", "go"), (".rs", "rust"), (".c", "c")
|
||||
, (".java", "java"), (".kt", "kotlin"), (".hs", "haskell")
|
||||
, (".clj", "clojure"), (".erl", "erlang")
|
||||
]
|
||||
|
||||
putStrLn "\n=== Extension Mapping Tests ==="
|
||||
|
||||
test passedRef failedRef "Python extension maps correctly" $
|
||||
Map.lookup ".py" extMap == Just "python"
|
||||
|
||||
test passedRef failedRef "Haskell extension maps correctly" $
|
||||
Map.lookup ".hs" extMap == Just "haskell"
|
||||
|
||||
test passedRef failedRef "JavaScript extension maps correctly" $
|
||||
Map.lookup ".js" extMap == Just "javascript"
|
||||
|
||||
test passedRef failedRef "Go extension maps correctly" $
|
||||
Map.lookup ".go" extMap == Just "go"
|
||||
|
||||
test passedRef failedRef "Clojure extension maps correctly" $
|
||||
Map.lookup ".clj" extMap == Just "clojure"
|
||||
|
||||
putStrLn "\n=== Signature Format Tests ==="
|
||||
|
||||
let timestamp = "1704067200"
|
||||
method = "POST"
|
||||
endpoint = "/execute"
|
||||
body = "{\"language\":\"python\"}"
|
||||
message = timestamp ++ ":" ++ method ++ ":" ++ endpoint ++ ":" ++ body
|
||||
|
||||
test passedRef failedRef "Signature format starts with timestamp" $
|
||||
isPrefixOf timestamp message
|
||||
|
||||
test passedRef failedRef "Signature format contains :POST:" $
|
||||
isInfixOf ":POST:" message
|
||||
|
||||
test passedRef failedRef "Signature format contains :/execute:" $
|
||||
isInfixOf ":/execute:" message
|
||||
|
||||
putStrLn "\n=== Language Detection Tests ==="
|
||||
|
||||
let content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
firstLine = head (lines content)
|
||||
|
||||
test passedRef failedRef "Python shebang detection - starts with #!" $
|
||||
isPrefixOf "#!" firstLine
|
||||
|
||||
test passedRef failedRef "Python shebang detection - contains python" $
|
||||
isInfixOf "python" firstLine
|
||||
|
||||
putStrLn "\n=== Argument Parsing Tests ==="
|
||||
|
||||
let arg1 = "DEBUG=1"
|
||||
(key1, _:value1) = break (== '=') arg1
|
||||
|
||||
test passedRef failedRef "Parse -e KEY=VALUE format - key" $
|
||||
key1 == "DEBUG"
|
||||
|
||||
test passedRef failedRef "Parse -e KEY=VALUE format - value" $
|
||||
value1 == "1"
|
||||
|
||||
let arg2 = "URL=https://example.com?foo=bar"
|
||||
(key2, _:value2) = break (== '=') arg2
|
||||
|
||||
test passedRef failedRef "Parse -e KEY=VALUE with equals in value" $
|
||||
key2 == "URL" && value2 == "https://example.com?foo=bar"
|
||||
|
||||
putStrLn "\n=== File Operations Tests ==="
|
||||
|
||||
let path = "/home/user/project/script.hs"
|
||||
basename = reverse $ takeWhile (/= '/') $ reverse path
|
||||
|
||||
test passedRef failedRef "Extract file basename" $
|
||||
basename == "script.hs"
|
||||
|
||||
let ext = dropWhile (/= '.') basename
|
||||
|
||||
test passedRef failedRef "Extract file extension" $
|
||||
ext == ".hs"
|
||||
|
||||
putStrLn "\n=== API Constants Tests ==="
|
||||
|
||||
let apiBase = "https://api.unsandbox.com"
|
||||
|
||||
test passedRef failedRef "API base URL starts with https://" $
|
||||
isPrefixOf "https://" apiBase
|
||||
|
||||
test passedRef failedRef "API base URL contains unsandbox.com" $
|
||||
isInfixOf "unsandbox.com" apiBase
|
||||
|
||||
-- Summary
|
||||
passed <- readIORef passedRef
|
||||
failed <- readIORef failedRef
|
||||
|
||||
putStrLn "\n=== Summary ==="
|
||||
putStrLn $ "Passed: " ++ show passed
|
||||
putStrLn $ "Failed: " ++ show failed
|
||||
putStrLn $ "Total: " ++ show (passed + failed)
|
||||
|
||||
exitWith $ if failed > 0 then ExitFailure 1 else ExitSuccess
|
||||
|
||||
test :: IORef Int -> IORef Int -> String -> Bool -> IO ()
|
||||
test passedRef failedRef name result =
|
||||
if result
|
||||
then do
|
||||
putStrLn $ " ✓ " ++ name
|
||||
modifyIORef passedRef (+1)
|
||||
else do
|
||||
putStrLn $ " ✗ " ++ name
|
||||
modifyIORef failedRef (+1)
|
||||
181
tests/unit/test_java.java
Normal file
181
tests/unit/test_java.java
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
// Unit tests for Un.java - tests internal functions without API calls
|
||||
// Run with: javac test_java.java && java TestJava
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.*;
|
||||
|
||||
public class test_java {
|
||||
static int passed = 0;
|
||||
static int failed = 0;
|
||||
|
||||
public static void main(String[] args) {
|
||||
Map<String, String> extMap = new HashMap<>();
|
||||
extMap.put(".py", "python");
|
||||
extMap.put(".js", "javascript");
|
||||
extMap.put(".ts", "typescript");
|
||||
extMap.put(".rb", "ruby");
|
||||
extMap.put(".go", "go");
|
||||
extMap.put(".rs", "rust");
|
||||
extMap.put(".c", "c");
|
||||
extMap.put(".java", "java");
|
||||
extMap.put(".kt", "kotlin");
|
||||
extMap.put(".hs", "haskell");
|
||||
|
||||
System.out.println("\n=== Extension Mapping Tests ===");
|
||||
|
||||
test("Python extension maps correctly", () ->
|
||||
assertEqual(extMap.get(".py"), "python"));
|
||||
|
||||
test("Java extension maps correctly", () ->
|
||||
assertEqual(extMap.get(".java"), "java"));
|
||||
|
||||
test("JavaScript extension maps correctly", () ->
|
||||
assertEqual(extMap.get(".js"), "javascript"));
|
||||
|
||||
test("Go extension maps correctly", () ->
|
||||
assertEqual(extMap.get(".go"), "go"));
|
||||
|
||||
test("Kotlin extension maps correctly", () ->
|
||||
assertEqual(extMap.get(".kt"), "kotlin"));
|
||||
|
||||
System.out.println("\n=== HMAC Signature Tests ===");
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string", () -> {
|
||||
String sig = hmacSha256("test-secret", "test-message");
|
||||
assertEqual(sig.length(), 64);
|
||||
});
|
||||
|
||||
test("Same input produces same signature", () -> {
|
||||
String sig1 = hmacSha256("key", "msg");
|
||||
String sig2 = hmacSha256("key", "msg");
|
||||
assertEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
test("Different secrets produce different signatures", () -> {
|
||||
String sig1 = hmacSha256("key1", "msg");
|
||||
String sig2 = hmacSha256("key2", "msg");
|
||||
assertNotEqual(sig1, sig2);
|
||||
});
|
||||
|
||||
test("Signature format verification", () -> {
|
||||
String timestamp = "1704067200";
|
||||
String method = "POST";
|
||||
String endpoint = "/execute";
|
||||
String body = "{\"language\":\"python\"}";
|
||||
String message = timestamp + ":" + method + ":" + endpoint + ":" + body;
|
||||
|
||||
assertTrue(message.startsWith(timestamp));
|
||||
assertContains(message, ":POST:");
|
||||
assertContains(message, ":/execute:");
|
||||
});
|
||||
|
||||
System.out.println("\n=== Language Detection Tests ===");
|
||||
|
||||
test("Detect language from .java extension", () -> {
|
||||
String filename = "Main.java";
|
||||
int dot = filename.lastIndexOf('.');
|
||||
String ext = filename.substring(dot);
|
||||
assertEqual(extMap.get(ext), "java");
|
||||
});
|
||||
|
||||
test("Python shebang detection", () -> {
|
||||
String content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
String firstLine = content.split("\n")[0];
|
||||
assertTrue(firstLine.startsWith("#!"));
|
||||
assertContains(firstLine, "python");
|
||||
});
|
||||
|
||||
System.out.println("\n=== Argument Parsing Tests ===");
|
||||
|
||||
test("Parse -e KEY=VALUE format", () -> {
|
||||
String arg = "DEBUG=1";
|
||||
String[] parts = arg.split("=", 2);
|
||||
assertEqual(parts[0], "DEBUG");
|
||||
assertEqual(parts[1], "1");
|
||||
});
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value", () -> {
|
||||
String arg = "URL=https://example.com?foo=bar";
|
||||
String[] parts = arg.split("=", 2);
|
||||
assertEqual(parts[0], "URL");
|
||||
assertEqual(parts[1], "https://example.com?foo=bar");
|
||||
});
|
||||
|
||||
System.out.println("\n=== File Operations Tests ===");
|
||||
|
||||
test("Base64 encoding/decoding", () -> {
|
||||
String content = "print('hello world')";
|
||||
String encoded = Base64.getEncoder().encodeToString(content.getBytes());
|
||||
String decoded = new String(Base64.getDecoder().decode(encoded));
|
||||
assertEqual(decoded, content);
|
||||
});
|
||||
|
||||
System.out.println("\n=== API Constants Tests ===");
|
||||
|
||||
test("API base URL format", () -> {
|
||||
String apiBase = "https://api.unsandbox.com";
|
||||
assertTrue(apiBase.startsWith("https://"));
|
||||
assertContains(apiBase, "unsandbox.com");
|
||||
});
|
||||
|
||||
System.out.println("\n=== Summary ===");
|
||||
System.out.println("Passed: " + passed);
|
||||
System.out.println("Failed: " + failed);
|
||||
System.out.println("Total: " + (passed + failed));
|
||||
|
||||
System.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
static void test(String name, Runnable fn) {
|
||||
try {
|
||||
fn.run();
|
||||
System.out.println(" ✓ " + name);
|
||||
passed++;
|
||||
} catch (Exception e) {
|
||||
System.out.println(" ✗ " + name);
|
||||
System.out.println(" " + e.getMessage());
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
static void assertEqual(Object actual, Object expected) {
|
||||
if (!Objects.equals(actual, expected)) {
|
||||
throw new RuntimeException("Expected '" + expected + "' but got '" + actual + "'");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertNotEqual(Object a, Object b) {
|
||||
if (Objects.equals(a, b)) {
|
||||
throw new RuntimeException("Expected values to be different but both were '" + a + "'");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertContains(String str, String substr) {
|
||||
if (!str.contains(substr)) {
|
||||
throw new RuntimeException("Expected '" + str + "' to contain '" + substr + "'");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTrue(boolean val) {
|
||||
if (!val) {
|
||||
throw new RuntimeException("Expected true but got false");
|
||||
}
|
||||
}
|
||||
|
||||
static String hmacSha256(String secret, String message) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
|
||||
mac.init(key);
|
||||
byte[] hash = mac.doFinal(message.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
162
tests/unit/test_kotlin.kt
Normal file
162
tests/unit/test_kotlin.kt
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// Unit tests for un.kt - tests internal functions without API calls
|
||||
// Run with: kotlinc -script test_kotlin.kt
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import java.util.Base64
|
||||
|
||||
var passed = 0
|
||||
var failed = 0
|
||||
|
||||
fun test(name: String, fn: () -> Unit) {
|
||||
try {
|
||||
fn()
|
||||
println(" ✓ $name")
|
||||
passed++
|
||||
} catch (e: Exception) {
|
||||
println(" ✗ $name")
|
||||
println(" ${e.message}")
|
||||
failed++
|
||||
}
|
||||
}
|
||||
|
||||
fun assertEqual(actual: Any?, expected: Any?) {
|
||||
if (actual != expected) {
|
||||
throw Exception("Expected '$expected' but got '$actual'")
|
||||
}
|
||||
}
|
||||
|
||||
fun assertNotEqual(a: Any?, b: Any?) {
|
||||
if (a == b) {
|
||||
throw Exception("Expected values to be different but both were '$a'")
|
||||
}
|
||||
}
|
||||
|
||||
fun assertContains(str: String, substr: String) {
|
||||
if (!str.contains(substr)) {
|
||||
throw Exception("Expected '$str' to contain '$substr'")
|
||||
}
|
||||
}
|
||||
|
||||
fun assertTrue(val_: Boolean) {
|
||||
if (!val_) {
|
||||
throw Exception("Expected true but got false")
|
||||
}
|
||||
}
|
||||
|
||||
fun hmacSha256(secret: String, message: String): String {
|
||||
val mac = Mac.getInstance("HmacSHA256")
|
||||
val key = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
|
||||
mac.init(key)
|
||||
return mac.doFinal(message.toByteArray()).joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
|
||||
val extMap = mapOf(
|
||||
".py" to "python", ".js" to "javascript", ".ts" to "typescript",
|
||||
".rb" to "ruby", ".go" to "go", ".rs" to "rust", ".c" to "c",
|
||||
".java" to "java", ".kt" to "kotlin", ".hs" to "haskell"
|
||||
)
|
||||
|
||||
println("\n=== Extension Mapping Tests ===")
|
||||
|
||||
test("Python extension maps correctly") {
|
||||
assertEqual(extMap[".py"], "python")
|
||||
}
|
||||
|
||||
test("Kotlin extension maps correctly") {
|
||||
assertEqual(extMap[".kt"], "kotlin")
|
||||
}
|
||||
|
||||
test("JavaScript extension maps correctly") {
|
||||
assertEqual(extMap[".js"], "javascript")
|
||||
}
|
||||
|
||||
test("Go extension maps correctly") {
|
||||
assertEqual(extMap[".go"], "go")
|
||||
}
|
||||
|
||||
println("\n=== HMAC Signature Tests ===")
|
||||
|
||||
test("HMAC-SHA256 generates 64 character hex string") {
|
||||
val sig = hmacSha256("test-secret", "test-message")
|
||||
assertEqual(sig.length, 64)
|
||||
}
|
||||
|
||||
test("Same input produces same signature") {
|
||||
val sig1 = hmacSha256("key", "msg")
|
||||
val sig2 = hmacSha256("key", "msg")
|
||||
assertEqual(sig1, sig2)
|
||||
}
|
||||
|
||||
test("Different secrets produce different signatures") {
|
||||
val sig1 = hmacSha256("key1", "msg")
|
||||
val sig2 = hmacSha256("key2", "msg")
|
||||
assertNotEqual(sig1, sig2)
|
||||
}
|
||||
|
||||
test("Signature format verification") {
|
||||
val timestamp = "1704067200"
|
||||
val method = "POST"
|
||||
val endpoint = "/execute"
|
||||
val body = """{"language":"python"}"""
|
||||
val message = "$timestamp:$method:$endpoint:$body"
|
||||
|
||||
assertTrue(message.startsWith(timestamp))
|
||||
assertContains(message, ":POST:")
|
||||
assertContains(message, ":/execute:")
|
||||
}
|
||||
|
||||
println("\n=== Language Detection Tests ===")
|
||||
|
||||
test("Detect language from .kt extension") {
|
||||
val filename = "script.kt"
|
||||
val ext = "." + filename.substringAfterLast('.')
|
||||
assertEqual(extMap[ext], "kotlin")
|
||||
}
|
||||
|
||||
test("Python shebang detection") {
|
||||
val content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
val firstLine = content.split("\n")[0]
|
||||
assertTrue(firstLine.startsWith("#!"))
|
||||
assertContains(firstLine, "python")
|
||||
}
|
||||
|
||||
println("\n=== Argument Parsing Tests ===")
|
||||
|
||||
test("Parse -e KEY=VALUE format") {
|
||||
val arg = "DEBUG=1"
|
||||
val parts = arg.split("=", limit = 2)
|
||||
assertEqual(parts[0], "DEBUG")
|
||||
assertEqual(parts[1], "1")
|
||||
}
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value") {
|
||||
val arg = "URL=https://example.com?foo=bar"
|
||||
val parts = arg.split("=", limit = 2)
|
||||
assertEqual(parts[0], "URL")
|
||||
assertEqual(parts[1], "https://example.com?foo=bar")
|
||||
}
|
||||
|
||||
println("\n=== File Operations Tests ===")
|
||||
|
||||
test("Base64 encoding/decoding") {
|
||||
val content = "print('hello world')"
|
||||
val encoded = Base64.getEncoder().encodeToString(content.toByteArray())
|
||||
val decoded = String(Base64.getDecoder().decode(encoded))
|
||||
assertEqual(decoded, content)
|
||||
}
|
||||
|
||||
println("\n=== API Constants Tests ===")
|
||||
|
||||
test("API base URL format") {
|
||||
val apiBase = "https://api.unsandbox.com"
|
||||
assertTrue(apiBase.startsWith("https://"))
|
||||
assertContains(apiBase, "unsandbox.com")
|
||||
}
|
||||
|
||||
println("\n=== Summary ===")
|
||||
println("Passed: $passed")
|
||||
println("Failed: $failed")
|
||||
println("Total: ${passed + failed}")
|
||||
|
||||
kotlin.system.exitProcess(if (failed > 0) 1 else 0)
|
||||
128
tests/unit/test_lisp.lisp
Normal file
128
tests/unit/test_lisp.lisp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#!/usr/bin/env sbcl --script
|
||||
;;; Unit tests for un.lisp - tests internal functions without API calls
|
||||
;;; Run with: sbcl --script test_lisp.lisp
|
||||
|
||||
(defvar *passed* 0)
|
||||
(defvar *failed* 0)
|
||||
|
||||
(defun test (name result)
|
||||
(if result
|
||||
(progn
|
||||
(format t " ✓ ~a~%" name)
|
||||
(incf *passed*))
|
||||
(progn
|
||||
(format t " ✗ ~a~%" name)
|
||||
(incf *failed*))))
|
||||
|
||||
(defvar *ext-map*
|
||||
'((".py" . "python") (".js" . "javascript") (".ts" . "typescript")
|
||||
(".rb" . "ruby") (".go" . "go") (".rs" . "rust") (".c" . "c")
|
||||
(".lisp" . "lisp") (".java" . "java") (".hs" . "haskell")))
|
||||
|
||||
(defun get-language (ext)
|
||||
(cdr (assoc ext *ext-map* :test #'string=)))
|
||||
|
||||
(defun get-extension (filename)
|
||||
(let ((pos (position #\. filename :from-end t)))
|
||||
(if pos (subseq filename pos) "")))
|
||||
|
||||
(defun get-basename (path)
|
||||
(let ((pos (position #\/ path :from-end t)))
|
||||
(if pos (subseq path (1+ pos)) path)))
|
||||
|
||||
(defun starts-with (prefix str)
|
||||
(and (>= (length str) (length prefix))
|
||||
(string= prefix (subseq str 0 (length prefix)))))
|
||||
|
||||
(defun string-contains (haystack needle)
|
||||
(search needle haystack))
|
||||
|
||||
(format t "~%=== Extension Mapping Tests ===~%")
|
||||
|
||||
(test "Python extension maps correctly"
|
||||
(string= (get-language ".py") "python"))
|
||||
|
||||
(test "Common Lisp extension maps correctly"
|
||||
(string= (get-language ".lisp") "lisp"))
|
||||
|
||||
(test "JavaScript extension maps correctly"
|
||||
(string= (get-language ".js") "javascript"))
|
||||
|
||||
(test "Go extension maps correctly"
|
||||
(string= (get-language ".go") "go"))
|
||||
|
||||
(format t "~%=== Signature Format Tests ===~%")
|
||||
|
||||
(let* ((timestamp "1704067200")
|
||||
(method "POST")
|
||||
(endpoint "/execute")
|
||||
(body "{\"language\":\"python\"}")
|
||||
(message (format nil "~a:~a:~a:~a" timestamp method endpoint body)))
|
||||
|
||||
(test "Signature format starts with timestamp"
|
||||
(starts-with timestamp message))
|
||||
|
||||
(test "Signature format contains :POST:"
|
||||
(string-contains message ":POST:"))
|
||||
|
||||
(test "Signature format contains :/execute:"
|
||||
(string-contains message ":/execute:")))
|
||||
|
||||
(format t "~%=== Language Detection Tests ===~%")
|
||||
|
||||
(let* ((content "#!/usr/bin/env python3
|
||||
print('hello')")
|
||||
(first-line (subseq content 0 (position #\Newline content))))
|
||||
|
||||
(test "Python shebang detection - starts with #!"
|
||||
(starts-with "#!" first-line))
|
||||
|
||||
(test "Python shebang detection - contains python"
|
||||
(string-contains first-line "python")))
|
||||
|
||||
(format t "~%=== Argument Parsing Tests ===~%")
|
||||
|
||||
(let* ((arg1 "DEBUG=1")
|
||||
(eq-pos (position #\= arg1))
|
||||
(key1 (subseq arg1 0 eq-pos))
|
||||
(value1 (subseq arg1 (1+ eq-pos))))
|
||||
|
||||
(test "Parse -e KEY=VALUE format - key"
|
||||
(string= key1 "DEBUG"))
|
||||
|
||||
(test "Parse -e KEY=VALUE format - value"
|
||||
(string= value1 "1")))
|
||||
|
||||
(let* ((arg2 "URL=https://example.com?foo=bar")
|
||||
(eq-pos (position #\= arg2))
|
||||
(key2 (subseq arg2 0 eq-pos))
|
||||
(value2 (subseq arg2 (1+ eq-pos))))
|
||||
|
||||
(test "Parse -e KEY=VALUE with equals in value"
|
||||
(and (string= key2 "URL")
|
||||
(string= value2 "https://example.com?foo=bar"))))
|
||||
|
||||
(format t "~%=== File Operations Tests ===~%")
|
||||
|
||||
(test "Extract file basename"
|
||||
(string= (get-basename "/home/user/project/script.lisp") "script.lisp"))
|
||||
|
||||
(test "Extract file extension"
|
||||
(string= (get-extension "/home/user/project/script.lisp") ".lisp"))
|
||||
|
||||
(format t "~%=== API Constants Tests ===~%")
|
||||
|
||||
(let ((api-base "https://api.unsandbox.com"))
|
||||
|
||||
(test "API base URL starts with https://"
|
||||
(starts-with "https://" api-base))
|
||||
|
||||
(test "API base URL contains unsandbox.com"
|
||||
(string-contains api-base "unsandbox.com")))
|
||||
|
||||
(format t "~%=== Summary ===~%")
|
||||
(format t "Passed: ~a~%" *passed*)
|
||||
(format t "Failed: ~a~%" *failed*)
|
||||
(format t "Total: ~a~%" (+ *passed* *failed*))
|
||||
|
||||
(sb-ext:exit :code (if (> *failed* 0) 1 0))
|
||||
120
tests/unit/test_nim.nim
Normal file
120
tests/unit/test_nim.nim
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
# Unit tests for un.nim - tests internal functions without API calls
|
||||
# Run with: nim r test_nim.nim
|
||||
|
||||
import strutils, tables, os
|
||||
|
||||
var passed = 0
|
||||
var failed = 0
|
||||
|
||||
proc test(name: string, result: bool) =
|
||||
if result:
|
||||
echo " ✓ ", name
|
||||
inc passed
|
||||
else:
|
||||
echo " ✗ ", name
|
||||
inc failed
|
||||
|
||||
let extMap = {
|
||||
".py": "python", ".js": "javascript", ".ts": "typescript",
|
||||
".rb": "ruby", ".go": "go", ".rs": "rust", ".c": "c",
|
||||
".nim": "nim", ".java": "java", ".kt": "kotlin", ".hs": "haskell"
|
||||
}.toTable
|
||||
|
||||
proc getLanguage(ext: string): string =
|
||||
if extMap.hasKey(ext): extMap[ext] else: ""
|
||||
|
||||
proc getExtension(filename: string): string =
|
||||
let dot = filename.rfind('.')
|
||||
if dot >= 0: filename[dot..^1] else: ""
|
||||
|
||||
proc getBasename(path: string): string =
|
||||
let slash = path.rfind('/')
|
||||
if slash >= 0: path[slash+1..^1] else: path
|
||||
|
||||
echo "\n=== Extension Mapping Tests ==="
|
||||
|
||||
test("Python extension maps correctly",
|
||||
getLanguage(".py") == "python")
|
||||
|
||||
test("Nim extension maps correctly",
|
||||
getLanguage(".nim") == "nim")
|
||||
|
||||
test("JavaScript extension maps correctly",
|
||||
getLanguage(".js") == "javascript")
|
||||
|
||||
test("Go extension maps correctly",
|
||||
getLanguage(".go") == "go")
|
||||
|
||||
echo "\n=== Signature Format Tests ==="
|
||||
|
||||
let timestamp = "1704067200"
|
||||
let httpMethod = "POST"
|
||||
let endpoint = "/execute"
|
||||
let body = """{"language":"python"}"""
|
||||
let message = timestamp & ":" & httpMethod & ":" & endpoint & ":" & body
|
||||
|
||||
test("Signature format starts with timestamp",
|
||||
message.startsWith(timestamp))
|
||||
|
||||
test("Signature format contains :POST:",
|
||||
message.contains(":POST:"))
|
||||
|
||||
test("Signature format contains :/execute:",
|
||||
message.contains(":/execute:"))
|
||||
|
||||
echo "\n=== Language Detection Tests ==="
|
||||
|
||||
let content = "#!/usr/bin/env python3\nprint('hello')"
|
||||
let firstLine = content.split("\n")[0]
|
||||
|
||||
test("Python shebang detection - starts with #!",
|
||||
firstLine.startsWith("#!"))
|
||||
|
||||
test("Python shebang detection - contains python",
|
||||
firstLine.contains("python"))
|
||||
|
||||
echo "\n=== Argument Parsing Tests ==="
|
||||
|
||||
let arg1 = "DEBUG=1"
|
||||
let eq1 = arg1.find('=')
|
||||
let key1 = arg1[0..<eq1]
|
||||
let value1 = arg1[eq1+1..^1]
|
||||
|
||||
test("Parse -e KEY=VALUE format - key",
|
||||
key1 == "DEBUG")
|
||||
|
||||
test("Parse -e KEY=VALUE format - value",
|
||||
value1 == "1")
|
||||
|
||||
let arg2 = "URL=https://example.com?foo=bar"
|
||||
let eq2 = arg2.find('=')
|
||||
let key2 = arg2[0..<eq2]
|
||||
let value2 = arg2[eq2+1..^1]
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value",
|
||||
key2 == "URL" and value2 == "https://example.com?foo=bar")
|
||||
|
||||
echo "\n=== File Operations Tests ==="
|
||||
|
||||
test("Extract file basename",
|
||||
getBasename("/home/user/project/script.nim") == "script.nim")
|
||||
|
||||
test("Extract file extension",
|
||||
getExtension("/home/user/project/script.nim") == ".nim")
|
||||
|
||||
echo "\n=== API Constants Tests ==="
|
||||
|
||||
let apiBase = "https://api.unsandbox.com"
|
||||
|
||||
test("API base URL starts with https://",
|
||||
apiBase.startsWith("https://"))
|
||||
|
||||
test("API base URL contains unsandbox.com",
|
||||
apiBase.contains("unsandbox.com"))
|
||||
|
||||
echo "\n=== Summary ==="
|
||||
echo "Passed: ", passed
|
||||
echo "Failed: ", failed
|
||||
echo "Total: ", passed + failed
|
||||
|
||||
quit(if failed > 0: 1 else: 0)
|
||||
157
tests/unit/test_objc.m
Normal file
157
tests/unit/test_objc.m
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
// Unit tests for un.m - tests internal functions without API calls
|
||||
// Compile: clang -framework Foundation -o test_objc test_objc.m && ./test_objc
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CommonCrypto/CommonHMAC.h>
|
||||
|
||||
static int passed = 0;
|
||||
static int failed = 0;
|
||||
|
||||
void test(NSString *name, BOOL result) {
|
||||
if (result) {
|
||||
NSLog(@" ✓ %@", name);
|
||||
passed++;
|
||||
} else {
|
||||
NSLog(@" ✗ %@", name);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *extMap;
|
||||
|
||||
NSString *getLanguage(NSString *ext) {
|
||||
return extMap[ext] ?: @"";
|
||||
}
|
||||
|
||||
NSString *getExtension(NSString *filename) {
|
||||
NSRange range = [filename rangeOfString:@"." options:NSBackwardsSearch];
|
||||
if (range.location != NSNotFound) {
|
||||
return [filename substringFromIndex:range.location];
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
NSString *getBasename(NSString *path) {
|
||||
return [path lastPathComponent];
|
||||
}
|
||||
|
||||
NSString *hmacSha256(NSString *secret, NSString *message) {
|
||||
const char *cKey = [secret UTF8String];
|
||||
const char *cData = [message UTF8String];
|
||||
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
|
||||
|
||||
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
|
||||
|
||||
NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
|
||||
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
|
||||
[hash appendFormat:@"%02x", cHMAC[i]];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@autoreleasepool {
|
||||
extMap = @{
|
||||
@".py": @"python", @".js": @"javascript", @".ts": @"typescript",
|
||||
@".rb": @"ruby", @".go": @"go", @".rs": @"rust", @".c": @"c",
|
||||
@".m": @"objective-c", @".java": @"java", @".kt": @"kotlin"
|
||||
};
|
||||
|
||||
NSLog(@"\n=== Extension Mapping Tests ===");
|
||||
|
||||
test(@"Python extension maps correctly",
|
||||
[getLanguage(@".py") isEqualToString:@"python"]);
|
||||
|
||||
test(@"Objective-C extension maps correctly",
|
||||
[getLanguage(@".m") isEqualToString:@"objective-c"]);
|
||||
|
||||
test(@"JavaScript extension maps correctly",
|
||||
[getLanguage(@".js") isEqualToString:@"javascript"]);
|
||||
|
||||
test(@"Go extension maps correctly",
|
||||
[getLanguage(@".go") isEqualToString:@"go"]);
|
||||
|
||||
NSLog(@"\n=== HMAC Signature Tests ===");
|
||||
|
||||
test(@"HMAC-SHA256 generates 64 character hex string",
|
||||
[hmacSha256(@"test-secret", @"test-message") length] == 64);
|
||||
|
||||
test(@"Same input produces same signature",
|
||||
[hmacSha256(@"key", @"msg") isEqualToString:hmacSha256(@"key", @"msg")]);
|
||||
|
||||
NSString *timestamp = @"1704067200";
|
||||
NSString *method = @"POST";
|
||||
NSString *endpoint = @"/execute";
|
||||
NSString *body = @"{\"language\":\"python\"}";
|
||||
NSString *message = [NSString stringWithFormat:@"%@:%@:%@:%@",
|
||||
timestamp, method, endpoint, body];
|
||||
|
||||
test(@"Signature format starts with timestamp",
|
||||
[message hasPrefix:timestamp]);
|
||||
|
||||
test(@"Signature format contains :POST:",
|
||||
[message containsString:@":POST:"]);
|
||||
|
||||
test(@"Signature format contains :/execute:",
|
||||
[message containsString:@":/execute:"]);
|
||||
|
||||
NSLog(@"\n=== Language Detection Tests ===");
|
||||
|
||||
NSString *content = @"#!/usr/bin/env python3\nprint('hello')";
|
||||
NSString *firstLine = [content componentsSeparatedByString:@"\n"][0];
|
||||
|
||||
test(@"Python shebang detection - starts with #!",
|
||||
[firstLine hasPrefix:@"#!"]);
|
||||
|
||||
test(@"Python shebang detection - contains python",
|
||||
[firstLine containsString:@"python"]);
|
||||
|
||||
NSLog(@"\n=== Argument Parsing Tests ===");
|
||||
|
||||
NSString *arg1 = @"DEBUG=1";
|
||||
NSArray *parts1 = [arg1 componentsSeparatedByString:@"="];
|
||||
NSString *key1 = parts1[0];
|
||||
NSString *value1 = [[parts1 subarrayWithRange:NSMakeRange(1, parts1.count - 1)]
|
||||
componentsJoinedByString:@"="];
|
||||
|
||||
test(@"Parse -e KEY=VALUE format - key",
|
||||
[key1 isEqualToString:@"DEBUG"]);
|
||||
|
||||
test(@"Parse -e KEY=VALUE format - value",
|
||||
[value1 isEqualToString:@"1"]);
|
||||
|
||||
NSString *arg2 = @"URL=https://example.com?foo=bar";
|
||||
NSRange eqRange = [arg2 rangeOfString:@"="];
|
||||
NSString *key2 = [arg2 substringToIndex:eqRange.location];
|
||||
NSString *value2 = [arg2 substringFromIndex:eqRange.location + 1];
|
||||
|
||||
test(@"Parse -e KEY=VALUE with equals in value",
|
||||
[key2 isEqualToString:@"URL"] &&
|
||||
[value2 isEqualToString:@"https://example.com?foo=bar"]);
|
||||
|
||||
NSLog(@"\n=== File Operations Tests ===");
|
||||
|
||||
test(@"Extract file basename",
|
||||
[getBasename(@"/home/user/project/script.m") isEqualToString:@"script.m"]);
|
||||
|
||||
test(@"Extract file extension",
|
||||
[getExtension(@"/home/user/project/script.m") isEqualToString:@".m"]);
|
||||
|
||||
NSLog(@"\n=== API Constants Tests ===");
|
||||
|
||||
NSString *apiBase = @"https://api.unsandbox.com";
|
||||
|
||||
test(@"API base URL starts with https://",
|
||||
[apiBase hasPrefix:@"https://"]);
|
||||
|
||||
test(@"API base URL contains unsandbox.com",
|
||||
[apiBase containsString:@"unsandbox.com"]);
|
||||
|
||||
NSLog(@"\n=== Summary ===");
|
||||
NSLog(@"Passed: %d", passed);
|
||||
NSLog(@"Failed: %d", failed);
|
||||
NSLog(@"Total: %d", passed + failed);
|
||||
|
||||
return failed > 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
135
tests/unit/test_ocaml.ml
Normal file
135
tests/unit/test_ocaml.ml
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
(* Unit tests for un.ml - tests internal functions without API calls *)
|
||||
(* Run with: ocaml test_ocaml.ml *)
|
||||
|
||||
let passed = ref 0
|
||||
let failed = ref 0
|
||||
|
||||
let test name result =
|
||||
if result then begin
|
||||
Printf.printf " ✓ %s\n" name;
|
||||
incr passed
|
||||
end else begin
|
||||
Printf.printf " ✗ %s\n" name;
|
||||
incr failed
|
||||
end
|
||||
|
||||
let ext_map = [
|
||||
(".py", "python"); (".js", "javascript"); (".ts", "typescript");
|
||||
(".rb", "ruby"); (".go", "go"); (".rs", "rust"); (".c", "c");
|
||||
(".ml", "ocaml"); (".java", "java"); (".hs", "haskell")
|
||||
]
|
||||
|
||||
let get_language ext =
|
||||
try List.assoc ext ext_map
|
||||
with Not_found -> ""
|
||||
|
||||
let get_extension filename =
|
||||
try
|
||||
let idx = String.rindex filename '.' in
|
||||
String.sub filename idx (String.length filename - idx)
|
||||
with Not_found -> ""
|
||||
|
||||
let get_basename path =
|
||||
try
|
||||
let idx = String.rindex path '/' in
|
||||
String.sub path (idx + 1) (String.length path - idx - 1)
|
||||
with Not_found -> path
|
||||
|
||||
let starts_with prefix str =
|
||||
let plen = String.length prefix in
|
||||
String.length str >= plen && String.sub str 0 plen = prefix
|
||||
|
||||
let contains haystack needle =
|
||||
try
|
||||
let _ = Str.search_forward (Str.regexp_string needle) haystack 0 in
|
||||
true
|
||||
with Not_found -> false
|
||||
|
||||
let () =
|
||||
print_endline "\n=== Extension Mapping Tests ===";
|
||||
|
||||
test "Python extension maps correctly"
|
||||
(get_language ".py" = "python");
|
||||
|
||||
test "OCaml extension maps correctly"
|
||||
(get_language ".ml" = "ocaml");
|
||||
|
||||
test "JavaScript extension maps correctly"
|
||||
(get_language ".js" = "javascript");
|
||||
|
||||
test "Go extension maps correctly"
|
||||
(get_language ".go" = "go");
|
||||
|
||||
print_endline "\n=== Signature Format Tests ===";
|
||||
|
||||
let timestamp = "1704067200" in
|
||||
let http_method = "POST" in
|
||||
let endpoint = "/execute" in
|
||||
let body = {|{"language":"python"}|} in
|
||||
let message = Printf.sprintf "%s:%s:%s:%s" timestamp http_method endpoint body in
|
||||
|
||||
test "Signature format starts with timestamp"
|
||||
(starts_with timestamp message);
|
||||
|
||||
test "Signature format contains :POST:"
|
||||
(contains message ":POST:");
|
||||
|
||||
test "Signature format contains :/execute:"
|
||||
(contains message ":/execute:");
|
||||
|
||||
print_endline "\n=== Language Detection Tests ===";
|
||||
|
||||
let content = "#!/usr/bin/env python3\nprint('hello')" in
|
||||
let first_line = List.hd (String.split_on_char '\n' content) in
|
||||
|
||||
test "Python shebang detection - starts with #!"
|
||||
(starts_with "#!" first_line);
|
||||
|
||||
test "Python shebang detection - contains python"
|
||||
(contains first_line "python");
|
||||
|
||||
print_endline "\n=== Argument Parsing Tests ===";
|
||||
|
||||
let arg1 = "DEBUG=1" in
|
||||
let idx1 = String.index arg1 '=' in
|
||||
let key1 = String.sub arg1 0 idx1 in
|
||||
let value1 = String.sub arg1 (idx1 + 1) (String.length arg1 - idx1 - 1) in
|
||||
|
||||
test "Parse -e KEY=VALUE format - key"
|
||||
(key1 = "DEBUG");
|
||||
|
||||
test "Parse -e KEY=VALUE format - value"
|
||||
(value1 = "1");
|
||||
|
||||
let arg2 = "URL=https://example.com?foo=bar" in
|
||||
let idx2 = String.index arg2 '=' in
|
||||
let key2 = String.sub arg2 0 idx2 in
|
||||
let value2 = String.sub arg2 (idx2 + 1) (String.length arg2 - idx2 - 1) in
|
||||
|
||||
test "Parse -e KEY=VALUE with equals in value"
|
||||
(key2 = "URL" && value2 = "https://example.com?foo=bar");
|
||||
|
||||
print_endline "\n=== File Operations Tests ===";
|
||||
|
||||
test "Extract file basename"
|
||||
(get_basename "/home/user/project/script.ml" = "script.ml");
|
||||
|
||||
test "Extract file extension"
|
||||
(get_extension "/home/user/project/script.ml" = ".ml");
|
||||
|
||||
print_endline "\n=== API Constants Tests ===";
|
||||
|
||||
let api_base = "https://api.unsandbox.com" in
|
||||
|
||||
test "API base URL starts with https://"
|
||||
(starts_with "https://" api_base);
|
||||
|
||||
test "API base URL contains unsandbox.com"
|
||||
(contains api_base "unsandbox.com");
|
||||
|
||||
print_endline "\n=== Summary ===";
|
||||
Printf.printf "Passed: %d\n" !passed;
|
||||
Printf.printf "Failed: %d\n" !failed;
|
||||
Printf.printf "Total: %d\n" (!passed + !failed);
|
||||
|
||||
exit (if !failed > 0 then 1 else 0)
|
||||
137
tests/unit/test_prolog.pro
Normal file
137
tests/unit/test_prolog.pro
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/env swipl
|
||||
% Unit tests for un.pro - tests internal functions without API calls
|
||||
% Run with: swipl -g main -t halt test_prolog.pro
|
||||
|
||||
:- initialization(main, main).
|
||||
|
||||
:- dynamic passed/1, failed/1.
|
||||
passed(0).
|
||||
failed(0).
|
||||
|
||||
test(Name, Goal) :-
|
||||
( call(Goal) ->
|
||||
format(" ✓ ~w~n", [Name]),
|
||||
retract(passed(P)),
|
||||
P1 is P + 1,
|
||||
assertz(passed(P1))
|
||||
;
|
||||
format(" ✗ ~w~n", [Name]),
|
||||
retract(failed(F)),
|
||||
F1 is F + 1,
|
||||
assertz(failed(F1))
|
||||
).
|
||||
|
||||
% Extension mapping
|
||||
ext_map(".py", "python").
|
||||
ext_map(".js", "javascript").
|
||||
ext_map(".ts", "typescript").
|
||||
ext_map(".rb", "ruby").
|
||||
ext_map(".go", "go").
|
||||
ext_map(".rs", "rust").
|
||||
ext_map(".c", "c").
|
||||
ext_map(".pro", "prolog").
|
||||
ext_map(".java", "java").
|
||||
ext_map(".hs", "haskell").
|
||||
|
||||
get_language(Ext, Lang) :-
|
||||
ext_map(Ext, Lang), !.
|
||||
get_language(_, "").
|
||||
|
||||
% String utilities
|
||||
starts_with(String, Prefix) :-
|
||||
atom_string(StringAtom, String),
|
||||
atom_string(PrefixAtom, Prefix),
|
||||
atom_concat(PrefixAtom, _, StringAtom).
|
||||
|
||||
contains(String, Substr) :-
|
||||
sub_string(String, _, _, _, Substr).
|
||||
|
||||
get_extension(Filename, Ext) :-
|
||||
atom_string(FilenameAtom, Filename),
|
||||
file_name_extension(_, ExtNoDoc, FilenameAtom),
|
||||
atom_concat('.', ExtNoDoc, ExtAtom),
|
||||
atom_string(ExtAtom, Ext).
|
||||
|
||||
get_basename(Path, Basename) :-
|
||||
atom_string(PathAtom, Path),
|
||||
file_base_name(PathAtom, BasenameAtom),
|
||||
atom_string(BasenameAtom, Basename).
|
||||
|
||||
main :-
|
||||
format("~n=== Extension Mapping Tests ===~n", []),
|
||||
|
||||
test("Python extension maps correctly",
|
||||
(get_language(".py", L1), L1 == "python")),
|
||||
|
||||
test("Prolog extension maps correctly",
|
||||
(get_language(".pro", L2), L2 == "prolog")),
|
||||
|
||||
test("JavaScript extension maps correctly",
|
||||
(get_language(".js", L3), L3 == "javascript")),
|
||||
|
||||
test("Go extension maps correctly",
|
||||
(get_language(".go", L4), L4 == "go")),
|
||||
|
||||
format("~n=== Signature Format Tests ===~n", []),
|
||||
|
||||
Timestamp = "1704067200",
|
||||
Method = "POST",
|
||||
Endpoint = "/execute",
|
||||
Body = "{\"language\":\"python\"}",
|
||||
format(string(Message), "~w:~w:~w:~w", [Timestamp, Method, Endpoint, Body]),
|
||||
|
||||
test("Signature format starts with timestamp",
|
||||
starts_with(Message, Timestamp)),
|
||||
|
||||
test("Signature format contains :POST:",
|
||||
contains(Message, ":POST:")),
|
||||
|
||||
test("Signature format contains :/execute:",
|
||||
contains(Message, ":/execute:")),
|
||||
|
||||
format("~n=== Language Detection Tests ===~n", []),
|
||||
|
||||
Content = "#!/usr/bin/env python3\nprint('hello')",
|
||||
split_string(Content, "\n", "", [FirstLine|_]),
|
||||
|
||||
test("Python shebang detection - starts with #!",
|
||||
starts_with(FirstLine, "#!")),
|
||||
|
||||
test("Python shebang detection - contains python",
|
||||
contains(FirstLine, "python")),
|
||||
|
||||
format("~n=== Argument Parsing Tests ===~n", []),
|
||||
|
||||
Arg1 = "DEBUG=1",
|
||||
split_string(Arg1, "=", "", [Key1, Value1]),
|
||||
|
||||
test("Parse -e KEY=VALUE format - key",
|
||||
Key1 == "DEBUG"),
|
||||
|
||||
test("Parse -e KEY=VALUE format - value",
|
||||
Value1 == "1"),
|
||||
|
||||
format("~n=== File Operations Tests ===~n", []),
|
||||
|
||||
test("Extract file basename",
|
||||
(get_basename("/home/user/project/script.pro", B), B == "script.pro")),
|
||||
|
||||
format("~n=== API Constants Tests ===~n", []),
|
||||
|
||||
ApiBase = "https://api.unsandbox.com",
|
||||
|
||||
test("API base URL starts with https://",
|
||||
starts_with(ApiBase, "https://")),
|
||||
|
||||
test("API base URL contains unsandbox.com",
|
||||
contains(ApiBase, "unsandbox.com")),
|
||||
|
||||
format("~n=== Summary ===~n", []),
|
||||
passed(P),
|
||||
failed(F),
|
||||
Total is P + F,
|
||||
format("Passed: ~w~n", [P]),
|
||||
format("Failed: ~w~n", [F]),
|
||||
format("Total: ~w~n", [Total]),
|
||||
|
||||
( F > 0 -> halt(1) ; halt(0) ).
|
||||
129
tests/unit/test_raku.raku
Normal file
129
tests/unit/test_raku.raku
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env raku
|
||||
# Unit tests for un.raku - tests internal functions without API calls
|
||||
# Run with: raku test_raku.raku
|
||||
|
||||
my $passed = 0;
|
||||
my $failed = 0;
|
||||
|
||||
sub test(Str $name, Bool $result) {
|
||||
if $result {
|
||||
say " ✓ $name";
|
||||
$passed++;
|
||||
} else {
|
||||
say " ✗ $name";
|
||||
$failed++;
|
||||
}
|
||||
}
|
||||
|
||||
my %ext-map = (
|
||||
'.py' => 'python', '.js' => 'javascript', '.ts' => 'typescript',
|
||||
'.rb' => 'ruby', '.go' => 'go', '.rs' => 'rust', '.c' => 'c',
|
||||
'.raku' => 'raku', '.java' => 'java', '.hs' => 'haskell'
|
||||
);
|
||||
|
||||
sub get-language(Str $ext --> Str) {
|
||||
%ext-map{$ext} // '';
|
||||
}
|
||||
|
||||
sub get-extension(Str $filename --> Str) {
|
||||
my $idx = $filename.rindex('.');
|
||||
$idx.defined ?? $filename.substr($idx) !! '';
|
||||
}
|
||||
|
||||
sub get-basename(Str $path --> Str) {
|
||||
my $idx = $path.rindex('/');
|
||||
$idx.defined ?? $path.substr($idx + 1) !! $path;
|
||||
}
|
||||
|
||||
sub hmac-sha256(Str $secret, Str $message --> Str) {
|
||||
use Digest::HMAC;
|
||||
use Digest::SHA256::Native;
|
||||
my $hmac = hmac($secret.encode, $message.encode, &sha256);
|
||||
$hmac>>.fmt('%02x').join;
|
||||
}
|
||||
|
||||
say "\n=== Extension Mapping Tests ===";
|
||||
|
||||
test("Python extension maps correctly",
|
||||
get-language('.py') eq 'python');
|
||||
|
||||
test("Raku extension maps correctly",
|
||||
get-language('.raku') eq 'raku');
|
||||
|
||||
test("JavaScript extension maps correctly",
|
||||
get-language('.js') eq 'javascript');
|
||||
|
||||
test("Go extension maps correctly",
|
||||
get-language('.go') eq 'go');
|
||||
|
||||
say "\n=== Signature Format Tests ===";
|
||||
|
||||
my $timestamp = "1704067200";
|
||||
my $method = "POST";
|
||||
my $endpoint = "/execute";
|
||||
my $body = '{"language":"python"}';
|
||||
my $message = "$timestamp:$method:$endpoint:$body";
|
||||
|
||||
test("Signature format starts with timestamp",
|
||||
$message.starts-with($timestamp));
|
||||
|
||||
test("Signature format contains :POST:",
|
||||
$message.contains(':POST:'));
|
||||
|
||||
test("Signature format contains :/execute:",
|
||||
$message.contains(':/execute:'));
|
||||
|
||||
say "\n=== Language Detection Tests ===";
|
||||
|
||||
my $content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
my $first-line = $content.split("\n")[0];
|
||||
|
||||
test("Python shebang detection - starts with #!",
|
||||
$first-line.starts-with('#!'));
|
||||
|
||||
test("Python shebang detection - contains python",
|
||||
$first-line.contains('python'));
|
||||
|
||||
say "\n=== Argument Parsing Tests ===";
|
||||
|
||||
my $arg1 = "DEBUG=1";
|
||||
my @parts1 = $arg1.split('=', 2);
|
||||
my ($key1, $value1) = @parts1;
|
||||
|
||||
test("Parse -e KEY=VALUE format - key",
|
||||
$key1 eq 'DEBUG');
|
||||
|
||||
test("Parse -e KEY=VALUE format - value",
|
||||
$value1 eq '1');
|
||||
|
||||
my $arg2 = "URL=https://example.com?foo=bar";
|
||||
my @parts2 = $arg2.split('=', 2);
|
||||
my ($key2, $value2) = @parts2;
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value",
|
||||
$key2 eq 'URL' && $value2 eq 'https://example.com?foo=bar');
|
||||
|
||||
say "\n=== File Operations Tests ===";
|
||||
|
||||
test("Extract file basename",
|
||||
get-basename('/home/user/project/script.raku') eq 'script.raku');
|
||||
|
||||
test("Extract file extension",
|
||||
get-extension('/home/user/project/script.raku') eq '.raku');
|
||||
|
||||
say "\n=== API Constants Tests ===";
|
||||
|
||||
my $api-base = "https://api.unsandbox.com";
|
||||
|
||||
test("API base URL starts with https://",
|
||||
$api-base.starts-with('https://'));
|
||||
|
||||
test("API base URL contains unsandbox.com",
|
||||
$api-base.contains('unsandbox.com'));
|
||||
|
||||
say "\n=== Summary ===";
|
||||
say "Passed: $passed";
|
||||
say "Failed: $failed";
|
||||
say "Total: {$passed + $failed}";
|
||||
|
||||
exit($failed > 0 ?? 1 !! 0);
|
||||
123
tests/unit/test_rust.rs
Normal file
123
tests/unit/test_rust.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// Unit tests for un.rs - tests internal functions without API calls
|
||||
// Run with: rustc --test test_rust.rs && ./test_rust
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn main() {
|
||||
let mut passed = 0;
|
||||
let mut failed = 0;
|
||||
|
||||
println!("\n=== Extension Mapping Tests ===");
|
||||
|
||||
let mut ext_map: HashMap<&str, &str> = HashMap::new();
|
||||
ext_map.insert(".py", "python");
|
||||
ext_map.insert(".js", "javascript");
|
||||
ext_map.insert(".ts", "typescript");
|
||||
ext_map.insert(".rb", "ruby");
|
||||
ext_map.insert(".go", "go");
|
||||
ext_map.insert(".rs", "rust");
|
||||
ext_map.insert(".c", "c");
|
||||
ext_map.insert(".cpp", "cpp");
|
||||
ext_map.insert(".java", "java");
|
||||
ext_map.insert(".hs", "haskell");
|
||||
|
||||
test("Python extension maps correctly", || {
|
||||
assert_eq!(ext_map.get(".py"), Some(&"python"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
test("Rust extension maps correctly", || {
|
||||
assert_eq!(ext_map.get(".rs"), Some(&"rust"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
test("JavaScript extension maps correctly", || {
|
||||
assert_eq!(ext_map.get(".js"), Some(&"javascript"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
test("Go extension maps correctly", || {
|
||||
assert_eq!(ext_map.get(".go"), Some(&"go"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
println!("\n=== Signature Format Tests ===");
|
||||
|
||||
test("Signature format is timestamp:METHOD:path:body", || {
|
||||
let timestamp = "1704067200";
|
||||
let method = "POST";
|
||||
let endpoint = "/execute";
|
||||
let body = r#"{"language":"python"}"#;
|
||||
let message = format!("{}:{}:{}:{}", timestamp, method, endpoint, body);
|
||||
|
||||
assert!(message.starts_with(timestamp));
|
||||
assert!(message.contains(":POST:"));
|
||||
assert!(message.contains(":/execute:"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
println!("\n=== Language Detection Tests ===");
|
||||
|
||||
test("Python shebang detection", || {
|
||||
let content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
let first_line: &str = content.lines().next().unwrap();
|
||||
assert!(first_line.starts_with("#!"));
|
||||
assert!(first_line.contains("python"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
println!("\n=== Argument Parsing Tests ===");
|
||||
|
||||
test("Parse -e KEY=VALUE format", || {
|
||||
let arg = "DEBUG=1";
|
||||
let parts: Vec<&str> = arg.splitn(2, '=').collect();
|
||||
assert_eq!(parts[0], "DEBUG");
|
||||
assert_eq!(parts[1], "1");
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
test("Parse -e KEY=VALUE with equals in value", || {
|
||||
let arg = "URL=https://example.com?foo=bar";
|
||||
let parts: Vec<&str> = arg.splitn(2, '=').collect();
|
||||
assert_eq!(parts[0], "URL");
|
||||
assert_eq!(parts[1], "https://example.com?foo=bar");
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
println!("\n=== File Operations Tests ===");
|
||||
|
||||
test("Extract file basename", || {
|
||||
let path = "/home/user/project/script.rs";
|
||||
let basename = path.rsplit('/').next().unwrap();
|
||||
assert_eq!(basename, "script.rs");
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
test("Extract file extension", || {
|
||||
let path = "/home/user/project/script.rs";
|
||||
let ext = path.rsplit('.').next().unwrap();
|
||||
assert_eq!(ext, "rs");
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
println!("\n=== API Constants Tests ===");
|
||||
|
||||
test("API base URL format", || {
|
||||
let api_base = "https://api.unsandbox.com";
|
||||
assert!(api_base.starts_with("https://"));
|
||||
assert!(api_base.contains("unsandbox.com"));
|
||||
}, &mut passed, &mut failed);
|
||||
|
||||
println!("\n=== Summary ===");
|
||||
println!("Passed: {}", passed);
|
||||
println!("Failed: {}", failed);
|
||||
println!("Total: {}", passed + failed);
|
||||
|
||||
std::process::exit(if failed > 0 { 1 } else { 0 });
|
||||
}
|
||||
|
||||
fn test<F>(name: &str, f: F, passed: &mut i32, failed: &mut i32)
|
||||
where
|
||||
F: FnOnce() + std::panic::UnwindSafe,
|
||||
{
|
||||
match std::panic::catch_unwind(f) {
|
||||
Ok(_) => {
|
||||
println!(" ✓ {}", name);
|
||||
*passed += 1;
|
||||
}
|
||||
Err(_) => {
|
||||
println!(" ✗ {}", name);
|
||||
*failed += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
121
tests/unit/test_scheme.scm
Normal file
121
tests/unit/test_scheme.scm
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env guile
|
||||
!#
|
||||
;; Unit tests for un.scm - tests internal functions without API calls
|
||||
|
||||
(use-modules (ice-9 format))
|
||||
|
||||
(define passed 0)
|
||||
(define failed 0)
|
||||
|
||||
(define (test name thunk)
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(thunk)
|
||||
(format #t " ✓ ~a~%" name)
|
||||
(set! passed (+ passed 1)))
|
||||
(lambda (key . args)
|
||||
(format #t " ✗ ~a~%" name)
|
||||
(set! failed (+ failed 1)))))
|
||||
|
||||
(define (assert-equal actual expected)
|
||||
(unless (equal? actual expected)
|
||||
(error (format #f "Expected '~a' but got '~a'" expected actual))))
|
||||
|
||||
(define (assert-true val)
|
||||
(unless val
|
||||
(error "Expected true but got false")))
|
||||
|
||||
(define (string-contains? str substr)
|
||||
(string-contains str substr))
|
||||
|
||||
(define (string-prefix? prefix str)
|
||||
(and (>= (string-length str) (string-length prefix))
|
||||
(string=? prefix (substring str 0 (string-length prefix)))))
|
||||
|
||||
;; Extension mapping
|
||||
(define ext-map
|
||||
'((".py" . "python") (".js" . "javascript") (".ts" . "typescript")
|
||||
(".rb" . "ruby") (".go" . "go") (".rs" . "rust") (".c" . "c")
|
||||
(".java" . "java") (".scm" . "scheme") (".hs" . "haskell")))
|
||||
|
||||
(define (get-language ext)
|
||||
(let ((pair (assoc ext ext-map)))
|
||||
(if pair (cdr pair) #f)))
|
||||
|
||||
(format #t "~%=== Extension Mapping Tests ===~%")
|
||||
|
||||
(test "Python extension maps correctly"
|
||||
(lambda () (assert-equal (get-language ".py") "python")))
|
||||
|
||||
(test "Scheme extension maps correctly"
|
||||
(lambda () (assert-equal (get-language ".scm") "scheme")))
|
||||
|
||||
(test "JavaScript extension maps correctly"
|
||||
(lambda () (assert-equal (get-language ".js") "javascript")))
|
||||
|
||||
(test "Go extension maps correctly"
|
||||
(lambda () (assert-equal (get-language ".go") "go")))
|
||||
|
||||
(format #t "~%=== Signature Format Tests ===~%")
|
||||
|
||||
(let* ((timestamp "1704067200")
|
||||
(method "POST")
|
||||
(endpoint "/execute")
|
||||
(body "{\"language\":\"python\"}")
|
||||
(message (string-append timestamp ":" method ":" endpoint ":" body)))
|
||||
|
||||
(test "Signature format starts with timestamp"
|
||||
(lambda () (assert-true (string-prefix? timestamp message))))
|
||||
|
||||
(test "Signature format contains :POST:"
|
||||
(lambda () (assert-true (string-contains? message ":POST:"))))
|
||||
|
||||
(test "Signature format contains :/execute:"
|
||||
(lambda () (assert-true (string-contains? message ":/execute:")))))
|
||||
|
||||
(format #t "~%=== Language Detection Tests ===~%")
|
||||
|
||||
(let* ((content "#!/usr/bin/env python3\nprint('hello')")
|
||||
(first-line (car (string-split content #\newline))))
|
||||
|
||||
(test "Python shebang detection - starts with #!"
|
||||
(lambda () (assert-true (string-prefix? "#!" first-line))))
|
||||
|
||||
(test "Python shebang detection - contains python"
|
||||
(lambda () (assert-true (string-contains? first-line "python")))))
|
||||
|
||||
(format #t "~%=== Argument Parsing Tests ===~%")
|
||||
|
||||
(test "Parse -e KEY=VALUE format"
|
||||
(lambda ()
|
||||
(let* ((arg "DEBUG=1")
|
||||
(parts (string-split arg #\=))
|
||||
(key (car parts))
|
||||
(value (string-join (cdr parts) "=")))
|
||||
(assert-equal key "DEBUG")
|
||||
(assert-equal value "1"))))
|
||||
|
||||
(test "Parse -e KEY=VALUE with equals in value"
|
||||
(lambda ()
|
||||
(let* ((arg "URL=https://example.com?foo=bar")
|
||||
(idx (string-index arg #\=))
|
||||
(key (substring arg 0 idx))
|
||||
(value (substring arg (+ idx 1))))
|
||||
(assert-equal key "URL")
|
||||
(assert-equal value "https://example.com?foo=bar"))))
|
||||
|
||||
(format #t "~%=== API Constants Tests ===~%")
|
||||
|
||||
(let ((api-base "https://api.unsandbox.com"))
|
||||
(test "API base URL starts with https://"
|
||||
(lambda () (assert-true (string-prefix? "https://" api-base))))
|
||||
|
||||
(test "API base URL contains unsandbox.com"
|
||||
(lambda () (assert-true (string-contains? api-base "unsandbox.com")))))
|
||||
|
||||
(format #t "~%=== Summary ===~%")
|
||||
(format #t "Passed: ~a~%" passed)
|
||||
(format #t "Failed: ~a~%" failed)
|
||||
(format #t "Total: ~a~%" (+ passed failed))
|
||||
|
||||
(exit (if (> failed 0) 1 0))
|
||||
118
tests/unit/test_v.v
Normal file
118
tests/unit/test_v.v
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// Unit tests for un.v - tests internal functions without API calls
|
||||
// Run with: v run test_v.v
|
||||
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
mut passed := 0
|
||||
mut failed := 0
|
||||
|
||||
ext_map := {
|
||||
'.py': 'python'
|
||||
'.js': 'javascript'
|
||||
'.ts': 'typescript'
|
||||
'.rb': 'ruby'
|
||||
'.go': 'go'
|
||||
'.rs': 'rust'
|
||||
'.c': 'c'
|
||||
'.v': 'v'
|
||||
'.java': 'java'
|
||||
'.kt': 'kotlin'
|
||||
}
|
||||
|
||||
get_language := fn [ext_map] (ext string) string {
|
||||
return ext_map[ext] or { '' }
|
||||
}
|
||||
|
||||
get_extension := fn (filename string) string {
|
||||
idx := filename.last_index('.') or { return '' }
|
||||
return filename[idx..]
|
||||
}
|
||||
|
||||
get_basename := fn (path string) string {
|
||||
idx := path.last_index('/') or { return path }
|
||||
return path[idx + 1..]
|
||||
}
|
||||
|
||||
test := fn [mut passed, mut failed] (name string, result bool) {
|
||||
if result {
|
||||
println(' ✓ ${name}')
|
||||
passed++
|
||||
} else {
|
||||
println(' ✗ ${name}')
|
||||
failed++
|
||||
}
|
||||
}
|
||||
|
||||
println('\n=== Extension Mapping Tests ===')
|
||||
|
||||
test('Python extension maps correctly', get_language('.py') == 'python')
|
||||
|
||||
test('V extension maps correctly', get_language('.v') == 'v')
|
||||
|
||||
test('JavaScript extension maps correctly', get_language('.js') == 'javascript')
|
||||
|
||||
test('Go extension maps correctly', get_language('.go') == 'go')
|
||||
|
||||
println('\n=== Signature Format Tests ===')
|
||||
|
||||
timestamp := '1704067200'
|
||||
method := 'POST'
|
||||
endpoint := '/execute'
|
||||
body := '{"language":"python"}'
|
||||
message := '${timestamp}:${method}:${endpoint}:${body}'
|
||||
|
||||
test('Signature format starts with timestamp', message.starts_with(timestamp))
|
||||
|
||||
test('Signature format contains :POST:', message.contains(':POST:'))
|
||||
|
||||
test('Signature format contains :/execute:', message.contains(':/execute:'))
|
||||
|
||||
println('\n=== Language Detection Tests ===')
|
||||
|
||||
content := '#!/usr/bin/env python3\nprint(\'hello\')'
|
||||
first_line := content.split('\n')[0]
|
||||
|
||||
test('Python shebang detection - starts with #!', first_line.starts_with('#!'))
|
||||
|
||||
test('Python shebang detection - contains python', first_line.contains('python'))
|
||||
|
||||
println('\n=== Argument Parsing Tests ===')
|
||||
|
||||
arg1 := 'DEBUG=1'
|
||||
eq1 := arg1.index('=') or { 0 }
|
||||
key1 := arg1[..eq1]
|
||||
value1 := arg1[eq1 + 1..]
|
||||
|
||||
test('Parse -e KEY=VALUE format - key', key1 == 'DEBUG')
|
||||
|
||||
test('Parse -e KEY=VALUE format - value', value1 == '1')
|
||||
|
||||
arg2 := 'URL=https://example.com?foo=bar'
|
||||
eq2 := arg2.index('=') or { 0 }
|
||||
key2 := arg2[..eq2]
|
||||
value2 := arg2[eq2 + 1..]
|
||||
|
||||
test('Parse -e KEY=VALUE with equals in value', key2 == 'URL' && value2 == 'https://example.com?foo=bar')
|
||||
|
||||
println('\n=== File Operations Tests ===')
|
||||
|
||||
test('Extract file basename', get_basename('/home/user/project/script.v') == 'script.v')
|
||||
|
||||
test('Extract file extension', get_extension('/home/user/project/script.v') == '.v')
|
||||
|
||||
println('\n=== API Constants Tests ===')
|
||||
|
||||
api_base := 'https://api.unsandbox.com'
|
||||
|
||||
test('API base URL starts with https://', api_base.starts_with('https://'))
|
||||
|
||||
test('API base URL contains unsandbox.com', api_base.contains('unsandbox.com'))
|
||||
|
||||
println('\n=== Summary ===')
|
||||
println('Passed: ${passed}')
|
||||
println('Failed: ${failed}')
|
||||
println('Total: ${passed + failed}')
|
||||
|
||||
exit(if failed > 0 { 1 } else { 0 })
|
||||
}
|
||||
132
tests/unit/test_zig.zig
Normal file
132
tests/unit/test_zig.zig
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Unit tests for un.zig - tests internal functions without API calls
|
||||
// Run with: zig run test_zig.zig
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
var passed: u32 = 0;
|
||||
var failed: u32 = 0;
|
||||
|
||||
fn test_case(name: []const u8, result: bool) void {
|
||||
if (result) {
|
||||
std.debug.print(" ✓ {s}\n", .{name});
|
||||
passed += 1;
|
||||
} else {
|
||||
std.debug.print(" ✗ {s}\n", .{name});
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn getLanguage(ext: []const u8) ?[]const u8 {
|
||||
const extensions = [_]struct { ext: []const u8, lang: []const u8 }{
|
||||
.{ .ext = ".py", .lang = "python" },
|
||||
.{ .ext = ".js", .lang = "javascript" },
|
||||
.{ .ext = ".ts", .lang = "typescript" },
|
||||
.{ .ext = ".rb", .lang = "ruby" },
|
||||
.{ .ext = ".go", .lang = "go" },
|
||||
.{ .ext = ".rs", .lang = "rust" },
|
||||
.{ .ext = ".c", .lang = "c" },
|
||||
.{ .ext = ".zig", .lang = "zig" },
|
||||
.{ .ext = ".java", .lang = "java" },
|
||||
};
|
||||
|
||||
for (extensions) |e| {
|
||||
if (mem.eql(u8, ext, e.ext)) return e.lang;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getExtension(filename: []const u8) []const u8 {
|
||||
var i: usize = filename.len;
|
||||
while (i > 0) : (i -= 1) {
|
||||
if (filename[i - 1] == '.') return filename[i - 1 ..];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
fn getBasename(path: []const u8) []const u8 {
|
||||
var i: usize = path.len;
|
||||
while (i > 0) : (i -= 1) {
|
||||
if (path[i - 1] == '/') return path[i..];
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
fn startsWith(haystack: []const u8, needle: []const u8) bool {
|
||||
if (needle.len > haystack.len) return false;
|
||||
return mem.eql(u8, haystack[0..needle.len], needle);
|
||||
}
|
||||
|
||||
fn contains(haystack: []const u8, needle: []const u8) bool {
|
||||
return mem.indexOf(u8, haystack, needle) != null;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("\n=== Extension Mapping Tests ===\n", .{});
|
||||
|
||||
test_case("Python extension maps correctly", if (getLanguage(".py")) |l| mem.eql(u8, l, "python") else false);
|
||||
|
||||
test_case("Zig extension maps correctly", if (getLanguage(".zig")) |l| mem.eql(u8, l, "zig") else false);
|
||||
|
||||
test_case("JavaScript extension maps correctly", if (getLanguage(".js")) |l| mem.eql(u8, l, "javascript") else false);
|
||||
|
||||
test_case("Go extension maps correctly", if (getLanguage(".go")) |l| mem.eql(u8, l, "go") else false);
|
||||
|
||||
std.debug.print("\n=== Signature Format Tests ===\n", .{});
|
||||
|
||||
const timestamp = "1704067200";
|
||||
const method = "POST";
|
||||
const endpoint = "/execute";
|
||||
const body = "{\"language\":\"python\"}";
|
||||
|
||||
var message_buf: [256]u8 = undefined;
|
||||
const message = std.fmt.bufPrint(&message_buf, "{s}:{s}:{s}:{s}", .{ timestamp, method, endpoint, body }) catch "";
|
||||
|
||||
test_case("Signature format starts with timestamp", startsWith(message, timestamp));
|
||||
|
||||
test_case("Signature format contains :POST:", contains(message, ":POST:"));
|
||||
|
||||
test_case("Signature format contains :/execute:", contains(message, ":/execute:"));
|
||||
|
||||
std.debug.print("\n=== Language Detection Tests ===\n", .{});
|
||||
|
||||
const content = "#!/usr/bin/env python3\nprint('hello')";
|
||||
const newline_idx = mem.indexOf(u8, content, "\n") orelse content.len;
|
||||
const first_line = content[0..newline_idx];
|
||||
|
||||
test_case("Python shebang detection - starts with #!", startsWith(first_line, "#!"));
|
||||
|
||||
test_case("Python shebang detection - contains python", contains(first_line, "python"));
|
||||
|
||||
std.debug.print("\n=== Argument Parsing Tests ===\n", .{});
|
||||
|
||||
const arg1 = "DEBUG=1";
|
||||
const eq1_idx = mem.indexOf(u8, arg1, "=") orelse 0;
|
||||
const key1 = arg1[0..eq1_idx];
|
||||
const value1 = arg1[eq1_idx + 1 ..];
|
||||
|
||||
test_case("Parse -e KEY=VALUE format - key", mem.eql(u8, key1, "DEBUG"));
|
||||
|
||||
test_case("Parse -e KEY=VALUE format - value", mem.eql(u8, value1, "1"));
|
||||
|
||||
std.debug.print("\n=== File Operations Tests ===\n", .{});
|
||||
|
||||
test_case("Extract file basename", mem.eql(u8, getBasename("/home/user/project/script.zig"), "script.zig"));
|
||||
|
||||
test_case("Extract file extension", mem.eql(u8, getExtension("/home/user/project/script.zig"), ".zig"));
|
||||
|
||||
std.debug.print("\n=== API Constants Tests ===\n", .{});
|
||||
|
||||
const api_base = "https://api.unsandbox.com";
|
||||
|
||||
test_case("API base URL starts with https://", startsWith(api_base, "https://"));
|
||||
|
||||
test_case("API base URL contains unsandbox.com", contains(api_base, "unsandbox.com"));
|
||||
|
||||
std.debug.print("\n=== Summary ===\n", .{});
|
||||
std.debug.print("Passed: {d}\n", .{passed});
|
||||
std.debug.print("Failed: {d}\n", .{failed});
|
||||
std.debug.print("Total: {d}\n", .{passed + failed});
|
||||
|
||||
std.process.exit(if (failed > 0) 1 else 0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue