310 lines
10 KiB
Python
310 lines
10 KiB
Python
# 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 new SDK functions (feature parity with C implementation)"""
|
|
|
|
import pytest
|
|
from un import (
|
|
# Execution
|
|
execute_code,
|
|
execute_async,
|
|
get_job,
|
|
wait_for_job,
|
|
cancel_job,
|
|
list_jobs,
|
|
get_languages,
|
|
detect_language,
|
|
# Sessions
|
|
list_sessions,
|
|
get_session,
|
|
create_session,
|
|
delete_session,
|
|
freeze_session,
|
|
unfreeze_session,
|
|
boost_session,
|
|
unboost_session,
|
|
shell_session,
|
|
# Services
|
|
list_services,
|
|
create_service,
|
|
get_service,
|
|
update_service,
|
|
delete_service,
|
|
freeze_service,
|
|
unfreeze_service,
|
|
lock_service,
|
|
unlock_service,
|
|
set_unfreeze_on_demand,
|
|
get_service_logs,
|
|
get_service_env,
|
|
set_service_env,
|
|
delete_service_env,
|
|
export_service_env,
|
|
redeploy_service,
|
|
execute_in_service,
|
|
resize_service,
|
|
# Snapshots
|
|
session_snapshot,
|
|
service_snapshot,
|
|
list_snapshots,
|
|
get_snapshot,
|
|
restore_snapshot,
|
|
delete_snapshot,
|
|
lock_snapshot,
|
|
unlock_snapshot,
|
|
clone_snapshot,
|
|
# Images
|
|
image_publish,
|
|
list_images,
|
|
get_image,
|
|
delete_image,
|
|
lock_image,
|
|
unlock_image,
|
|
set_image_visibility,
|
|
grant_image_access,
|
|
revoke_image_access,
|
|
list_image_trusted,
|
|
transfer_image,
|
|
spawn_from_image,
|
|
clone_image,
|
|
# PaaS Logs
|
|
logs_fetch,
|
|
logs_stream,
|
|
# Utilities
|
|
version,
|
|
health_check,
|
|
last_error,
|
|
hmac_sign,
|
|
validate_keys,
|
|
)
|
|
|
|
|
|
class TestUtilityFunctions:
|
|
"""Test utility functions that don't require API calls"""
|
|
|
|
def test_version_returns_string(self):
|
|
"""Test version function returns a string"""
|
|
v = version()
|
|
assert isinstance(v, str)
|
|
assert len(v) > 0
|
|
# Should be semantic version format
|
|
parts = v.split('.')
|
|
assert len(parts) >= 2
|
|
|
|
def test_hmac_sign_basic(self):
|
|
"""Test HMAC signing produces correct format"""
|
|
signature = hmac_sign("secret_key", "message_to_sign")
|
|
assert isinstance(signature, str)
|
|
assert len(signature) == 64
|
|
# Should be lowercase hex
|
|
assert all(c in '0123456789abcdef' for c in signature)
|
|
|
|
def test_hmac_sign_deterministic(self):
|
|
"""Test HMAC signing is deterministic"""
|
|
sig1 = hmac_sign("secret", "message")
|
|
sig2 = hmac_sign("secret", "message")
|
|
assert sig1 == sig2
|
|
|
|
def test_hmac_sign_different_secrets(self):
|
|
"""Test different secrets produce different signatures"""
|
|
sig1 = hmac_sign("secret1", "message")
|
|
sig2 = hmac_sign("secret2", "message")
|
|
assert sig1 != sig2
|
|
|
|
def test_hmac_sign_different_messages(self):
|
|
"""Test different messages produce different signatures"""
|
|
sig1 = hmac_sign("secret", "message1")
|
|
sig2 = hmac_sign("secret", "message2")
|
|
assert sig1 != sig2
|
|
|
|
def test_hmac_sign_known_value(self):
|
|
"""Test HMAC signing matches known value"""
|
|
# This is a known test vector
|
|
signature = hmac_sign("test_secret", "1234567890:POST:/execute:")
|
|
assert len(signature) == 64
|
|
|
|
def test_last_error_initial_none(self):
|
|
"""Test last_error is None initially or after success"""
|
|
# Note: This may not be None if previous tests failed
|
|
error = last_error()
|
|
assert error is None or isinstance(error, str)
|
|
|
|
|
|
class TestFunctionExports:
|
|
"""Test that all required functions are exported"""
|
|
|
|
def test_execution_functions_exported(self):
|
|
"""Test execution functions are exported"""
|
|
assert callable(execute_code)
|
|
assert callable(execute_async)
|
|
assert callable(get_job)
|
|
assert callable(wait_for_job)
|
|
assert callable(cancel_job)
|
|
assert callable(list_jobs)
|
|
assert callable(get_languages)
|
|
assert callable(detect_language)
|
|
|
|
def test_session_functions_exported(self):
|
|
"""Test session functions are exported"""
|
|
assert callable(list_sessions)
|
|
assert callable(get_session)
|
|
assert callable(create_session)
|
|
assert callable(delete_session)
|
|
assert callable(freeze_session)
|
|
assert callable(unfreeze_session)
|
|
assert callable(boost_session)
|
|
assert callable(unboost_session)
|
|
assert callable(shell_session)
|
|
|
|
def test_service_functions_exported(self):
|
|
"""Test service functions are exported"""
|
|
assert callable(list_services)
|
|
assert callable(create_service)
|
|
assert callable(get_service)
|
|
assert callable(update_service)
|
|
assert callable(delete_service)
|
|
assert callable(freeze_service)
|
|
assert callable(unfreeze_service)
|
|
assert callable(lock_service)
|
|
assert callable(unlock_service)
|
|
assert callable(set_unfreeze_on_demand)
|
|
assert callable(get_service_logs)
|
|
assert callable(get_service_env)
|
|
assert callable(set_service_env)
|
|
assert callable(delete_service_env)
|
|
assert callable(export_service_env)
|
|
assert callable(redeploy_service)
|
|
assert callable(execute_in_service)
|
|
assert callable(resize_service) # New function
|
|
|
|
def test_snapshot_functions_exported(self):
|
|
"""Test snapshot functions are exported"""
|
|
assert callable(session_snapshot)
|
|
assert callable(service_snapshot)
|
|
assert callable(list_snapshots)
|
|
assert callable(get_snapshot) # New function
|
|
assert callable(restore_snapshot)
|
|
assert callable(delete_snapshot)
|
|
assert callable(lock_snapshot)
|
|
assert callable(unlock_snapshot)
|
|
assert callable(clone_snapshot)
|
|
|
|
def test_image_functions_exported(self):
|
|
"""Test image functions are exported"""
|
|
assert callable(image_publish)
|
|
assert callable(list_images)
|
|
assert callable(get_image)
|
|
assert callable(delete_image)
|
|
assert callable(lock_image)
|
|
assert callable(unlock_image)
|
|
assert callable(set_image_visibility)
|
|
assert callable(grant_image_access)
|
|
assert callable(revoke_image_access)
|
|
assert callable(list_image_trusted)
|
|
assert callable(transfer_image)
|
|
assert callable(spawn_from_image)
|
|
assert callable(clone_image)
|
|
|
|
def test_logs_functions_exported(self):
|
|
"""Test PaaS logs functions are exported"""
|
|
assert callable(logs_fetch) # New function
|
|
assert callable(logs_stream) # New function
|
|
|
|
def test_utility_functions_exported(self):
|
|
"""Test utility functions are exported"""
|
|
assert callable(validate_keys)
|
|
assert callable(version) # New function
|
|
assert callable(health_check) # New function
|
|
assert callable(last_error) # New function
|
|
assert callable(hmac_sign) # New function
|
|
|
|
|
|
class TestLanguageDetection:
|
|
"""Test language detection (subset, full tests in test_language_detection.py)"""
|
|
|
|
def test_common_extensions(self):
|
|
"""Test common file extensions"""
|
|
assert detect_language("test.py") == "python"
|
|
assert detect_language("test.js") == "javascript"
|
|
assert detect_language("test.rb") == "ruby"
|
|
assert detect_language("test.go") == "go"
|
|
assert detect_language("test.rs") == "rust"
|
|
|
|
def test_unknown_returns_none(self):
|
|
"""Test unknown extensions return None"""
|
|
assert detect_language("test.unknown") is None
|
|
assert detect_language("Makefile") is None
|
|
|
|
|
|
# Functional tests require API credentials - mark as skip without credentials
|
|
@pytest.mark.skipif(
|
|
not all([
|
|
__import__('os').environ.get('UNSANDBOX_PUBLIC_KEY'),
|
|
__import__('os').environ.get('UNSANDBOX_SECRET_KEY'),
|
|
]),
|
|
reason="UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY required"
|
|
)
|
|
class TestFunctionalAPI:
|
|
"""Functional tests that require real API credentials"""
|
|
|
|
def test_health_check(self):
|
|
"""Test API health check"""
|
|
result = health_check()
|
|
assert isinstance(result, bool)
|
|
# API should be healthy
|
|
assert result is True
|
|
|
|
def test_validate_keys(self):
|
|
"""Test key validation"""
|
|
result = validate_keys()
|
|
assert isinstance(result, dict)
|
|
assert 'valid' in result or 'tier' in result
|
|
|
|
def test_get_languages(self):
|
|
"""Test getting supported languages"""
|
|
languages = get_languages()
|
|
assert isinstance(languages, list)
|
|
assert len(languages) > 0
|
|
assert 'python' in languages
|
|
assert 'javascript' in languages
|
|
|
|
def test_list_sessions(self):
|
|
"""Test listing sessions"""
|
|
sessions = list_sessions()
|
|
assert isinstance(sessions, list)
|
|
|
|
def test_list_services(self):
|
|
"""Test listing services"""
|
|
services = list_services()
|
|
assert isinstance(services, list)
|
|
|
|
def test_list_snapshots(self):
|
|
"""Test listing snapshots"""
|
|
snapshots = list_snapshots()
|
|
assert isinstance(snapshots, list)
|
|
|
|
def test_list_images(self):
|
|
"""Test listing images"""
|
|
images = list_images()
|
|
assert isinstance(images, list)
|
|
|
|
def test_execute_code(self):
|
|
"""Test code execution"""
|
|
result = execute_code("python", "print('hello world')")
|
|
assert isinstance(result, dict)
|
|
assert result.get('status') in ['completed', 'pending']
|
|
if result.get('status') == 'completed':
|
|
assert 'hello world' in result.get('stdout', '')
|