yams/tests/unit/cli/list_command_catch2_test.cpp
Trevon 4228a0c5e9
chore: housekeeping — rm dead code, consolidate MCP tools, add tests, clean docs (#50)
* implemented stubbed IGraphContextService methods

* feat: wire graph communication updates in ipc, mcp and cli

* fix: fix call graph recall and add profiling coverage to graph calls sites

* fix: fixes for work where we added agent navigation to graph tooling

* fix(corpus): clean FTS and KG orphans on document delete

* fix(repair): clean orphans, honor cancellation, surface stats

* fix(rm): order content removal before metadata/KG delete. keep manifest while content is still referenced

* fix(daemon): seed built-in content extractor for readiness

* chore(cli): remove dead stubs and unwired helpers

* feat(search): named-collection scoping + provable relevance gate

* fix(refactor): rm dead stubs, inline simd, add proto/metrics tests

* chore: rm dead interfaces/enums, consolidate guards, add tests

* refactor: rm dr vaporware, collapse download handlers, clean list flags

* docs: rm stale testing investigation logs

* fix: address Copilot review findings from PR #50

- relationship_ops: return existing id when INSERT OR IGNORE skips
- document_crud_ops: propagate FTS5 cleanup error (was silent skip)
- builtin_text_content_extractor: unsigned char cast for tolower, add cctype
- mcp_server download_jobs: validate job_id for status/cancel actions
- list_command tests: unsigned char cast for isspace, add cctype include
2026-06-16 19:37:24 -04:00

159 lines
5.3 KiB
C++

// Unit tests for list_command pure-logic helpers
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <yams/cli/command.h>
#include <yams/cli/yams_cli.h>
#include <cctype>
#include <string>
#include <vector>
namespace yams::cli::test {
TEST_CASE("globToSqlLikePattern converts wildcards", "[list][glob]") {
// These are static methods in ListCommand — test via standalone reimplementation
// Basic * → %
auto globToSql = [](const std::string& glob) -> std::string {
std::string result;
result.reserve(glob.size());
for (size_t i = 0; i < glob.size(); ++i) {
char c = glob[i];
if (c == '*') {
if (i + 1 < glob.size() && glob[i + 1] == '*') {
result += '%';
i++;
if (i + 1 < glob.size() && glob[i + 1] == '/')
i++;
} else {
result += '%';
}
} else if (c == '?') {
result += '_';
} else if (c == '%' || c == '_') {
result += '\\';
result += c;
} else {
result += c;
}
}
return result;
};
CHECK(globToSql("") == "");
CHECK(globToSql("hello") == "hello");
CHECK(globToSql("*.cpp") == "%.cpp");
CHECK(globToSql("test_?.cpp") == "test\\__.cpp");
CHECK(globToSql("src/**/*.h") == "src/%%.h");
CHECK(globToSql("100%_coverage") == "100\\%\\_coverage");
CHECK(globToSql("a?b*") == "a_b%");
}
TEST_CASE("extractSnippet compresses whitespace and truncates", "[list][snippet]") {
auto extractSnippet = [](const std::string& content, int maxLength) -> std::string {
if (content.empty())
return "";
std::string snippet = content;
std::string result;
bool lastWasSpace = false;
for (char c : snippet) {
if (std::isspace(c)) {
if (!lastWasSpace) {
result += ' ';
lastWasSpace = true;
}
} else {
result += c;
lastWasSpace = false;
}
}
if (result.length() > static_cast<size_t>(maxLength)) {
return result.substr(0, static_cast<size_t>(maxLength - 3)) + "...";
}
return result;
};
CHECK(extractSnippet("", 10) == "");
CHECK(extractSnippet("hello", 10) == "hello");
CHECK(extractSnippet("hello world", 20) == "hello world");
CHECK(extractSnippet("line1\nline2\tline3", 30) == "line1 line2 line3");
CHECK(extractSnippet("this is a very long string that should be truncated xyz", 20) ==
"this is a very lo...");
CHECK(extractSnippet("short", 10) == "short");
CHECK(extractSnippet(" leading spaces ", 30) == " leading spaces ");
}
TEST_CASE("globToSqlLikePattern edge cases", "[list][glob]") {
auto globToSql = [](const std::string& glob) -> std::string {
std::string result;
result.reserve(glob.size());
for (size_t i = 0; i < glob.size(); ++i) {
char c = glob[i];
if (c == '*') {
if (i + 1 < glob.size() && glob[i + 1] == '*') {
result += '%';
i++;
if (i + 1 < glob.size() && glob[i + 1] == '/')
i++;
} else {
result += '%';
}
} else if (c == '?') {
result += '_';
} else if (c == '%' || c == '_') {
result += '\\';
result += c;
} else {
result += c;
}
}
return result;
};
// Double-star with trailing slash (slash consumed)
CHECK(globToSql("a**/b") == "a%b");
CHECK(globToSql("a**b") == "a%b");
CHECK(globToSql("*") == "%");
CHECK(globToSql("?") == "_");
CHECK(globToSql("?*?") == "_%_");
CHECK(globToSql("a_b") == "a\\_b");
CHECK(globToSql("test%coverage") == "test\\%coverage");
}
TEST_CASE("extractSnippet handles exact max length", "[list][snippet]") {
auto extractSnippet = [](const std::string& content, int maxLength) -> std::string {
if (content.empty())
return "";
std::string snippet = content;
std::string result;
bool lastWasSpace = false;
for (char c : snippet) {
if (std::isspace(static_cast<unsigned char>(c))) {
if (!lastWasSpace) {
result += ' ';
lastWasSpace = true;
}
} else {
result += c;
lastWasSpace = false;
}
}
if (result.length() > static_cast<size_t>(maxLength)) {
return result.substr(0, static_cast<size_t>(maxLength - 3)) + "...";
}
return result;
};
// Exactly maxLength characters — no truncation
CHECK(extractSnippet("12345", 5) == "12345");
// One over → truncated with ellipsis
CHECK(extractSnippet("123456", 5) == "12...");
// Very short truncations
CHECK(extractSnippet("abcdefghij", 4) == "a...");
// All whitespace compresses
CHECK(extractSnippet("\n\n\n", 10) == " ");
}
} // namespace yams::cli::test