237 lines
6.9 KiB
Text
237 lines
6.9 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
|
||
|
|
# Hetzner provider pricing and cache integration tests (with mocks)
|
||
|
|
|
||
|
|
# Load mock responses
|
||
|
|
def load-mock-responses []: nothing -> record {
|
||
|
|
let mocks = (open ../../tests/mocks/mock_api_responses.json)
|
||
|
|
$mocks
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mock: get pricing data
|
||
|
|
def mock-get-pricing []: nothing -> record {
|
||
|
|
let mocks = (load-mock-responses)
|
||
|
|
{server_types: $mocks.server_types_list.server_types}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mock: cache operations
|
||
|
|
def mock-create-cache-entry [server: string]: nothing -> record {
|
||
|
|
{
|
||
|
|
server: $server
|
||
|
|
timestamp: (now)
|
||
|
|
cached_at: (date now | date to-record)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
def mock-read-cache-entry [server: string]: nothing -> record {
|
||
|
|
{
|
||
|
|
server: $server
|
||
|
|
timestamp: (now)
|
||
|
|
cached_at: (date now | date to-record)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test framework
|
||
|
|
def test-result [name: string, passed: bool]: nothing -> record {
|
||
|
|
if $passed {
|
||
|
|
print $"✓ ($name)"
|
||
|
|
} else {
|
||
|
|
print $"✗ ($name)"
|
||
|
|
}
|
||
|
|
{name: $name, passed: $passed}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Get pricing data
|
||
|
|
def test_get_pricing_data []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
($pricing | has server_types)
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "get_pricing: returns pricing data" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Server type pricing structure
|
||
|
|
def test_server_type_pricing_structure []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
let st = ($pricing.server_types | first)
|
||
|
|
{
|
||
|
|
has_name: ($st | has name)
|
||
|
|
has_cores: ($st | has cores)
|
||
|
|
has_prices: ($st | has prices)
|
||
|
|
}
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and
|
||
|
|
$result.stdout.has_name and
|
||
|
|
$result.stdout.has_cores and
|
||
|
|
$result.stdout.has_prices)
|
||
|
|
test-result "pricing: server type structure" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Pricing hourly rate exists
|
||
|
|
def test_pricing_hourly_rate []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
let st = ($pricing.server_types | first)
|
||
|
|
let price = ($st.prices | first)
|
||
|
|
($price | has price_hourly)
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "pricing: hourly rate field" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Pricing monthly rate exists
|
||
|
|
def test_pricing_monthly_rate []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
let st = ($pricing.server_types | first)
|
||
|
|
let price = ($st.prices | first)
|
||
|
|
($price | has price_monthly)
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "pricing: monthly rate field" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Create cache entry
|
||
|
|
def test_create_cache_entry []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let cache = (mock-create-cache-entry "test-server")
|
||
|
|
{
|
||
|
|
has_server: ($cache | has server)
|
||
|
|
has_timestamp: ($cache | has timestamp)
|
||
|
|
}
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and
|
||
|
|
$result.stdout.has_server and
|
||
|
|
$result.stdout.has_timestamp)
|
||
|
|
test-result "cache: create entry structure" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Read cache entry
|
||
|
|
def test_read_cache_entry []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let cache = (mock-read-cache-entry "test-server")
|
||
|
|
$cache.server
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout == "test-server")
|
||
|
|
test-result "cache: read entry" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Cache timestamp is valid
|
||
|
|
def test_cache_timestamp_valid []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let cache = (mock-create-cache-entry "test-server")
|
||
|
|
($cache.timestamp | describe) =~ "int"
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "cache: timestamp is integer" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Price with net and gross
|
||
|
|
def test_price_net_and_gross []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
let st = ($pricing.server_types | first)
|
||
|
|
let price = ($st.prices | first)
|
||
|
|
let hourly = $price.price_hourly
|
||
|
|
{
|
||
|
|
has_net: ($hourly | has net)
|
||
|
|
has_gross: ($hourly | has gross)
|
||
|
|
}
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and
|
||
|
|
$result.stdout.has_net and
|
||
|
|
$result.stdout.has_gross)
|
||
|
|
test-result "pricing: net and gross prices" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Multiple server types pricing
|
||
|
|
def test_multiple_server_types []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
($pricing.server_types | length)
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout > 1)
|
||
|
|
test-result "pricing: multiple server types" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Cache entry contains server name
|
||
|
|
def test_cache_server_name []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let cache = (mock-create-cache-entry "my-production-server")
|
||
|
|
$cache.server == "my-production-server"
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "cache: preserves server name" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Calculate pricing per GB
|
||
|
|
def test_pricing_per_gb []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
# Volume pricing: 0.085 EUR per GB per month
|
||
|
|
let volume_gb = 100
|
||
|
|
let volume_price = $volume_gb * 0.085
|
||
|
|
$volume_price > 0
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "pricing: volume per GB calculation" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Test: Server type names available
|
||
|
|
def test_server_type_names []: nothing -> record {
|
||
|
|
let result = (do {
|
||
|
|
let pricing = (mock-get-pricing)
|
||
|
|
let names = ($pricing.server_types | map {|st| $st.name})
|
||
|
|
("cx11" in $names) and ("cx21" in $names)
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
let passed = ($result.exit_code == 0 and $result.stdout)
|
||
|
|
test-result "pricing: standard server types available" $passed
|
||
|
|
}
|
||
|
|
|
||
|
|
# Main test runner
|
||
|
|
def main []: nothing -> record {
|
||
|
|
print "=== Hetzner Pricing & Cache Integration Tests (Mock Mode) ==="
|
||
|
|
print ""
|
||
|
|
|
||
|
|
let results = [
|
||
|
|
(test_get_pricing_data)
|
||
|
|
(test_server_type_pricing_structure)
|
||
|
|
(test_pricing_hourly_rate)
|
||
|
|
(test_pricing_monthly_rate)
|
||
|
|
(test_create_cache_entry)
|
||
|
|
(test_read_cache_entry)
|
||
|
|
(test_cache_timestamp_valid)
|
||
|
|
(test_price_net_and_gross)
|
||
|
|
(test_multiple_server_types)
|
||
|
|
(test_cache_server_name)
|
||
|
|
(test_pricing_per_gb)
|
||
|
|
(test_server_type_names)
|
||
|
|
]
|
||
|
|
|
||
|
|
print ""
|
||
|
|
print "Test Summary:"
|
||
|
|
print "============="
|
||
|
|
|
||
|
|
let passed = ($results | where {|r| $r.passed} | length)
|
||
|
|
let failed = ($results | where {|r| not $r.passed} | length)
|
||
|
|
let total = ($results | length)
|
||
|
|
|
||
|
|
print $"Passed: ($passed)/$total"
|
||
|
|
print $"Failed: ($failed)/$total"
|
||
|
|
|
||
|
|
{passed: $passed, failed: $failed, total: $total}
|
||
|
|
}
|