| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- #!/usr/bin/env bash
- # tests.sh — mem0server endpoint tests
- # Usage: bash tests.sh [BASE_URL]
- # Default base: http://192.168.0.200:8420
- BASE="${1:-http://192.168.0.200:8420}"
- PASS=0
- FAIL=0
- LAST_JSON=""
- LAST_STATUS=""
- MEMORY_MARKER="mem0-test-case"
- # Colours
- GREEN='\033[0;32m'
- RED='\033[0;31m'
- CYAN='\033[0;36m'
- BOLD='\033[1m'
- RESET='\033[0m'
- sep() { echo -e "\n${CYAN}${BOLD}━━━ $* ━━━${RESET}"; }
- ok() { echo -e "${GREEN}✓ $*${RESET}"; ((PASS++)); }
- fail() { echo -e "${RED}✗ $*${RESET}"; ((FAIL++)); }
- run() {
- local label="$1"; shift
- local expected="200"
- if [[ "$1" =~ ^[0-9]{3}$ ]]; then
- expected="$1"
- shift
- fi
- echo -e "\n${BOLD}▶ $label${RESET}"
- local body
- body=$(curl -s -w "\n__STATUS__%{http_code}" "$@")
- local status
- status=$(echo "$body" | tail -1 | sed 's/__STATUS__//')
- local json
- json=$(echo "$body" | sed '$d')
- echo "$json" | jq . 2>/dev/null || echo "$json"
- LAST_STATUS="$status"
- LAST_JSON="$json"
- if [[ "$status" == "$expected" ]]; then
- ok "HTTP $status"
- else
- fail "HTTP $status (expected $expected)"
- fi
- }
- assert_json() {
- local label="$1" expr="$2"
- if echo "$LAST_JSON" | jq -e "$expr" >/dev/null 2>&1; then
- ok "$label"
- else
- fail "$label (jq: $expr)"
- fi
- }
- assert_contains() {
- local label="$1" substring="$2"
- if echo "$LAST_JSON" | grep -qF "$substring"; then
- ok "$label"
- else
- fail "$label (missing '$substring')"
- fi
- }
- sep "HEALTH"
- run "GET /health" "$BASE/health"
- assert_json "health -> collections object" '.collections | type == "object"'
- assert_json "health -> prompts present" '.prompts | type == "object"'
- sep "DASHBOARD"
- run "GET /dashboard" "$BASE/dashboard"
- assert_contains "dashboard contains HTML" "<html"
- sep "/memories (conversational)"
- run "POST /memories — plain text" \
- -X POST "$BASE/memories" \
- -H "Content-Type: application/json" \
- -d '{"text": "${MEMORY_MARKER} plain text entry for testing", "user_id": "testuser"}'
- run "POST /memories — messages array" \
- -X POST "$BASE/memories" \
- -H "Content-Type: application/json" \
- -d '{"messages": [{"role": "user", "content": "${MEMORY_MARKER} message entry for testing"}], "user_id": "testuser"}'
- run "POST /memories/all" \
- -X POST "$BASE/memories/all" \
- -H "Content-Type: application/json" \
- -d '{"user_id": "testuser"}'
- assert_json "memories/all returns array" '.results | type == "array"'
- run "POST /memories/search — capture marker" \
- -X POST "$BASE/memories/search" \
- -H "Content-Type: application/json" \
- -d '{"query": "$MEMORY_MARKER", "user_id": "testuser", "limit": 5 }'
- assert_json "memories marker search returns array" '.results | type == "array"'
- MEMORY_IDS=$(echo "$LAST_JSON" | jq -r '.results[] | .id')
- if [[ -z "$MEMORY_IDS" ]]; then
- fail "no test memory IDs captured"
- else
- ok "captured conversational memory IDs"
- fi
- run "POST /memories/search" \
- -X POST "$BASE/memories/search" \
- -H "Content-Type: application/json" \
- -d '{"query": "programming preferences", "user_id": "testuser", "limit": 5 }'
- assert_json "memories search returns array" '.results | type == "array"'
- run "POST /memories/recent" \
- -X POST "$BASE/memories/recent" \
- -H "Content-Type: application/json" \
- -d '{"user_id": "testuser", "limit": 5}'
- assert_json "memories recent returns array" '.results | type == "array"'
- if [[ -n "$MEMORY_IDS" ]]; then
- for id in $MEMORY_IDS; do
- run "DELETE /memory/$id" \
- -X DELETE "$BASE/memory/$id" \
- -H "Content-Type: application/json" \
- -d '{"collection": "conversational"}'
- done
- else
- fail "conversational memory IDs missing before cleanup"
- fi
- sep "/knowledge (knowledge base)"
- run "POST /knowledge — Gesell metadata" \
- -X POST "$BASE/knowledge" \
- -H "Content-Type: application/json" \
- -d '{"text": "Silvio Gesell proposed demurrage to discourage hoarding of currency.", "user_id": "knowledge_base", "metadata": {"source_file": "gesell_neo.pdf", "chapter": 3, "page": 47}}'
- run "POST /knowledge — MIDI verbatim" \
- -X POST "$BASE/knowledge" \
- -H "Content-Type: application/json" \
- -d '{"text": "MIDI SysEx uses a 7-bit checksum.", "user_id": "knowledge_base", "infer": false, "metadata": {"source_file": "midi_spec.pdf", "chapter": 9, "page": 112}}'
- run "POST /knowledge/search — Gesell demurrage" \
- -X POST "$BASE/knowledge/search" \
- -H "Content-Type: application/json" \
- -d '{"query": "Gesell demurrage", "user_id": "knowledge_base", "limit": 5}'
- assert_json "knowledge search results array" '.results | type == "array"'
- run "POST /knowledge/recent" \
- -X POST "$BASE/knowledge/recent" \
- -H "Content-Type: application/json" \
- -d '{"user_id": "knowledge_base", "limit": 5}'
- assert_json "knowledge recent returns array" '.results | type == "array"'
- run "POST /knowledge/sources" \
- -X POST "$BASE/knowledge/sources" \
- -H "Content-Type: application/json" \
- -d '{"user_id": "knowledge_base"}'
- assert_json "knowledge sources array" '.sources | type == "array"'
- assert_json "knowledge sources include Gesell" '.sources | any(.source_file == "gesell_neo.pdf")'
- run "DELETE /knowledge/by-source — gesell_neo.pdf" \
- -X DELETE "$BASE/knowledge/by-source" \
- -H "Content-Type: application/json" \
- -d '{"source_file": "gesell_neo.pdf", "user_id": "knowledge_base"}'
- assert_json "knowledge by-source removed entries" '.deleted | tonumber > 0'
- run "DELETE /knowledge/by-source — midi_spec.pdf" \
- -X DELETE "$BASE/knowledge/by-source" \
- -H "Content-Type: application/json" \
- -d '{"source_file": "midi_spec.pdf", "user_id": "knowledge_base"}'
- assert_json "knowledge by-source removed MIDI entries" '.deleted | tonumber > 0'
- sep "/search (merged)"
- run "POST /search — Python programming" \
- -X POST "$BASE/search" \
- -H "Content-Type: application/json" \
- -d '{"query": "Python programming", "user_id": "testuser", "limit": 8}'
- assert_json "merged search results array" '.results | type == "array"'
- assert_json "merged search items have _source" 'if (.results | length) == 0 then true else (.results[] | has("_source")) end'
- run "POST /search — Gesell economic theory" \
- -X POST "$BASE/search" \
- -H "Content-Type: application/json" \
- -d '{"query": "Gesell economic theory", "user_id": "knowledge_base", "limit": 5}'
- assert_json "merged search results array (Gesell)" '.results | type == "array"'
- assert_json "merged Gesell results tag _source" 'if (.results | length) == 0 then true else (.results[] | has("_source")) end'
- sep "ERROR HANDLING"
- run "POST /memories — missing text" 400 \
- -X POST "$BASE/memories" \
- -H "Content-Type: application/json" \
- -d '{"user_id": "testuser"}'
- run "POST /memories — infer:false ignored" \
- -X POST "$BASE/memories" \
- -H "Content-Type: application/json" \
- -d '{"text": "This should still go through LLM extraction", "user_id": "testuser", "infer": false}'
- run "POST /search — empty query" \
- -X POST "$BASE/search" \
- -H "Content-Type: application/json" \
- -d '{"query": "", "user_id": "testuser"}'
- assert_json "empty search returns results array" '.results | type == "array"'
- assert_json "empty search returns zero results" '.results | length == 0'
- sep "RESULTS"
- echo -e "\n${BOLD}Passed: ${GREEN}$PASS${RESET} ${BOLD}Failed: ${RED}$FAIL${RESET}\n"
|