| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- // v2.3 — project-addressing parser tests.
- //
- // Locks in the edge-case semantics. Anything that relies on the parser
- // (gRPC handlers, mirror routing, client) builds on these guarantees.
- #include <cstdlib>
- #include <iostream>
- #include <stdexcept>
- #include "project_addressing.hpp"
- using smartbotic::database::kDefaultProject;
- using smartbotic::database::ProjectCollection;
- using smartbotic::database::isValidProjectName;
- using smartbotic::database::parseProjectCollection;
- namespace {
- int g_pass = 0;
- int g_fail = 0;
- void check(bool cond, const char* msg) {
- if (cond) ++g_pass;
- else { ++g_fail; std::cerr << "FAIL: " << msg << "\n"; }
- }
- template <typename Fn>
- void check_throws(Fn fn, const char* msg) {
- try {
- fn();
- ++g_fail;
- std::cerr << "FAIL (did not throw): " << msg << "\n";
- } catch (const std::invalid_argument&) {
- ++g_pass;
- } catch (...) {
- ++g_fail;
- std::cerr << "FAIL (wrong exception type): " << msg << "\n";
- }
- }
- void test_unqualified_resolves_to_default() {
- auto r = parseProjectCollection("users");
- check(r.project == kDefaultProject, "unqualified: project == default");
- check(r.collection == "users", "unqualified: collection preserved");
- }
- void test_qualified_splits_on_separator() {
- auto r = parseProjectCollection("my_app:users");
- check(r.project == "my_app", "qualified: project parsed");
- check(r.collection == "users", "qualified: collection parsed");
- }
- void test_long_collection_name() {
- auto r = parseProjectCollection("my_app:some_long_collection_name");
- check(r.collection == "some_long_collection_name",
- "qualified: long collection preserved");
- }
- void test_empty_input_throws() {
- check_throws([] { parseProjectCollection(""); },
- "empty input must throw");
- }
- void test_leading_colon_throws() {
- check_throws([] { parseProjectCollection(":users"); },
- "leading ':' must throw (empty project)");
- }
- void test_trailing_colon_throws() {
- check_throws([] { parseProjectCollection("my_app:"); },
- "trailing ':' must throw (empty collection)");
- }
- void test_double_separator_throws() {
- check_throws([] { parseProjectCollection("a:b:c"); },
- "double ':' must throw (ambiguous)");
- }
- void test_just_colon_throws() {
- check_throws([] { parseProjectCollection(":"); },
- "':' alone must throw");
- }
- void test_valid_project_names() {
- check(isValidProjectName("default"), "'default' is valid");
- check(isValidProjectName("my_app"), "'my_app' is valid");
- check(isValidProjectName("App1"), "'App1' is valid");
- check(isValidProjectName("a-b-c"), "'a-b-c' is valid (dash allowed)");
- check(isValidProjectName("_starts_underscore") == false,
- "'_-prefix is reserved (system)");
- check(isValidProjectName("") == false, "empty is invalid");
- check(isValidProjectName("9starts_digit") == false,
- "digit-start is invalid");
- check(isValidProjectName("has:colon") == false,
- "':' inside name is invalid");
- check(isValidProjectName("has/slash") == false,
- "'/' inside name is invalid (filesystem safety)");
- check(isValidProjectName("has.dot") == false,
- "'.' inside name is invalid");
- check(isValidProjectName("has space") == false,
- "whitespace inside name is invalid");
- std::string too_long(64, 'a');
- check(isValidProjectName(too_long) == false,
- "64+ chars exceeds kMaxProjectNameLen");
- std::string max_len(63, 'a');
- check(isValidProjectName(max_len), "63 chars is the boundary");
- }
- } // namespace
- int main() {
- test_unqualified_resolves_to_default();
- test_qualified_splits_on_separator();
- test_long_collection_name();
- test_empty_input_throws();
- test_leading_colon_throws();
- test_trailing_colon_throws();
- test_double_separator_throws();
- test_just_colon_throws();
- test_valid_project_names();
- std::cout << "project_addressing: " << g_pass << " passed, "
- << g_fail << " failed\n";
- return g_fail == 0 ? 0 : 1;
- }
|