add Go test files, JS/Rust lock files; gitignore egg-info
- Add Go sync unit tests, functional tests, and async unit tests - Add package-lock.json for reproducible JS installs - Add Cargo.lock for reproducible Rust builds - Gitignore *.egg-info/ (Python packaging artifacts)
This commit is contained in:
parent
369f3aa39d
commit
8d17b2babc
6 changed files with 6804 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
__pycache__/
|
||||
.venv/
|
||||
*.egg-info/
|
||||
|
||||
# Build directories
|
||||
_build/
|
||||
|
|
|
|||
396
clients/go/async/src/un_async_test.go
Normal file
396
clients/go/async/src/un_async_test.go
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
// This is free software for the public good of a permacomputer hosted at
|
||||
// permacomputer.com, an always-on computer by the people, for the people.
|
||||
// One which is durable, easy to repair, & distributed like tap water
|
||||
// for machine learning intelligence.
|
||||
//
|
||||
// The permacomputer is community-owned infrastructure optimized around
|
||||
// four values:
|
||||
//
|
||||
// TRUTH First principles, math & science, open source code freely distributed
|
||||
// FREEDOM Voluntary partnerships, freedom from tyranny & corporate control
|
||||
// HARMONY Minimal waste, self-renewing systems with diverse thriving connections
|
||||
// LOVE Be yourself without hurting others, cooperation through natural law
|
||||
//
|
||||
// This software contributes to that vision by enabling code execution across 42+ programming languages through a unified interface, accessible to all.
|
||||
// Code is seeds to sprout on any abandoned technology.
|
||||
|
||||
/*
|
||||
Tests for the unsandbox Go SDK (Asynchronous)
|
||||
|
||||
Run tests:
|
||||
cd /home/fox/git/un-inception/clients/go/async
|
||||
go test ./tests/...
|
||||
|
||||
Or run with verbose output:
|
||||
go test -v ./tests/...
|
||||
*/
|
||||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
un_async "github.com/unsandbox/un-go-async/src"
|
||||
)
|
||||
|
||||
// TestDetectLanguage tests language detection from filenames
|
||||
func TestDetectLanguage(t *testing.T) {
|
||||
tests := []struct {
|
||||
filename string
|
||||
expected string
|
||||
}{
|
||||
{"hello.py", "python"},
|
||||
{"script.js", "javascript"},
|
||||
{"main.go", "go"},
|
||||
{"test.rs", "rust"},
|
||||
{"program.c", "c"},
|
||||
{"app.rb", "ruby"},
|
||||
{"test.php", "php"},
|
||||
{"script.sh", "bash"},
|
||||
{"data.R", "r"}, // Uppercase R
|
||||
{"data.r", "r"}, // Lowercase r
|
||||
{"unknown", ""}, // No extension
|
||||
{"no_ext", ""}, // No extension
|
||||
{"file.xyz", ""}, // Unknown extension
|
||||
{"test.ts", "typescript"},
|
||||
{"code.java", "java"},
|
||||
{"test.kt", "kotlin"},
|
||||
{"app.ex", "elixir"},
|
||||
{"prog.erl", "erlang"},
|
||||
{"script.lua", "lua"},
|
||||
{"test.nim", "nim"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.filename, func(t *testing.T) {
|
||||
result := un_async.DetectLanguage(tc.filename)
|
||||
if result != tc.expected {
|
||||
t.Errorf("DetectLanguage(%q) = %q, want %q", tc.filename, result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSignRequest tests HMAC-SHA256 signature generation
|
||||
func TestSignRequest(t *testing.T) {
|
||||
// This is a basic test to ensure signature generation works
|
||||
// The actual signature validation would need to match server-side implementation
|
||||
secretKey := "test-secret-key"
|
||||
timestamp := int64(1234567890)
|
||||
method := "POST"
|
||||
path := "/execute"
|
||||
body := []byte(`{"language":"python","code":"print(42)"}`)
|
||||
|
||||
// We test that signRequest returns a non-empty 64-character hex string
|
||||
// Note: signRequest is not exported, so we test via LanguageMap as a proxy
|
||||
// for now we just verify the LanguageMap is properly defined
|
||||
if len(un_async.LanguageMap) == 0 {
|
||||
t.Error("LanguageMap should not be empty")
|
||||
}
|
||||
|
||||
// Verify constants are defined
|
||||
if un_async.APIBase != "https://api.unsandbox.com" {
|
||||
t.Errorf("APIBase = %q, want %q", un_async.APIBase, "https://api.unsandbox.com")
|
||||
}
|
||||
|
||||
if un_async.LanguagesCacheTTL != 3600 {
|
||||
t.Errorf("LanguagesCacheTTL = %d, want %d", un_async.LanguagesCacheTTL, 3600)
|
||||
}
|
||||
|
||||
// Verify poll delays are defined
|
||||
if len(un_async.PollDelaysMs) == 0 {
|
||||
t.Error("PollDelaysMs should not be empty")
|
||||
}
|
||||
|
||||
// Use the variables to avoid unused variable warnings
|
||||
_ = secretKey
|
||||
_ = timestamp
|
||||
_ = method
|
||||
_ = path
|
||||
_ = body
|
||||
}
|
||||
|
||||
// TestCredentialsError tests the CredentialsError type
|
||||
func TestCredentialsError(t *testing.T) {
|
||||
err := &un_async.CredentialsError{Message: "test error"}
|
||||
if err.Error() != "test error" {
|
||||
t.Errorf("CredentialsError.Error() = %q, want %q", err.Error(), "test error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveCredentialsFromArgs tests credential resolution from arguments
|
||||
func TestResolveCredentialsFromArgs(t *testing.T) {
|
||||
creds, err := un_async.ResolveCredentials("test-pk", "test-sk")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
|
||||
if creds.PublicKey != "test-pk" {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, "test-pk")
|
||||
}
|
||||
|
||||
if creds.SecretKey != "test-sk" {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, "test-sk")
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveCredentialsFromEnv tests credential resolution from environment
|
||||
func TestResolveCredentialsFromEnv(t *testing.T) {
|
||||
// Save original values
|
||||
origPK := os.Getenv("UNSANDBOX_PUBLIC_KEY")
|
||||
origSK := os.Getenv("UNSANDBOX_SECRET_KEY")
|
||||
|
||||
// Set test values
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", "env-pk")
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", "env-sk")
|
||||
|
||||
// Restore after test
|
||||
defer func() {
|
||||
if origPK != "" {
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", origPK)
|
||||
} else {
|
||||
os.Unsetenv("UNSANDBOX_PUBLIC_KEY")
|
||||
}
|
||||
if origSK != "" {
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", origSK)
|
||||
} else {
|
||||
os.Unsetenv("UNSANDBOX_SECRET_KEY")
|
||||
}
|
||||
}()
|
||||
|
||||
creds, err := un_async.ResolveCredentials("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
|
||||
if creds.PublicKey != "env-pk" {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, "env-pk")
|
||||
}
|
||||
|
||||
if creds.SecretKey != "env-sk" {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, "env-sk")
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveCredentialsArgsOverrideEnv tests that args take priority over env
|
||||
func TestResolveCredentialsArgsOverrideEnv(t *testing.T) {
|
||||
// Save original values
|
||||
origPK := os.Getenv("UNSANDBOX_PUBLIC_KEY")
|
||||
origSK := os.Getenv("UNSANDBOX_SECRET_KEY")
|
||||
|
||||
// Set env values
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", "env-pk")
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", "env-sk")
|
||||
|
||||
// Restore after test
|
||||
defer func() {
|
||||
if origPK != "" {
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", origPK)
|
||||
} else {
|
||||
os.Unsetenv("UNSANDBOX_PUBLIC_KEY")
|
||||
}
|
||||
if origSK != "" {
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", origSK)
|
||||
} else {
|
||||
os.Unsetenv("UNSANDBOX_SECRET_KEY")
|
||||
}
|
||||
}()
|
||||
|
||||
// Args should override env
|
||||
creds, err := un_async.ResolveCredentials("arg-pk", "arg-sk")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
|
||||
if creds.PublicKey != "arg-pk" {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, "arg-pk")
|
||||
}
|
||||
|
||||
if creds.SecretKey != "arg-sk" {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, "arg-sk")
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveCredentialsNoSourcesError tests error when no credentials found
|
||||
func TestResolveCredentialsNoSourcesError(t *testing.T) {
|
||||
// Save original values
|
||||
origPK := os.Getenv("UNSANDBOX_PUBLIC_KEY")
|
||||
origSK := os.Getenv("UNSANDBOX_SECRET_KEY")
|
||||
|
||||
// Clear env values
|
||||
os.Unsetenv("UNSANDBOX_PUBLIC_KEY")
|
||||
os.Unsetenv("UNSANDBOX_SECRET_KEY")
|
||||
|
||||
// Restore after test
|
||||
defer func() {
|
||||
if origPK != "" {
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", origPK)
|
||||
}
|
||||
if origSK != "" {
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", origSK)
|
||||
}
|
||||
}()
|
||||
|
||||
// Should fail if no CSV files exist
|
||||
_, err := un_async.ResolveCredentials("", "")
|
||||
if err == nil {
|
||||
t.Log("Note: ResolveCredentials succeeded - CSV file may exist in test environment")
|
||||
return
|
||||
}
|
||||
|
||||
credErr, ok := err.(*un_async.CredentialsError)
|
||||
if !ok {
|
||||
t.Errorf("Expected CredentialsError, got %T", err)
|
||||
return
|
||||
}
|
||||
|
||||
if credErr.Message == "" {
|
||||
t.Error("CredentialsError.Message should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCSVCredentials tests loading credentials from CSV file
|
||||
func TestCSVCredentials(t *testing.T) {
|
||||
// Create temp directory
|
||||
tmpDir, err := os.MkdirTemp("", "unsandbox-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create test CSV file
|
||||
csvPath := filepath.Join(tmpDir, "accounts.csv")
|
||||
csvContent := "public_key_1,secret_key_1\n# comment line\npublic_key_2,secret_key_2\n"
|
||||
if err := os.WriteFile(csvPath, []byte(csvContent), 0600); err != nil {
|
||||
t.Fatalf("Failed to create CSV file: %v", err)
|
||||
}
|
||||
|
||||
// Change to temp dir to test ./accounts.csv loading
|
||||
origDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get working dir: %v", err)
|
||||
}
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("Failed to change to temp dir: %v", err)
|
||||
}
|
||||
|
||||
// Clear env vars
|
||||
origPK := os.Getenv("UNSANDBOX_PUBLIC_KEY")
|
||||
origSK := os.Getenv("UNSANDBOX_SECRET_KEY")
|
||||
os.Unsetenv("UNSANDBOX_PUBLIC_KEY")
|
||||
os.Unsetenv("UNSANDBOX_SECRET_KEY")
|
||||
defer func() {
|
||||
if origPK != "" {
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", origPK)
|
||||
}
|
||||
if origSK != "" {
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", origSK)
|
||||
}
|
||||
}()
|
||||
|
||||
// Test loading first account (index 0)
|
||||
creds, err := un_async.ResolveCredentials("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
|
||||
if creds.PublicKey != "public_key_1" {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, "public_key_1")
|
||||
}
|
||||
|
||||
if creds.SecretKey != "secret_key_1" {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, "secret_key_1")
|
||||
}
|
||||
|
||||
// Test loading second account (index 1)
|
||||
os.Setenv("UNSANDBOX_ACCOUNT", "1")
|
||||
defer os.Unsetenv("UNSANDBOX_ACCOUNT")
|
||||
|
||||
creds, err = un_async.ResolveCredentials("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
|
||||
if creds.PublicKey != "public_key_2" {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, "public_key_2")
|
||||
}
|
||||
|
||||
if creds.SecretKey != "secret_key_2" {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, "secret_key_2")
|
||||
}
|
||||
}
|
||||
|
||||
// TestLanguageMapCompleteness tests that common languages are mapped
|
||||
func TestLanguageMapCompleteness(t *testing.T) {
|
||||
requiredMappings := map[string]string{
|
||||
"py": "python",
|
||||
"js": "javascript",
|
||||
"ts": "typescript",
|
||||
"rb": "ruby",
|
||||
"php": "php",
|
||||
"go": "go",
|
||||
"rs": "rust",
|
||||
"c": "c",
|
||||
"cpp": "cpp",
|
||||
"java": "java",
|
||||
"sh": "bash",
|
||||
}
|
||||
|
||||
for ext, expected := range requiredMappings {
|
||||
if lang, ok := un_async.LanguageMap[ext]; !ok {
|
||||
t.Errorf("LanguageMap missing extension %q", ext)
|
||||
} else if lang != expected {
|
||||
t.Errorf("LanguageMap[%q] = %q, want %q", ext, lang, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPollDelays tests that poll delays are reasonable
|
||||
func TestPollDelays(t *testing.T) {
|
||||
delays := un_async.PollDelaysMs
|
||||
|
||||
if len(delays) < 5 {
|
||||
t.Errorf("PollDelaysMs has %d elements, want at least 5", len(delays))
|
||||
}
|
||||
|
||||
// First delay should be small (for quick jobs)
|
||||
if delays[0] > 500 {
|
||||
t.Errorf("First poll delay %d ms too large, should be < 500ms", delays[0])
|
||||
}
|
||||
|
||||
// Last delay should be reasonable (not too long)
|
||||
lastDelay := delays[len(delays)-1]
|
||||
if lastDelay > 5000 {
|
||||
t.Errorf("Last poll delay %d ms too large, should be < 5000ms", lastDelay)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAsyncChannelBehavior tests that async functions return properly buffered channels
|
||||
func TestAsyncChannelBehavior(t *testing.T) {
|
||||
// Create test credentials
|
||||
creds := &un_async.Credentials{
|
||||
PublicKey: "test-pk",
|
||||
SecretKey: "test-sk",
|
||||
}
|
||||
|
||||
// Test that ExecuteCode returns a channel (even if request fails)
|
||||
resultChan := un_async.ExecuteCode(creds, "python", "print(1)")
|
||||
if resultChan == nil {
|
||||
t.Error("ExecuteCode returned nil channel")
|
||||
}
|
||||
|
||||
// The channel should be buffered and eventually close
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
// We expect an error since we're using test credentials
|
||||
if result.Err == nil {
|
||||
t.Log("Note: ExecuteCode succeeded - may have valid credentials")
|
||||
}
|
||||
case <-time.After(30 * time.Second):
|
||||
t.Error("ExecuteCode channel did not receive result within timeout")
|
||||
}
|
||||
}
|
||||
181
clients/go/sync/src/functional_test.go
Normal file
181
clients/go/sync/src/functional_test.go
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
// This is free software for the public good of a permacomputer hosted at
|
||||
// permacomputer.com, an always-on computer by the people, for the people.
|
||||
// One which is durable, easy to repair, & distributed like tap water
|
||||
// for machine learning intelligence.
|
||||
//
|
||||
// The permacomputer is community-owned infrastructure optimized around
|
||||
// four values:
|
||||
//
|
||||
// TRUTH First principles, math & science, open source code freely distributed
|
||||
// FREEDOM Voluntary partnerships, freedom from tyranny & corporate control
|
||||
// HARMONY Minimal waste, self-renewing systems with diverse thriving connections
|
||||
// LOVE Be yourself without hurting others, cooperation through natural law
|
||||
//
|
||||
// This software contributes to that vision by enabling code execution across 42+ programming languages through a unified interface, accessible to all.
|
||||
// Code is seeds to sprout on any abandoned technology.
|
||||
|
||||
// UN Go SDK - Functional Tests
|
||||
//
|
||||
// Tests library functions against real API.
|
||||
// Requires: UNSANDBOX_PUBLIC_KEY, UNSANDBOX_SECRET_KEY
|
||||
//
|
||||
// Usage:
|
||||
// Copy to sync/src/ then: go test -v -run TestFunctional
|
||||
package un
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func skipIfNoCreds(t *testing.T) *Credentials {
|
||||
t.Helper()
|
||||
pk := os.Getenv("UNSANDBOX_PUBLIC_KEY")
|
||||
sk := os.Getenv("UNSANDBOX_SECRET_KEY")
|
||||
if pk == "" || sk == "" {
|
||||
t.Skip("UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY required")
|
||||
}
|
||||
return &Credentials{PublicKey: pk, SecretKey: sk}
|
||||
}
|
||||
|
||||
func TestFunctionalHealthCheck(t *testing.T) {
|
||||
_ = skipIfNoCreds(t)
|
||||
result := HealthCheck()
|
||||
// HealthCheck returns a bool - just verify it runs without panic
|
||||
t.Logf("HealthCheck: %v", result)
|
||||
}
|
||||
|
||||
func TestFunctionalValidateKeys(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
info, err := ValidateKeys(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateKeys error: %v", err)
|
||||
}
|
||||
if info == nil {
|
||||
t.Fatal("ValidateKeys returned nil")
|
||||
}
|
||||
valid, ok := info["valid"]
|
||||
if !ok {
|
||||
t.Fatal("ValidateKeys result missing 'valid' key")
|
||||
}
|
||||
if valid != true {
|
||||
t.Errorf("Keys should be valid, got: %v", valid)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionalGetLanguages(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
langs, err := GetLanguages(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("GetLanguages error: %v", err)
|
||||
}
|
||||
if len(langs) == 0 {
|
||||
t.Fatal("GetLanguages returned empty list")
|
||||
}
|
||||
foundPython := false
|
||||
for _, l := range langs {
|
||||
if l == "python" {
|
||||
foundPython = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundPython {
|
||||
t.Error("python not found in languages list")
|
||||
}
|
||||
t.Logf("Found %d languages", len(langs))
|
||||
}
|
||||
|
||||
func TestFunctionalExecute(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
result, err := ExecuteCode(creds, "python", "print('hello from Go SDK')")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteCode error: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("ExecuteCode returned nil")
|
||||
}
|
||||
stdout, _ := result["stdout"].(string)
|
||||
if !strings.Contains(stdout, "hello from Go SDK") {
|
||||
t.Errorf("stdout should contain 'hello from Go SDK', got: %s", stdout)
|
||||
}
|
||||
exitCode, _ := result["exit_code"].(float64)
|
||||
if exitCode != 0 {
|
||||
t.Errorf("exit_code should be 0, got: %v", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionalExecuteError(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
result, err := ExecuteCode(creds, "python", "import sys; sys.exit(1)")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteCode error: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("ExecuteCode returned nil")
|
||||
}
|
||||
exitCode, _ := result["exit_code"].(float64)
|
||||
if exitCode != 1 {
|
||||
t.Errorf("exit_code should be 1, got: %v", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionalSessionList(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
sessions, err := ListSessions(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSessions error: %v", err)
|
||||
}
|
||||
t.Logf("Found %d sessions", len(sessions))
|
||||
}
|
||||
|
||||
func TestFunctionalSessionLifecycle(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
|
||||
// Create
|
||||
session, err := CreateSession(creds, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateSession error: %v", err)
|
||||
}
|
||||
if session == nil {
|
||||
t.Fatal("CreateSession returned nil")
|
||||
}
|
||||
sessionID, _ := session["id"].(string)
|
||||
if sessionID == "" {
|
||||
t.Fatal("Session missing id")
|
||||
}
|
||||
t.Logf("Created session: %s", sessionID)
|
||||
|
||||
// Destroy
|
||||
_, err = DeleteSession(creds, sessionID)
|
||||
if err != nil {
|
||||
t.Errorf("DeleteSession error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionalServiceList(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
services, err := ListServices(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ListServices error: %v", err)
|
||||
}
|
||||
t.Logf("Found %d services", len(services))
|
||||
}
|
||||
|
||||
func TestFunctionalSnapshotList(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
snapshots, err := ListSnapshots(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSnapshots error: %v", err)
|
||||
}
|
||||
t.Logf("Found %d snapshots", len(snapshots))
|
||||
}
|
||||
|
||||
func TestFunctionalImageList(t *testing.T) {
|
||||
creds := skipIfNoCreds(t)
|
||||
images, err := ListImages(creds, "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListImages error: %v", err)
|
||||
}
|
||||
t.Logf("Found %d images", len(images))
|
||||
}
|
||||
379
clients/go/sync/src/un_test.go
Normal file
379
clients/go/sync/src/un_test.go
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
// This is free software for the public good of a permacomputer hosted at
|
||||
// permacomputer.com, an always-on computer by the people, for the people.
|
||||
// One which is durable, easy to repair, & distributed like tap water
|
||||
// for machine learning intelligence.
|
||||
//
|
||||
// The permacomputer is community-owned infrastructure optimized around
|
||||
// four values:
|
||||
//
|
||||
// TRUTH First principles, math & science, open source code freely distributed
|
||||
// FREEDOM Voluntary partnerships, freedom from tyranny & corporate control
|
||||
// HARMONY Minimal waste, self-renewing systems with diverse thriving connections
|
||||
// LOVE Be yourself without hurting others, cooperation through natural law
|
||||
//
|
||||
// This software contributes to that vision by enabling code execution across 42+ programming languages through a unified interface, accessible to all.
|
||||
// Code is seeds to sprout on any abandoned technology.
|
||||
|
||||
// Tests for the Go unsandbox SDK
|
||||
// Run with: go test -v ./tests/
|
||||
package un
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// Unit Tests - Test exported library functions
|
||||
// ============================================================================
|
||||
|
||||
func TestDetectLanguage(t *testing.T) {
|
||||
tests := []struct {
|
||||
filename string
|
||||
expected string
|
||||
}{
|
||||
{"script.py", "python"},
|
||||
{"script.js", "javascript"},
|
||||
{"script.ts", "typescript"},
|
||||
{"script.rb", "ruby"},
|
||||
{"script.go", "go"},
|
||||
{"script.rs", "rust"},
|
||||
{"script.c", "c"},
|
||||
{"script.cpp", "cpp"},
|
||||
{"script.d", "d"},
|
||||
{"script.zig", "zig"},
|
||||
{"script.sh", "bash"},
|
||||
{"script.lua", "lua"},
|
||||
{"script.php", "php"},
|
||||
{"script.unknown", ""},
|
||||
{"script", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.filename, func(t *testing.T) {
|
||||
result := DetectLanguage(tt.filename)
|
||||
if result != tt.expected {
|
||||
t.Errorf("DetectLanguage(%q) = %q, want %q", tt.filename, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHmacSign(t *testing.T) {
|
||||
// Test with known values
|
||||
secretKey := "test-secret"
|
||||
message := "test-message"
|
||||
|
||||
result := HmacSign(secretKey, message)
|
||||
|
||||
// Should return a 64-character hex string
|
||||
if len(result) != 64 {
|
||||
t.Errorf("HmacSign returned %d characters, want 64", len(result))
|
||||
}
|
||||
|
||||
// Should be deterministic
|
||||
result2 := HmacSign(secretKey, message)
|
||||
if result != result2 {
|
||||
t.Error("HmacSign is not deterministic")
|
||||
}
|
||||
|
||||
// Different inputs should produce different outputs
|
||||
result3 := HmacSign(secretKey, "different-message")
|
||||
if result == result3 {
|
||||
t.Error("HmacSign returned same result for different inputs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
version := Version()
|
||||
if version == "" {
|
||||
t.Error("Version() returned empty string")
|
||||
}
|
||||
// Should be in semver format
|
||||
if len(version) < 5 { // At minimum "0.0.0"
|
||||
t.Errorf("Version() = %q, expected semver format", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastError(t *testing.T) {
|
||||
// Set an error
|
||||
SetLastError("test error message")
|
||||
|
||||
// Retrieve it
|
||||
err := LastError()
|
||||
if err != "test error message" {
|
||||
t.Errorf("LastError() = %q, want %q", err, "test error message")
|
||||
}
|
||||
|
||||
// Clear it
|
||||
SetLastError("")
|
||||
err = LastError()
|
||||
if err != "" {
|
||||
t.Errorf("LastError() after clear = %q, want empty", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCredentialsNew(t *testing.T) {
|
||||
pk := "unsb-pk-test-test-test-test"
|
||||
sk := "unsb-sk-test1-test2-test3-test4"
|
||||
|
||||
creds := &Credentials{
|
||||
PublicKey: pk,
|
||||
SecretKey: sk,
|
||||
}
|
||||
|
||||
if creds.PublicKey != pk {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, pk)
|
||||
}
|
||||
if creds.SecretKey != sk {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, sk)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Integration Tests - Test SDK internal consistency
|
||||
// ============================================================================
|
||||
|
||||
func TestSignRequest(t *testing.T) {
|
||||
secretKey := "test-secret-key"
|
||||
timestamp := int64(1704067200) // 2024-01-01 00:00:00 UTC
|
||||
method := "POST"
|
||||
path := "/execute"
|
||||
body := `{"language":"python","code":"print(1)"}`
|
||||
|
||||
signature := signRequest(secretKey, timestamp, method, path, []byte(body))
|
||||
|
||||
// Should return a 64-character hex string
|
||||
if len(signature) != 64 {
|
||||
t.Errorf("signRequest returned %d characters, want 64", len(signature))
|
||||
}
|
||||
|
||||
// Should be deterministic
|
||||
signature2 := signRequest(secretKey, timestamp, method, path, []byte(body))
|
||||
if signature != signature2 {
|
||||
t.Error("signRequest is not deterministic")
|
||||
}
|
||||
|
||||
// Different timestamps should produce different signatures
|
||||
signature3 := signRequest(secretKey, timestamp+1, method, path, []byte(body))
|
||||
if signature == signature3 {
|
||||
t.Error("signRequest returned same result for different timestamps")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCredentialsFromEnv(t *testing.T) {
|
||||
// Save original env vars
|
||||
origPK := os.Getenv("UNSANDBOX_PUBLIC_KEY")
|
||||
origSK := os.Getenv("UNSANDBOX_SECRET_KEY")
|
||||
|
||||
// Set test env vars
|
||||
testPK := "unsb-pk-test-test-test-test"
|
||||
testSK := "unsb-sk-test1-test2-test3-test4"
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", testPK)
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", testSK)
|
||||
|
||||
// Test
|
||||
creds, err := ResolveCredentials("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
if creds.PublicKey != testPK {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, testPK)
|
||||
}
|
||||
if creds.SecretKey != testSK {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, testSK)
|
||||
}
|
||||
|
||||
// Restore original env vars
|
||||
if origPK != "" {
|
||||
os.Setenv("UNSANDBOX_PUBLIC_KEY", origPK)
|
||||
} else {
|
||||
os.Unsetenv("UNSANDBOX_PUBLIC_KEY")
|
||||
}
|
||||
if origSK != "" {
|
||||
os.Setenv("UNSANDBOX_SECRET_KEY", origSK)
|
||||
} else {
|
||||
os.Unsetenv("UNSANDBOX_SECRET_KEY")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCredentialsFromArgs(t *testing.T) {
|
||||
testPK := "unsb-pk-arg1-arg2-arg3-arg4"
|
||||
testSK := "unsb-sk-arg11-arg22-arg33-arg44"
|
||||
|
||||
creds, err := ResolveCredentials(testPK, testSK)
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials failed: %v", err)
|
||||
}
|
||||
if creds.PublicKey != testPK {
|
||||
t.Errorf("PublicKey = %q, want %q", creds.PublicKey, testPK)
|
||||
}
|
||||
if creds.SecretKey != testSK {
|
||||
t.Errorf("SecretKey = %q, want %q", creds.SecretKey, testSK)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Functional Tests - Test against real API (requires credentials)
|
||||
// ============================================================================
|
||||
|
||||
func getTestCredentials(t *testing.T) *Credentials {
|
||||
creds, err := ResolveCredentials("", "")
|
||||
if err != nil {
|
||||
t.Skip("No credentials available for functional tests")
|
||||
}
|
||||
return creds
|
||||
}
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
healthy := HealthCheck()
|
||||
if !healthy {
|
||||
t.Log("API health check returned unhealthy (API may be unreachable)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLanguages(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
languages, err := GetLanguages(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("GetLanguages failed: %v", err)
|
||||
}
|
||||
|
||||
if len(languages) == 0 {
|
||||
t.Error("GetLanguages returned empty list")
|
||||
}
|
||||
|
||||
// Should include common languages
|
||||
hasPython := false
|
||||
hasJavascript := false
|
||||
for _, lang := range languages {
|
||||
if lang == "python" {
|
||||
hasPython = true
|
||||
}
|
||||
if lang == "javascript" {
|
||||
hasJavascript = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasPython {
|
||||
t.Error("GetLanguages missing 'python'")
|
||||
}
|
||||
if !hasJavascript {
|
||||
t.Error("GetLanguages missing 'javascript'")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateKeys(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
result, err := ValidateKeys(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateKeys failed: %v", err)
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
t.Error("ValidateKeys returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteCode(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
result, err := ExecuteCode(creds, "python", "print('hello from go test')")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteCode failed: %v", err)
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
t.Error("ExecuteCode returned nil")
|
||||
}
|
||||
|
||||
// Check for stdout in result
|
||||
if stdout, ok := result["stdout"].(string); ok {
|
||||
if stdout == "" {
|
||||
t.Error("ExecuteCode returned empty stdout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListSessions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
sessions, err := ListSessions(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSessions failed: %v", err)
|
||||
}
|
||||
|
||||
// Should return a list (possibly empty)
|
||||
if sessions == nil {
|
||||
t.Error("ListSessions returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListServices(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
services, err := ListServices(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ListServices failed: %v", err)
|
||||
}
|
||||
|
||||
// Should return a list (possibly empty)
|
||||
if services == nil {
|
||||
t.Error("ListServices returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListSnapshots(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
snapshots, err := ListSnapshots(creds)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSnapshots failed: %v", err)
|
||||
}
|
||||
|
||||
// Should return a list (possibly empty)
|
||||
if snapshots == nil {
|
||||
t.Error("ListSnapshots returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListImages(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping functional test in short mode")
|
||||
}
|
||||
|
||||
creds := getTestCredentials(t)
|
||||
images, err := ListImages(creds, "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListImages failed: %v", err)
|
||||
}
|
||||
|
||||
// Should return a list (possibly empty)
|
||||
if images == nil {
|
||||
t.Error("ListImages returned nil")
|
||||
}
|
||||
}
|
||||
3652
clients/javascript/sync/package-lock.json
generated
Normal file
3652
clients/javascript/sync/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
2195
clients/rust/sync/Cargo.lock
generated
Normal file
2195
clients/rust/sync/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue