diff --git a/tests/curl-test-collection.sh b/tests/curl-test-collection.sh new file mode 100644 index 0000000..1d2ebad --- /dev/null +++ b/tests/curl-test-collection.sh @@ -0,0 +1,514 @@ +#!/bin/bash + +############################################################################### +# E2E Test Collection - Service Health & Connectivity Checks +# Purpose: Verify all services are running and accessible +# Author: QA/Testing Agent +# Date: 2026-03-16 +############################################################################### + +set -e # Exit on error + +# Color output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Counters +PASSED=0 +FAILED=0 +SKIPPED=0 + +# Configuration +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# Load environment variables if .env exists +if [ -f "$PROJECT_ROOT/.env" ]; then + source "$PROJECT_ROOT/.env" +fi + +# Default values (can be overridden by env vars) +FREESCOUT_HOST="${FREESCOUT_HOST:-https://ekshelpdesk.fft-it.de}" +LITELLM_HOST="${LITELLM_HOST:-http://llm.eks-ai.apps.asgard.eks-lnx.fft-it.de}" +MILVUS_HOST="${MILVUS_HOST:-127.0.0.1}" +MILVUS_PORT="${MILVUS_PORT:-19530}" +POSTGRES_HOST="${POSTGRES_HOST:-127.0.0.1}" +POSTGRES_PORT="${POSTGRES_PORT:-5432}" +POSTGRES_USER="${POSTGRES_USER:-kb_user}" +POSTGRES_DB="${POSTGRES_DB:-n8n_kb}" +N8N_HOST="${N8N_HOST:-http://localhost}" +N8N_PORT="${N8N_PORT:-5678}" + +############################################################################### +# Helper Functions +############################################################################### + +log_header() { + echo -e "\n${BLUE}=== $1 ===${NC}\n" +} + +log_pass() { + echo -e "${GREEN}✓ PASS${NC}: $1" + ((PASSED++)) +} + +log_fail() { + echo -e "${RED}✗ FAIL${NC}: $1" + ((FAILED++)) +} + +log_skip() { + echo -e "${YELLOW}⊘ SKIP${NC}: $1" + ((SKIPPED++)) +} + +log_info() { + echo -e "${BLUE}ℹ INFO${NC}: $1" +} + +test_endpoint() { + local name="$1" + local method="${2:-GET}" + local url="$3" + local headers="$4" + local data="$5" + local expected_code="${6:-200}" + + echo -n "Testing $name... " + + # Build curl command + local curl_cmd="curl -s -w '%{http_code}' -X $method" + + if [ -n "$headers" ]; then + curl_cmd="$curl_cmd -H '$headers'" + fi + + if [ "$method" != "GET" ] && [ -n "$data" ]; then + curl_cmd="$curl_cmd -d '$data'" + fi + + curl_cmd="$curl_cmd '$url' -o /tmp/response.txt" + + # Execute and capture response code + local response_code=$(eval "$curl_cmd" 2>/dev/null || echo "000") + + if [ "$response_code" = "$expected_code" ]; then + log_pass "$name (HTTP $response_code)" + return 0 + else + log_fail "$name (Expected HTTP $expected_code, got $response_code)" + cat /tmp/response.txt 2>/dev/null | head -c 200 + echo "" + return 1 + fi +} + +############################################################################### +# Test Suite +############################################################################### + +main() { + echo -e "${BLUE}" + cat << 'EOF' +╔════════════════════════════════════════════════════════════════════════════╗ +║ E2E Test Collection - Health Checks ║ +║ ║ +║ This script verifies that all microservices are running and accessible. ║ +║ It performs basic connectivity tests and returns a summary report. ║ +╚════════════════════════════════════════════════════════════════════════════╝ +EOF + echo -e "${NC}\n" + + # Test Milvus Vector DB + log_header "1. Testing Milvus Vector Database" + test_milvus + + # Test PostgreSQL + log_header "2. Testing PostgreSQL Database" + test_postgres + + # Test Freescout + log_header "3. Testing Freescout API" + test_freescout + + # Test LiteLLM + log_header "4. Testing LiteLLM API" + test_litellm + + # Test n8n + log_header "5. Testing n8n Workflow Engine" + test_n8n + + # Test Docker Compose Stack + log_header "6. Testing Docker Compose Services" + test_docker_compose + + # Print Summary + print_summary +} + +############################################################################### +# Individual Test Functions +############################################################################### + +test_milvus() { + local milvus_url="http://$MILVUS_HOST:$MILVUS_PORT" + + echo "Checking Milvus at $milvus_url" + + # Test health endpoint + echo -n " Health check... " + if curl -s "$milvus_url/healthz" >/dev/null 2>&1; then + log_pass "Milvus is responding" + else + log_fail "Milvus health endpoint not reachable" + return 1 + fi + + # Test metrics endpoint + echo -n " Metrics check... " + if curl -s "$milvus_url/metrics" >/dev/null 2>&1; then + log_pass "Milvus metrics available" + else + log_skip "Milvus metrics endpoint (non-critical)" + fi + + # Test REST API version + echo -n " API version check... " + if curl -s "$milvus_url/v1/version" >/dev/null 2>&1; then + log_pass "Milvus REST API responding" + else + log_fail "Milvus REST API not responding" + fi +} + +test_postgres() { + echo "Checking PostgreSQL at $POSTGRES_HOST:$POSTGRES_PORT" + + # Test basic connectivity + echo -n " Connection test... " + if nc -z "$POSTGRES_HOST" "$POSTGRES_PORT" 2>/dev/null; then + log_pass "PostgreSQL port is open" + else + log_fail "Cannot connect to PostgreSQL port" + return 1 + fi + + # Test database query (if inside docker network) + echo -n " Database query test... " + if command -v docker &> /dev/null; then + if docker-compose -f "$PROJECT_ROOT/docker-compose.yml" exec postgres \ + psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT COUNT(*) FROM knowledge_base_updates;" \ + >/dev/null 2>&1; then + log_pass "PostgreSQL database accessible and configured" + else + log_fail "PostgreSQL query failed - might be authentication issue" + fi + else + log_skip "PostgreSQL query test (Docker not available)" + fi + + # Check for required tables + echo -n " Schema validation... " + if docker-compose -f "$PROJECT_ROOT/docker-compose.yml" exec postgres \ + psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c \ + "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='knowledge_base_updates');" \ + 2>/dev/null | grep -q "t"; then + log_pass "Required tables exist" + else + log_skip "Schema validation (might not be initialized)" + fi +} + +test_freescout() { + echo "Checking Freescout at $FREESCOUT_HOST" + + # Test basic connectivity + echo -n " Web server check... " + if curl -s -I "$FREESCOUT_HOST/login" 2>/dev/null | grep -q "200\|301\|302"; then + log_pass "Freescout web interface responding" + else + log_fail "Freescout web interface not responding" + return 1 + fi + + # Test API health (if API key available) + if [ -n "$FREESCOUT_API_KEY" ]; then + echo -n " API authentication test... " + local response=$(curl -s -w '%{http_code}' \ + -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + "$FREESCOUT_HOST/api/v1/mailboxes" -o /tmp/freescout_api_response.txt) + + if [ "$response" = "200" ]; then + log_pass "Freescout API authenticated" + local mailbox_count=$(jq '.mailboxes | length' /tmp/freescout_api_response.txt 2>/dev/null) + log_info "Found $mailbox_count mailbox(es)" + else + log_fail "Freescout API authentication failed (HTTP $response)" + fi + else + log_skip "Freescout API authentication test (FREESCOUT_API_KEY not set)" + fi + + # Test custom fields + if [ -n "$FREESCOUT_API_KEY" ]; then + echo -n " Custom fields check... " + if curl -s -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + "$FREESCOUT_HOST/api/v1/custom-fields" 2>/dev/null | \ + grep -q "AI_SUGGESTION"; then + log_pass "AI custom fields configured" + else + log_fail "AI custom fields not found - check Freescout configuration" + fi + fi +} + +test_litellm() { + echo "Checking LiteLLM at $LITELLM_HOST" + + # Test health endpoint + echo -n " Health check... " + if curl -s "$LITELLM_HOST/health" >/dev/null 2>&1; then + log_pass "LiteLLM is responding" + else + log_fail "LiteLLM health endpoint not reachable" + return 1 + fi + + # Test API endpoint + echo -n " API endpoint test... " + local test_payload='{ + "model": "gpt-3.5-turbo", + "messages": [{"role": "user", "content": "test"}], + "max_tokens": 10 + }' + + if curl -s -X POST "$LITELLM_HOST/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -d "$test_payload" >/dev/null 2>&1; then + log_pass "LiteLLM chat API responding" + else + log_fail "LiteLLM chat API not responding" + fi + + # Test model availability + if [ -n "$LITELLM_API_KEY" ]; then + echo -n " Model availability check... " + if curl -s -X GET "$LITELLM_HOST/v1/models" \ + -H "Authorization: Bearer $LITELLM_API_KEY" 2>/dev/null | \ + grep -q "gpt"; then + log_pass "Models available" + else + log_skip "Model availability (non-critical)" + fi + fi +} + +test_n8n() { + echo "Checking n8n at $N8N_HOST:$N8N_PORT" + + local n8n_url="$N8N_HOST:$N8N_PORT" + + # Test REST API + echo -n " REST API health... " + if curl -s "$n8n_url/healthz" >/dev/null 2>&1 || \ + curl -s "$n8n_url/health" >/dev/null 2>&1; then + log_pass "n8n REST API responding" + else + log_fail "n8n REST API not responding (might be behind reverse proxy)" + fi + + # Test web interface + echo -n " Web interface check... " + if curl -s "$n8n_url/" >/dev/null 2>&1; then + log_pass "n8n web interface accessible" + else + log_skip "n8n web interface check (might require authentication)" + fi + + # Test workflows endpoint (if authenticated) + if [ -n "$N8N_AUTH_TOKEN" ]; then + echo -n " Workflow list test... " + if curl -s -H "Authorization: Bearer $N8N_AUTH_TOKEN" \ + "$n8n_url/api/v1/workflows" >/dev/null 2>&1; then + log_pass "n8n workflows accessible" + else + log_fail "Cannot access n8n workflows" + fi + fi +} + +test_docker_compose() { + if ! command -v docker-compose &> /dev/null; then + log_skip "Docker Compose not available" + return + fi + + echo "Checking Docker Compose services..." + + # Get list of running services + local services=$(docker-compose -f "$PROJECT_ROOT/docker-compose.yml" ps --services 2>/dev/null) + + if [ -z "$services" ]; then + log_fail "No Docker Compose services found" + return 1 + fi + + # Check each service + while read -r service; do + echo -n " Service: $service... " + if docker-compose -f "$PROJECT_ROOT/docker-compose.yml" ps "$service" 2>/dev/null | \ + grep -q "Up"; then + log_pass "$service is running" + else + log_fail "$service is not running" + fi + done <<< "$services" + + # Check resource usage + echo "" + echo -n " Container resource check... " + if docker-compose -f "$PROJECT_ROOT/docker-compose.yml" stats --no-stream 2>/dev/null | \ + tail -n +2 | wc -l | grep -qv "0"; then + log_pass "Docker containers have allocated resources" + else + log_skip "Could not read container stats" + fi +} + +############################################################################### +# Summary Report +############################################################################### + +print_summary() { + local total=$((PASSED + FAILED + SKIPPED)) + local status="PASSED" + + if [ $FAILED -gt 0 ]; then + status="FAILED" + fi + + echo "" + echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" + echo -e "${BLUE}║ Test Summary Report ║${NC}" + echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}" + echo "" + echo -e " Total Tests: $total" + echo -e " ${GREEN}Passed:${NC} $PASSED" + if [ $FAILED -gt 0 ]; then + echo -e " ${RED}Failed:${NC} $FAILED" + fi + if [ $SKIPPED -gt 0 ]; then + echo -e " ${YELLOW}Skipped:${NC} $SKIPPED" + fi + echo "" + + if [ $FAILED -eq 0 ]; then + echo -e "${GREEN}✓ All critical services are running and accessible!${NC}" + echo -e " You can proceed with E2E testing." + echo "" + return 0 + else + echo -e "${RED}✗ Some services are not responding!${NC}" + echo -e " Please check the failures above and ensure all services are running." + echo "" + return 1 + fi +} + +############################################################################### +# Usage Information +############################################################################### + +show_help() { + cat << EOF +Usage: $0 [OPTIONS] + +E2E Test Collection - Service Health & Connectivity Checks + +Options: + -h, --help Show this help message + -v, --verbose Enable verbose output + -m, --milvus HOST:PORT Override Milvus address (default: 127.0.0.1:19530) + -p, --postgres HOST:PORT Override PostgreSQL address (default: 127.0.0.1:5432) + -f, --freescout URL Override Freescout URL + -l, --litellm URL Override LiteLLM URL + -n, --n8n URL Override n8n URL + +Environment Variables: + FREESCOUT_API_KEY Freescout API authentication token + LITELLM_API_KEY LiteLLM API authentication token + N8N_AUTH_TOKEN n8n authentication token + POSTGRES_USER PostgreSQL username (default: kb_user) + POSTGRES_DB PostgreSQL database name (default: n8n_kb) + +Examples: + # Run basic health checks with defaults + $0 + + # Override Milvus host + $0 --milvus 192.168.1.100:19530 + + # With API keys + FREESCOUT_API_KEY=token123 $0 + +EOF +} + +############################################################################### +# Main Execution +############################################################################### + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -v|--verbose) + set -x + shift + ;; + -m|--milvus) + MILVUS_HOST="${2%:*}" + MILVUS_PORT="${2##*:}" + shift 2 + ;; + -p|--postgres) + POSTGRES_HOST="${2%:*}" + POSTGRES_PORT="${2##*:}" + shift 2 + ;; + -f|--freescout) + FREESCOUT_HOST="$2" + shift 2 + ;; + -l|--litellm) + LITELLM_HOST="$2" + shift 2 + ;; + -n|--n8n) + N8N_HOST="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac +done + +# Run main test suite +main + +# Exit with appropriate code +if [ $FAILED -gt 0 ]; then + exit 1 +else + exit 0 +fi diff --git a/tests/e2e-test-scenario.md b/tests/e2e-test-scenario.md new file mode 100644 index 0000000..46be7c4 --- /dev/null +++ b/tests/e2e-test-scenario.md @@ -0,0 +1,527 @@ +# E2E Test: Full AI Support Automation Workflow + +## System Setup Prerequisites + +### Services Health Check +- **Milvus Vector DB**: Accessible on port 9091 +- **PostgreSQL**: Running with KB schema and audit tables +- **n8n**: Workflow engine operational +- **Freescout**: Help desk system with custom fields configured +- **LiteLLM**: LLM proxy service accessible +- **Baramundi**: Remote execution system (optional) + +### Test Environment Variables +```bash +export FREESCOUT_API_KEY="your-api-key" +export LITELLM_API_KEY="your-api-key" +export N8N_AUTH_TOKEN="your-auth-token" +export POSTGRES_PASSWORD="your-password" +``` + +--- + +## E2E Test Scenario: Complete Workflow + +### Test Case ID: E2E-001 +**Objective**: Full workflow from ticket creation through knowledge base update + +--- + +## 1. Setup Phase + +### Pre-Test Validation +```bash +# Verify all services are running +./tests/curl-test-collection.sh + +# Clear test data from previous runs (optional) +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "DELETE FROM knowledge_base_updates WHERE created_at > NOW() - INTERVAL '1 hour';" + +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "DELETE FROM ticket_audit WHERE ticket_id LIKE 'TEST_%';" +``` + +### Test Data Preparation +- Create test customer: `test@example.com` in Freescout (if not exists) +- Verify custom fields exist in Freescout: + - `AI_SUGGESTION` (text field) + - `AI_SUGGESTION_STATUS` (enum: PENDING, APPROVED, REJECTED, EXECUTED) + - `AI_CONFIDENCE` (number field) + +--- + +## 2. Workflow A: Ticket Analysis & AI Suggestion + +### Test Step 1: Create Test Ticket in Freescout + +**Action**: Create new ticket via Freescout UI or API +```bash +curl -X POST https://ekshelpdesk.fft-it.de/api/v1/mailboxes/1/conversations \ + -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "subject": "TEST_E2E_001: Drucker funktioniert nicht", + "body": "Jeder Druck-Befehl wird abgelehnt. Fehlercode 5. Bitte schnell lösen!", + "customer_email": "test@example.com", + "mailbox_id": 1 + }' +``` + +**Expected Outcome**: +- HTTP Status: 201 (Created) +- Response contains `conversation_id` +- Ticket visible in Freescout dashboard + +**Validation**: +```bash +# Get the created ticket ID from response +TICKET_ID="" +echo "Created Ticket: $TICKET_ID" +``` + +--- + +### Test Step 2: Wait for Workflow A Cycle (5 minutes) + +**Process Flow**: +1. n8n Workflow A triggers every 5 minutes +2. Fetches new tickets from Freescout +3. Sends ticket text to LiteLLM for analysis +4. Updates custom fields with AI suggestion + +**Wait Time**: 5 minutes (plus 1 min buffer = 6 minutes total) + +**During Wait - Monitor Logs**: +```bash +# Monitor n8n logs for workflow execution +docker-compose logs -f n8n | grep -i "workflow_a\|ticket\|analysis" + +# Check PostgreSQL audit log +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "SELECT * FROM ticket_audit WHERE ticket_id = '$TICKET_ID' ORDER BY created_at DESC;" +``` + +**Expected n8n Logs**: +- "Processing ticket: TEST_E2E_001" +- "Calling LiteLLM API for analysis" +- "Analysis complete: category=Hardware, confidence=0.92" +- "Updated custom fields in Freescout" + +--- + +### Test Step 3: Verify AI Suggestion in Freescout + +**Action**: Check ticket custom fields +```bash +curl -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID +``` + +**Expected Response Fields**: +```json +{ + "conversation": { + "id": "TICKET_ID", + "subject": "TEST_E2E_001: Drucker funktioniert nicht", + "custom_fields": { + "AI_SUGGESTION": "Hardware Problem: Drucker-Treiber fehlerhaft oder beschädigt. Empfohlene Lösung: 1) Drucker neustarten, 2) Treiber neu installieren", + "AI_SUGGESTION_STATUS": "PENDING", + "AI_CONFIDENCE": 0.92 + } + } +} +``` + +**Validation Checklist**: +- ✅ `AI_SUGGESTION` is not empty +- ✅ `AI_SUGGESTION_STATUS` = "PENDING" +- ✅ `AI_CONFIDENCE` between 0.7 and 1.0 +- ✅ Suggestion text contains problem category and solution + +**Alternative: Manual Check in UI**: +- Open ticket in Freescout +- Scroll to custom fields section +- Verify all three fields populated with expected values + +--- + +## 3. Workflow B: Approval & Execution + +### Test Step 4: Approve AI Suggestion + +**Action**: Update custom field via API +```bash +curl -X PUT https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID \ + -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "custom_fields": { + "AI_SUGGESTION_STATUS": "APPROVED" + } + }' +``` + +**Expected Outcome**: +- HTTP Status: 200 (OK) +- Custom field updated in Freescout + +**Validation**: +```bash +# Verify field was updated +curl -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID | \ + jq '.conversation.custom_fields.AI_SUGGESTION_STATUS' +# Expected output: "APPROVED" +``` + +--- + +### Test Step 5: Wait for Workflow B Cycle (2 minutes) + +**Process Flow**: +1. n8n Workflow B triggers every 2 minutes +2. Fetches tickets with `AI_SUGGESTION_STATUS = APPROVED` +3. Executes action based on category: + - **Hardware/Remote**: Create Baramundi job + - **Software/Config**: Send support email + - **Knowledge**: Update KB directly +4. Updates status to `EXECUTED` + +**Wait Time**: 2 minutes (plus 1 min buffer = 3 minutes total) + +**During Wait - Monitor Logs**: +```bash +# Check n8n workflow B execution +docker-compose logs -f n8n | grep -i "workflow_b\|approved\|executing" + +# Check for Baramundi job creation (if applicable) +curl https://baramundi-api.example.com/jobs \ + -H "Authorization: Bearer $BARAMUNDI_TOKEN" | \ + jq '.jobs[] | select(.ticket_id == "'"$TICKET_ID"'")' +``` + +--- + +### Test Step 6: Verify Execution Status + +**Action**: Check ticket status +```bash +curl -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID +``` + +**Expected Response**: +```json +{ + "conversation": { + "custom_fields": { + "AI_SUGGESTION_STATUS": "EXECUTED", + "EXECUTION_TIMESTAMP": "2026-03-16T14:35:00Z" + } + } +} +``` + +**Validation Checklist**: +- ✅ `AI_SUGGESTION_STATUS` = "EXECUTED" +- ✅ `EXECUTION_TIMESTAMP` is recent +- ✅ No errors in n8n logs + +--- + +## 4. Workflow C: Knowledge Base Update + +### Test Step 7: Wait for Workflow C Cycle (1 minute) + +**Process Flow**: +1. n8n Workflow C triggers every 1 minute +2. Fetches executed tickets +3. Extracts: Problem, Solution, Category +4. Inserts into PostgreSQL `knowledge_base_updates` table +5. Triggers Milvus vector DB embedding and indexing + +**Wait Time**: 1 minute (plus 1 min buffer = 2 minutes total) + +--- + +### Test Step 8: Verify Knowledge Base Entry + +**Action 1**: Check PostgreSQL insert +```bash +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "SELECT id, ticket_id, problem, solution, category, frequency, created_at + FROM knowledge_base_updates + WHERE ticket_id = '$TICKET_ID' + ORDER BY created_at DESC LIMIT 1;" +``` + +**Expected Output**: +``` + id | ticket_id | problem | solution | category | frequency | created_at +----+-----------+---------+----------+----------+-----------+--------------------- + 42 | TEST... | Drucker | Treiber | Hardware | 1 | 2026-03-16 14:35:00 +``` + +**Action 2**: Check Milvus vector DB +```bash +# Query Milvus for the new KB entry +curl -X POST http://127.0.0.1:19530/v1/search \ + -H "Content-Type: application/json" \ + -d '{ + "collection_name": "knowledge_base", + "vectors": [ + "vector_embedding_of_problem_text" + ], + "top_k": 10, + "output_fields": ["id", "ticket_id", "problem", "solution", "similarity"] + }' | jq '.' +``` + +**Expected Response**: +```json +{ + "results": [ + { + "id": 42, + "ticket_id": "TEST_E2E_001", + "problem": "Drucker funktioniert nicht - Fehlercode 5", + "solution": "Drucker neustarten und Treiber neu installieren", + "similarity": 0.98 + } + ] +} +``` + +**Validation Checklist**: +- ✅ Record exists in `knowledge_base_updates` table +- ✅ Fields populated: problem, solution, category, frequency +- ✅ Record appears in Milvus search results +- ✅ Similarity score >= 0.95 for exact match + +--- + +## 5. Vector DB Search Test + +### Test Step 9: Search Similar Problem + +**Objective**: Test that similar problems can be found via vector similarity + +**Action**: Query Milvus with similar but different text +```bash +# Prepare embedding for similar problem +curl -X POST http://127.0.0.1:19530/v1/search \ + -H "Content-Type: application/json" \ + -d '{ + "collection_name": "knowledge_base", + "vectors": [ + "vector_for_query: Druckerprobleme beim Ausdrucken mit Fehler" + ], + "top_k": 5, + "output_fields": ["id", "ticket_id", "problem", "solution", "category"] + }' +``` + +**Expected Outcome**: +- Top result is our test KB entry +- Similarity score > 0.85 +- Same category returned (Hardware) + +**Expected Result**: +```json +{ + "results": [ + { + "rank": 1, + "similarity": 0.91, + "ticket_id": "TEST_E2E_001", + "problem": "Drucker funktioniert nicht - Fehlercode 5", + "solution": "Drucker neustarten und Treiber neu installieren", + "category": "Hardware" + }, + { + "rank": 2, + "similarity": 0.78, + "ticket_id": "OTHER_TICKET", + "problem": "Netzwerkdrucker nicht erreichbar", + "solution": "IP-Konfiguration prüfen" + } + ] +} +``` + +**Validation**: +- ✅ Test entry ranks in top 3 +- ✅ Similarity > 0.85 +- ✅ Relevant results returned + +--- + +## 6. Failure Scenarios + +### Scenario F1: Service Unavailable + +**Test**: What happens if LiteLLM is unreachable? + +**Action**: Stop LiteLLM service +```bash +docker-compose stop litellm +``` + +**Expected Behavior**: +- n8n Workflow A fails gracefully +- Error logged in PostgreSQL error_log table +- Ticket status remains PENDING +- After service recovery, retry happens automatically + +**Verify**: +```bash +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "SELECT error_message, retry_count FROM error_log WHERE ticket_id = '$TICKET_ID';" +``` + +**Cleanup**: Restart service +```bash +docker-compose up -d litellm +``` + +--- + +### Scenario F2: Invalid Custom Field + +**Test**: What if custom field value is invalid? + +**Action**: Set invalid status value +```bash +curl -X PUT https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID \ + -H "Authorization: Bearer $FREESCOUT_API_KEY" \ + -d '{ + "custom_fields": { + "AI_SUGGESTION_STATUS": "INVALID_VALUE" + } + }' +``` + +**Expected Behavior**: +- Workflow B ignores ticket +- Error logged +- Manual intervention required + +--- + +## 7. Test Cleanup + +### Post-Test Actions +```bash +# Archive test data +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "UPDATE ticket_audit + SET archived = true + WHERE ticket_id LIKE 'TEST_%';" + +# Optional: Delete test ticket from Freescout +curl -X DELETE https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID \ + -H "Authorization: Bearer $FREESCOUT_API_KEY" + +# Verify cleanup +docker-compose exec postgres psql -U kb_user -d n8n_kb -c \ + "SELECT COUNT(*) as active_test_tickets FROM ticket_audit + WHERE ticket_id LIKE 'TEST_%' AND archived = false;" +``` + +--- + +## 8. Test Metrics & Success Criteria + +### Timing Metrics +| Phase | Expected Duration | Tolerance | +|-------|------------------|-----------| +| Setup | - | - | +| Workflow A (Analysis) | 5 min | ±1 min | +| Workflow B (Execution) | 2 min | ±30 sec | +| Workflow C (KB Update) | 1 min | ±30 sec | +| **Total End-to-End** | **~8 min** | **±2 min** | + +### Success Criteria +- ✅ All services respond to health checks +- ✅ Ticket created successfully in Freescout +- ✅ AI analysis completes within 5 min +- ✅ Confidence score >= 0.7 +- ✅ Approval triggers execution within 2 min +- ✅ KB entry created in PostgreSQL +- ✅ KB entry indexed in Milvus +- ✅ Vector search returns relevant results (similarity > 0.85) +- ✅ All audit logs recorded correctly +- ✅ No unhandled errors in logs + +### Performance Targets +- API response time: < 2 seconds +- LiteLLM inference: < 10 seconds +- Milvus embedding + indexing: < 5 seconds +- PostgreSQL inserts: < 1 second + +--- + +## 9. Test Execution Checklist + +```bash +# Start here: +[ ] Verify all services running: ./tests/curl-test-collection.sh +[ ] Create test ticket (capture TICKET_ID) +[ ] Wait 6 minutes for Workflow A +[ ] Verify AI_SUGGESTION populated +[ ] Approve ticket (update AI_SUGGESTION_STATUS) +[ ] Wait 3 minutes for Workflow B +[ ] Verify AI_SUGGESTION_STATUS = EXECUTED +[ ] Wait 2 minutes for Workflow C +[ ] Verify PostgreSQL kb entry exists +[ ] Verify Milvus vector search works +[ ] Review all audit logs +[ ] Clean up test data +[ ] Document any issues found +``` + +--- + +## 10. Troubleshooting Guide + +### Issue: AI_SUGGESTION not populated after 5 min + +**Check**: +1. n8n logs: `docker-compose logs n8n | grep -i error` +2. Freescout API connectivity: `curl https://ekshelpdesk.fft-it.de/healthz` +3. LiteLLM service: `curl http://llm.eks-ai.apps.asgard.eks-lnx.fft-it.de/health` +4. Custom field exists: Check Freescout admin panel + +### Issue: Workflow B not executing + +**Check**: +1. Ticket status field correct: `curl -H "Auth: Bearer $KEY" https://ekshelpdesk.fft-it.de/api/v1/conversations/$TICKET_ID | jq '.custom_fields.AI_SUGGESTION_STATUS'` +2. n8n Workflow B enabled: Check n8n UI +3. Error logs: `docker-compose exec postgres psql -U kb_user -d n8n_kb -c "SELECT * FROM error_log ORDER BY created_at DESC LIMIT 5;"` + +### Issue: Milvus search returns no results + +**Check**: +1. Milvus running: `curl http://127.0.0.1:19530/v1/healthz` +2. KB collection exists: See Milvus documentation for collection listing +3. Vector embedding generated: Check PostgreSQL `knowledge_base_updates.vector_embedding` + +--- + +## Appendix A: API Endpoints Reference + +| Service | Endpoint | Purpose | +|---------|----------|---------| +| Freescout | `https://ekshelpdesk.fft-it.de/api/v1/conversations` | Ticket management | +| LiteLLM | `http://llm.eks-ai.apps.asgard.eks-lnx.fft-it.de/v1/chat/completions` | AI analysis | +| Milvus | `http://127.0.0.1:19530/v1/search` | Vector DB search | +| PostgreSQL | `localhost:5432` | Data persistence | +| n8n | `http://localhost:5678` | Workflow orchestration | + +--- + +**Document Version**: 1.0 +**Last Updated**: 2026-03-16 +**Author**: QA/Testing Agent +**Status**: Ready for Testing