344 lines
15 KiB
C++
344 lines
15 KiB
C++
// CLI Error Hints tests
|
|
// Catch2 migration from GTest (yams-3s4 / yams-cli)
|
|
//
|
|
// Tests for error_hints.h - Pattern matching and hint generation for CLI errors.
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
|
|
#include <yams/cli/error_hints.h>
|
|
#include <yams/core/types.h>
|
|
|
|
using namespace yams;
|
|
using namespace yams::cli;
|
|
|
|
// ============================================================================
|
|
// getErrorHint() - Pattern-based matching tests
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - FTS5 error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "FTS5 tokenizer failed");
|
|
CHECK(hint.hint == "Full-text search index may be corrupted");
|
|
CHECK(hint.command == "yams doctor --fix");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Tokenize error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "failed to tokenize query");
|
|
CHECK(hint.hint == "Full-text search index may be corrupted");
|
|
CHECK(hint.command == "yams doctor --fix");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - FTS table error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "no such table: fts_content");
|
|
CHECK(hint.hint == "Full-text search index may be corrupted");
|
|
CHECK(hint.command == "yams doctor --fix");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Embedding error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::InternalError, "embedding generation failed");
|
|
CHECK(hint.hint == "Embedding model or vector index issue detected");
|
|
CHECK(hint.command == "yams doctor --check-embeddings");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Vector error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::InternalError, "vector index corrupted");
|
|
CHECK(hint.hint == "Embedding model or vector index issue detected");
|
|
CHECK(hint.command == "yams doctor --check-embeddings");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Dimension mismatch pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint =
|
|
getErrorHint(ErrorCode::InvalidArgument, "dimension mismatch: expected 384, got 768");
|
|
CHECK(hint.hint == "Embedding model or vector index issue detected");
|
|
CHECK(hint.command == "yams doctor --check-embeddings");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Daemon error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::NetworkError, "daemon not responding");
|
|
CHECK(hint.hint == "Daemon connection issue detected");
|
|
CHECK(hint.command == "yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Socket error pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::NetworkError, "socket connection failed");
|
|
CHECK(hint.hint == "Daemon connection issue detected");
|
|
CHECK(hint.command == "yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Connection refused pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::NetworkError, "connection refused to /tmp/yams.sock");
|
|
CHECK(hint.hint == "Daemon connection issue detected");
|
|
CHECK(hint.command == "yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Unique constraint pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "UNIQUE constraint failed: documents.hash");
|
|
CHECK(hint.hint == "Database integrity issue detected");
|
|
CHECK(hint.command == "yams doctor --fix");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - No such table pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "no such table: documents");
|
|
CHECK(hint.hint == "Database schema may need migration");
|
|
CHECK(hint.command == "yams migrate");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Database locked pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "database is locked");
|
|
CHECK(hint.hint == "Database is locked by another process");
|
|
CHECK(hint.command == "yams daemon stop && yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - SQLite busy pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "SQLITE_BUSY: database locked");
|
|
CHECK(hint.hint == "Database is locked by another process");
|
|
CHECK(hint.command == "yams daemon stop && yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Permission denied pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::IOError, "Permission denied: /var/yams/data");
|
|
CHECK(hint.hint == "Check file/directory permissions");
|
|
CHECK(hint.command.empty());
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Model not found pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::FileNotFound, "model not found: all-MiniLM-L6-v2");
|
|
CHECK(hint.hint == "Embedding model may need to be downloaded");
|
|
CHECK(hint.command == "yams init --download-model");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Not initialized pattern detected", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::InvalidState, "storage not initialized");
|
|
CHECK(hint.hint == "YAMS data directory not initialized");
|
|
CHECK(hint.command == "yams init");
|
|
}
|
|
|
|
// ============================================================================
|
|
// getErrorHint() - Error code fallback tests
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - NotInitialized code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::NotInitialized, "generic error");
|
|
CHECK(hint.hint == "YAMS not initialized");
|
|
CHECK(hint.command == "yams init");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - FileNotFound code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::FileNotFound, "generic error");
|
|
CHECK(hint.hint == "Verify the file path exists and is accessible");
|
|
CHECK(hint.command.empty());
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - PermissionDenied code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::PermissionDenied, "generic error");
|
|
CHECK(hint.hint == "Check file/directory permissions");
|
|
CHECK(hint.command.empty());
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - DatabaseError code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "generic error");
|
|
CHECK(hint.hint == "Run diagnostics to check database health");
|
|
CHECK(hint.command == "yams doctor");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - NetworkError code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::NetworkError, "generic error");
|
|
CHECK(hint.hint == "Check network connectivity and try again");
|
|
CHECK(hint.command.empty());
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Timeout code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::Timeout, "operation timed out");
|
|
CHECK(hint.hint == std::string(kDaemonLoadMessage));
|
|
CHECK(hint.command.empty());
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - InvalidArgument code with command", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::InvalidArgument, "invalid option", "add");
|
|
CHECK(hint.hint == "Check command syntax");
|
|
CHECK(hint.command == "yams add --help");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - InvalidArgument code without command", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::InvalidArgument, "invalid option", "");
|
|
CHECK(hint.hint == "Check command syntax");
|
|
CHECK(hint.command == "yams --help");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - CorruptedData code fallback", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::CorruptedData, "data checksum mismatch");
|
|
CHECK(hint.hint == "Data corruption detected; run repair");
|
|
CHECK(hint.command == "yams doctor --fix");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Unknown code returns empty hint", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::Unknown, "something weird happened");
|
|
CHECK(hint.hint.empty());
|
|
CHECK(hint.command.empty());
|
|
}
|
|
|
|
// ============================================================================
|
|
// formatErrorWithHint() tests
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - Format error includes message", "[cli][error_hints][catch2]") {
|
|
auto formatted = formatErrorWithHint(ErrorCode::NotInitialized, "storage missing");
|
|
CHECK(formatted.find("storage missing") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Format error includes hint emoji", "[cli][error_hints][catch2]") {
|
|
auto formatted = formatErrorWithHint(ErrorCode::NotInitialized, "storage missing");
|
|
CHECK(formatted.find("💡 Hint:") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Format error includes command", "[cli][error_hints][catch2]") {
|
|
auto formatted = formatErrorWithHint(ErrorCode::NotInitialized, "storage missing");
|
|
CHECK(formatted.find("📋 Try:") != std::string::npos);
|
|
CHECK(formatted.find("yams init") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Format error no hint for unknown", "[cli][error_hints][catch2]") {
|
|
auto formatted = formatErrorWithHint(ErrorCode::Unknown, "mystery error");
|
|
CHECK(formatted == "mystery error");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Format error passes command", "[cli][error_hints][catch2]") {
|
|
auto formatted = formatErrorWithHint(ErrorCode::InvalidArgument, "bad input", "search");
|
|
CHECK(formatted.find("yams search --help") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Format error preserves IPC message", "[cli][error_hints][catch2]") {
|
|
auto formatted =
|
|
formatErrorWithHint(ErrorCode::NetworkError, "[ipc:refused] Connection refused");
|
|
CHECK(formatted.find("[ipc:refused]") != std::string::npos);
|
|
CHECK(formatted.find("yams daemon start") != std::string::npos);
|
|
}
|
|
|
|
// ============================================================================
|
|
// shouldSuggestInit() tests
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - Should suggest init for NotInitialized code",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestInit(ErrorCode::NotInitialized, "any message"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest init for not initialized message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestInit(ErrorCode::Unknown, "storage not initialized"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest init for no such table message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestInit(ErrorCode::DatabaseError, "no such table: documents"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest init for database not found message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestInit(ErrorCode::FileNotFound, "database not found"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should not suggest init for unrelated error",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK_FALSE(shouldSuggestInit(ErrorCode::NetworkError, "connection refused"));
|
|
}
|
|
|
|
// ============================================================================
|
|
// shouldSuggestDoctor() tests
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - Should suggest doctor for DatabaseError code",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestDoctor(ErrorCode::DatabaseError, "any message"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest doctor for CorruptedData code",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestDoctor(ErrorCode::CorruptedData, "any message"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest doctor for FTS5 message", "[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestDoctor(ErrorCode::Unknown, "FTS5 error occurred"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest doctor for constraint message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestDoctor(ErrorCode::Unknown, "constraint failed"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest doctor for integrity message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestDoctor(ErrorCode::Unknown, "integrity check failed"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should suggest doctor for embedding message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(shouldSuggestDoctor(ErrorCode::Unknown, "embedding dimension mismatch"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Should not suggest doctor for unrelated error",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK_FALSE(shouldSuggestDoctor(ErrorCode::NetworkError, "connection refused"));
|
|
}
|
|
|
|
// ============================================================================
|
|
// isDaemonConnectionError() tests
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - Is daemon connection error for NetworkError code",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK_FALSE(isDaemonConnectionError(ErrorCode::NetworkError, "any message"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Is daemon connection error for daemon message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(isDaemonConnectionError(ErrorCode::Unknown, "daemon not responding"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Is daemon connection error for socket message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(isDaemonConnectionError(ErrorCode::Unknown, "socket error"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Is daemon connection error for connection refused message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(isDaemonConnectionError(ErrorCode::Unknown, "connection refused"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Is daemon connection error for ECONNREFUSED message",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK(isDaemonConnectionError(ErrorCode::Unknown, "ECONNREFUSED"));
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Is not daemon connection error for unrelated error",
|
|
"[cli][error_hints][catch2]") {
|
|
CHECK_FALSE(isDaemonConnectionError(ErrorCode::DatabaseError, "constraint failed"));
|
|
}
|
|
|
|
// ============================================================================
|
|
// Pattern priority tests - ensure message patterns take precedence over codes
|
|
// ============================================================================
|
|
|
|
TEST_CASE("ErrorHints - Message pattern takes precedence over code", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "FTS5 tokenizer error");
|
|
CHECK(hint.hint == "Full-text search index may be corrupted");
|
|
CHECK(hint.command == "yams doctor --fix");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - Daemon message with database code gives daemon hint",
|
|
"[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::DatabaseError, "daemon connection refused");
|
|
CHECK(hint.hint == "Daemon connection issue detected");
|
|
CHECK(hint.command == "yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - IPC socket missing kind gives daemon start hint",
|
|
"[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::NetworkError, "[ipc:socket_missing] /tmp/yams.sock");
|
|
CHECK(hint.hint == "Daemon IPC socket not found; start the daemon or check socket path");
|
|
CHECK(hint.command == "yams daemon start");
|
|
}
|
|
|
|
TEST_CASE("ErrorHints - IPC timeout kind gives load hint", "[cli][error_hints][catch2]") {
|
|
auto hint = getErrorHint(ErrorCode::Timeout, "[ipc:timeout] connect timed out");
|
|
CHECK(hint.hint == std::string(kDaemonLoadMessage));
|
|
}
|