240 lines
8.4 KiB
Bash
Executable File
240 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# BUD-08 NIP-94 File Metadata Tags Test Suite
|
||
# This test is created FIRST (TDD) and will FAIL until implementation is added to src/main.c.
|
||
#
|
||
# Expected procedures to be implemented in src/main.c (BUD-08 section):
|
||
# - nip94_is_enabled() -> Read server_config.nip94_enabled (default true)
|
||
# - nip94_get_origin() -> Read server_config.cdn_origin (default http://localhost:9001)
|
||
# - mime_to_extension() -> Centralize MIME to extension mapping
|
||
# - nip94_build_blob_url() -> Build canonical blob URL from origin + sha256 + extension
|
||
# - get_file_dimensions() -> Detect WxH for PNG, JPEG, WebP (parse headers only)
|
||
# - nip94_build_tags() -> Build tags: ["url",...], ["m",...], ["x",...], ["size",...], optional ["dim","WxH"]
|
||
# - nip94_attach_to_descriptor() -> Attach nip94 array to JSON response in /upload and /mirror
|
||
#
|
||
# Integration points expected:
|
||
# - PUT /upload success JSON includes "nip94" array
|
||
# - PUT /mirror success JSON includes "nip94" array
|
||
#
|
||
# Requirements:
|
||
# - curl, jq, sqlite3 must be available
|
||
# - Server should be running at http://localhost:9001 (restart-all.sh)
|
||
|
||
set -e
|
||
|
||
SERVER_URL="http://localhost:9001"
|
||
UPLOAD_ENDPOINT="${SERVER_URL}/upload"
|
||
MIRROR_ENDPOINT="${SERVER_URL}/mirror"
|
||
DB_PATH="db/ginxsom.db"
|
||
|
||
echo "=== BUD-08 NIP-94 File Metadata Tags Test Suite ==="
|
||
|
||
# Prereq checks
|
||
if ! command -v curl >/dev/null 2>&1; then
|
||
echo "ERROR: curl not found"
|
||
exit 1
|
||
fi
|
||
if ! command -v jq >/dev/null 2>&1; then
|
||
echo "ERROR: jq not found (required for JSON parsing)"
|
||
exit 1
|
||
fi
|
||
if ! command -v sqlite3 >/dev/null 2>&1; then
|
||
echo "ERROR: sqlite3 not found"
|
||
exit 1
|
||
fi
|
||
|
||
# Helpers
|
||
die() {
|
||
echo "FATAL: $*" 1>&2
|
||
exit 1
|
||
}
|
||
|
||
json_has_nip94() {
|
||
local json="$1"
|
||
echo "$json" | jq -e '.nip94 and ( .nip94 | type == "array" )' >/dev/null 2>&1
|
||
}
|
||
|
||
nip94_get_tag() {
|
||
local json="$1"
|
||
local key="$2"
|
||
echo "$json" | jq -r --arg k "$key" '.nip94 | map(select(.[0]==$k)) | if length>0 then .[0][1] else empty end'
|
||
}
|
||
|
||
# Authentication helper - create Blossom auth header for uploads
|
||
create_auth_header() {
|
||
local file_path="$1"
|
||
local hash=$(sha256sum "$file_path" | awk '{print $1}')
|
||
|
||
# Create Blossom event (kind 24242) with required tags
|
||
local expiration=$(date -d "+3600 seconds" +%s)
|
||
local event=$(nak event -k 24242 -c "" \
|
||
--tag "t=upload" \
|
||
--tag "x=$hash" \
|
||
--tag "expiration=$expiration" \
|
||
--sec "0000000000000000000000000000000000000000000000000000000000000001")
|
||
|
||
echo "Nostr $(echo "$event" | base64 -w 0)"
|
||
}
|
||
|
||
# Configuration is stored in database 'config' table with key-value pairs
|
||
|
||
# Create temporary working directory
|
||
WORKDIR="tests/tmp_bud08"
|
||
mkdir -p "$WORKDIR"
|
||
|
||
# Create a tiny 1x1 PNG (base64)
|
||
TINY_PNG_B64="iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO0RzjoAAAAASUVORK5CYII="
|
||
PNG_FILE="$WORKDIR/one_by_one.png"
|
||
echo "$TINY_PNG_B64" | base64 -d > "$PNG_FILE"
|
||
|
||
# Compute expected values
|
||
CONTENT_TYPE="image/png"
|
||
FILE_SIZE=$(wc -c < "$PNG_FILE" | tr -d ' ')
|
||
SHA256_HEX=$(sha256sum "$PNG_FILE" | awk '{print $1}')
|
||
|
||
echo ""
|
||
echo "Test artifact:"
|
||
echo " File: $PNG_FILE"
|
||
echo " Size: $FILE_SIZE"
|
||
echo " SHA256: $SHA256_HEX"
|
||
echo ""
|
||
|
||
# --- Test 1: PUT /upload returns nip94 with minimal required tags
|
||
echo "=== Test 1: PUT /upload returns nip94 minimal tags ==="
|
||
AUTH_HEADER=$(create_auth_header "$PNG_FILE")
|
||
UPLOAD_JSON=$(curl -s -X PUT "$UPLOAD_ENDPOINT" \
|
||
-H "Authorization: $AUTH_HEADER" \
|
||
-H "Content-Type: $CONTENT_TYPE" \
|
||
--data-binary @"$PNG_FILE")
|
||
|
||
echo "Upload Response JSON:"
|
||
echo "$UPLOAD_JSON" | jq '.' 2>/dev/null || echo "$UPLOAD_JSON"
|
||
echo ""
|
||
|
||
if json_has_nip94 "$UPLOAD_JSON"; then
|
||
URL_FIELD=$(echo "$UPLOAD_JSON" | jq -r '.url')
|
||
TYPE_FIELD=$(echo "$UPLOAD_JSON" | jq -r '.type')
|
||
SIZE_FIELD=$(echo "$UPLOAD_JSON" | jq -r '.size')
|
||
SHA256_FIELD=$(echo "$UPLOAD_JSON" | jq -r '.sha256')
|
||
|
||
TAG_URL=$(nip94_get_tag "$UPLOAD_JSON" "url")
|
||
TAG_M=$(nip94_get_tag "$UPLOAD_JSON" "m")
|
||
TAG_X=$(nip94_get_tag "$UPLOAD_JSON" "x")
|
||
TAG_SIZE=$(nip94_get_tag "$UPLOAD_JSON" "size")
|
||
|
||
PASS=1
|
||
[ -n "$TAG_URL" ] || { echo "FAIL: nip94 missing url tag"; PASS=0; }
|
||
[ -n "$TAG_M" ] || { echo "FAIL: nip94 missing m tag"; PASS=0; }
|
||
[ -n "$TAG_X" ] || { echo "FAIL: nip94 missing x tag"; PASS=0; }
|
||
[ -n "$TAG_SIZE" ] || { echo "FAIL: nip94 missing size tag"; PASS=0; }
|
||
|
||
# Validate tag values match descriptor
|
||
[ "$TAG_URL" = "$URL_FIELD" ] || { echo "FAIL: nip94 url tag != descriptor url"; PASS=0; }
|
||
[ "$TAG_M" = "$TYPE_FIELD" ] || { echo "FAIL: nip94 m tag != descriptor type"; PASS=0; }
|
||
[ "$TAG_X" = "$SHA256_FIELD" ] || { echo "FAIL: nip94 x tag != descriptor sha256"; PASS=0; }
|
||
[ "$TAG_SIZE" = "$SIZE_FIELD" ] || { echo "FAIL: nip94 size tag != descriptor size"; PASS=0; }
|
||
|
||
if [ "$PASS" = "1" ]; then
|
||
echo "✅ Test 1 PASSED: nip94 minimal tags present and correct"
|
||
else
|
||
echo "❌ Test 1 FAILED"
|
||
fi
|
||
else
|
||
echo "❌ Test 1 FAILED: Response missing nip94 array"
|
||
fi
|
||
|
||
# --- Test 2: dim present and equals 1x1 for PNG
|
||
echo ""
|
||
echo "=== Test 2: dim tag for 1x1 PNG ==="
|
||
echo "Response JSON (same as Test 1):"
|
||
echo "$UPLOAD_JSON" | jq '.' 2>/dev/null || echo "$UPLOAD_JSON"
|
||
echo ""
|
||
|
||
TAG_DIM=$(nip94_get_tag "$UPLOAD_JSON" "dim" || true)
|
||
if [ -n "$TAG_DIM" ]; then
|
||
if [ "$TAG_DIM" = "1x1" ]; then
|
||
echo "✅ Test 2 PASSED: dim tag present and equals 1x1"
|
||
else
|
||
echo "❌ Test 2 FAILED: dim tag expected 1x1, got '$TAG_DIM'"
|
||
fi
|
||
else
|
||
echo "❌ Test 2 FAILED: dim tag not present"
|
||
fi
|
||
|
||
# --- Test 3: Check configuration defaults in config table
|
||
echo ""
|
||
echo "=== Test 3: Configuration defaults test ==="
|
||
echo "Database Configuration JSON:"
|
||
CONFIG_JSON=$(sqlite3 "$DB_PATH" "SELECT json_object('key', key, 'value', value) FROM config WHERE key IN ('nip94_enabled', 'cdn_origin') ORDER BY key;" 2>/dev/null | sed 's/^/ /')
|
||
if [ -n "$CONFIG_JSON" ]; then
|
||
echo "$CONFIG_JSON" | while read line; do echo " $line"; done
|
||
else
|
||
echo " No NIP-94 config found"
|
||
fi
|
||
echo ""
|
||
|
||
echo -n "Test 3 - Configuration defaults: "
|
||
if sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM config WHERE key IN ('nip94_enabled', 'cdn_origin');" | grep -q "2"; then
|
||
echo "✓ PASS - Configuration defaults found"
|
||
else
|
||
echo "✗ FAIL - Missing configuration defaults"
|
||
echo "Debug: config table contents:"
|
||
sqlite3 "$DB_PATH" "SELECT * FROM config;" 2>/dev/null || echo "config table does not exist"
|
||
fi
|
||
|
||
# --- Test 4: Check NIP-94 enabled configuration
|
||
echo ""
|
||
echo "=== Test 4: NIP-94 enabled check test ==="
|
||
echo "NIP-94 Configuration JSON:"
|
||
NIP94_CONFIG_JSON=$(sqlite3 "$DB_PATH" "SELECT json_object('nip94_enabled', value) FROM config WHERE key='nip94_enabled';" 2>/dev/null)
|
||
if [ -n "$NIP94_CONFIG_JSON" ]; then
|
||
echo " $NIP94_CONFIG_JSON"
|
||
else
|
||
echo " {\"nip94_enabled\": null}"
|
||
fi
|
||
echo ""
|
||
|
||
echo -n "Test 4 - NIP-94 enabled check: "
|
||
nip94_enabled=$(sqlite3 "$DB_PATH" "SELECT value FROM config WHERE key='nip94_enabled';" 2>/dev/null)
|
||
if [[ "$nip94_enabled" == "true" ]]; then
|
||
echo "✓ PASS - NIP-94 is enabled"
|
||
else
|
||
echo "✗ FAIL - NIP-94 not enabled (got: '$nip94_enabled')"
|
||
fi
|
||
|
||
# --- Test 5: PUT /mirror returns nip94 minimal tags (best effort, network dependent)
|
||
echo ""
|
||
echo "=== Test 5: PUT /mirror returns nip94 minimal tags (network dependent) ==="
|
||
# Use a public small PNG; if network/policy blocks, mark INFO instead of failure.
|
||
REMOTE_URL="https://upload.wikimedia.org/wikipedia/commons/3/3c/Shaki_waterfall.jpg"
|
||
MIRROR_JSON=$(curl -s -X PUT "$MIRROR_ENDPOINT" \
|
||
-H "Content-Type: application/json" \
|
||
--data "{\"url\":\"$REMOTE_URL\"}")
|
||
|
||
echo "Mirror Response JSON:"
|
||
echo "$MIRROR_JSON" | jq '.' 2>/dev/null || echo "$MIRROR_JSON"
|
||
echo ""
|
||
|
||
HTTP_OK=$(echo "$MIRROR_JSON" | jq -e '.sha256 and .type and .size' >/dev/null 2>&1; echo $?)
|
||
if [ "$HTTP_OK" = "0" ]; then
|
||
if json_has_nip94 "$MIRROR_JSON"; then
|
||
TAG_URL_M=$(nip94_get_tag "$MIRROR_JSON" "url")
|
||
TAG_M_M=$(nip94_get_tag "$MIRROR_JSON" "m")
|
||
TAG_X_M=$(nip94_get_tag "$MIRROR_JSON" "x")
|
||
TAG_SIZE_M=$(nip94_get_tag "$MIRROR_JSON" "size")
|
||
if [ -n "$TAG_URL_M" ] && [ -n "$TAG_M_M" ] && [ -n "$TAG_X_M" ] && [ -n "$TAG_SIZE_M" ]; then
|
||
echo "✅ Test 5 PASSED: nip94 minimal tags present for mirror"
|
||
else
|
||
echo "❌ Test 5 FAILED: nip94 minimal tags missing for mirror response"
|
||
fi
|
||
else
|
||
echo "❌ Test 5 FAILED: mirror response missing nip94 array"
|
||
fi
|
||
else
|
||
echo "ℹ️ Test 5 INFO: mirror request did not return a blob descriptor (network or policy); skipping strict check"
|
||
fi
|
||
|
||
# Cleanup
|
||
rm -rf "$WORKDIR"
|
||
|
||
echo ""
|
||
echo "=== End of BUD-08 NIP-94 Test Suite ===" |