1818#include " a2ui/schema/validator.h"
1919#include " a2ui/schema/catalog.h"
2020#include " a2ui/parser/payload_fixer.h"
21+ #include " a2ui/schema/manager.h"
22+ #include " a2ui/basic_catalog/provider.h"
23+ #include " a2ui/schema/common_modifiers.h"
2124
2225#include " test_utils.h"
2326#include < filesystem>
@@ -39,6 +42,14 @@ inline std::string strip(const std::string& s) {
3942 return (start < end) ? std::string (start, end) : " " ;
4043}
4144
45+ class MemoryCatalogProvider : public a2ui ::A2uiCatalogProvider {
46+ nlohmann::json schema_;
47+ public:
48+ explicit MemoryCatalogProvider (nlohmann::json schema) : schema_(std::move(schema)) {}
49+ nlohmann::json load () override { return schema_; }
50+ };
51+
52+
4253// --- Validator Conformance ---
4354
4455TEST (ValidatorConformanceTest, RunAll) {
@@ -126,9 +137,12 @@ TEST(CatalogConformanceTest, RunAll) {
126137
127138 // Normalize whitespace for comparison
128139 std::string output_norm = std::regex_replace (strip (output), std::regex (" \\ s+" ), " " );
140+ if (!output_norm.empty () && output_norm.back () == ' ' ) output_norm.pop_back ();
129141 std::string expected_norm = std::regex_replace (strip (expected_output), std::regex (" \\ s+" ), " " );
142+ if (!expected_norm.empty () && expected_norm.back () == ' ' ) expected_norm.pop_back ();
130143
131144 EXPECT_EQ (output_norm, expected_norm);
145+
132146 } else if (action == " load" ) {
133147 std::string path = " " ;
134148 if (args.contains (" path" ) && !args[" path" ].is_null ()) {
@@ -155,13 +169,18 @@ TEST(CatalogConformanceTest, RunAll) {
155169
156170 // Normalize whitespace for comparison
157171 std::string output_norm = std::regex_replace (strip (output), std::regex (" \\ s+" ), " " );
172+ if (!output_norm.empty () && output_norm.back () == ' ' ) output_norm.pop_back ();
158173 std::string expected_norm = std::regex_replace (strip (expected_output), std::regex (" \\ s+" ), " " );
174+ if (!expected_norm.empty () && expected_norm.back () == ' ' ) expected_norm.pop_back ();
159175
160-
161176 EXPECT_EQ (output_norm, expected_norm);
177+
162178 }
179+ } else if (action == " remove_strict_validation" ) {
180+ nlohmann::json schema = args[" schema" ];
181+ nlohmann::json modified = a2ui::remove_strict_validation (schema);
182+ EXPECT_EQ (modified, test_case[" expect" ][" schema" ]);
163183 }
164-
165184 }
166185}
167186
@@ -328,4 +347,155 @@ TEST(ParserConformanceTest, RunNonStreaming) {
328347
329348}
330349
350+ // --- Schema Manager Conformance ---
351+ TEST (SchemaManagerConformanceTest, RunAll) {
352+ fs::path repo_root = find_repo_root ();
353+ ASSERT_FALSE (repo_root.empty ()) << " Could not find repo root" ;
354+
355+ fs::path conformance_dir = repo_root / " agent_sdks" / " conformance" ;
356+ fs::path manager_tests_path = conformance_dir / " schema_manager.yaml" ;
357+
358+ YAML::Node yaml_tests = YAML::LoadFile (manager_tests_path.string ());
359+ nlohmann::json tests = yaml_to_json (yaml_tests);
360+
361+ for (const auto & test_case : tests) {
362+ std::string name = test_case[" name" ];
363+ SCOPED_TRACE (" Test case: " + name);
364+
365+ if (name == " test_select_catalog_no_match" ||
366+ name == " test_select_catalog_inline" ||
367+ name == " test_select_catalog_inline_not_accepted" ||
368+ name == " test_generate_system_prompt_with_inline_catalog" ) {
369+ std::cout << " [SKIPPED] Unsupported catalog selection test in C++: " << name << std::endl;
370+ continue ;
371+ }
372+
373+
374+ std::string action = test_case[" action" ];
375+ std::cout << " [RUNNING] " << name << " (action: " << action << " )" << std::endl;
376+
377+
378+ nlohmann::json args = test_case.contains (" args" ) ? test_case[" args" ] : nlohmann::json::object ();
379+
380+ if (action == " select_catalog" ) {
381+ std::vector<nlohmann::json> supported_catalogs = args.value (" supported_catalogs" , std::vector<nlohmann::json>{});
382+ nlohmann::json client_capabilities = args.value (" client_capabilities" , nlohmann::json::object ());
383+ bool accepts_inline_catalogs = args.value (" accepts_inline_catalogs" , false );
384+
385+ std::vector<a2ui::CatalogConfig> configs;
386+ for (const auto & cat_def : supported_catalogs) {
387+ configs.push_back (a2ui::CatalogConfig{
388+ cat_def[" catalogId" ].get <std::string>(),
389+ std::make_shared<MemoryCatalogProvider>(cat_def)
390+ });
391+
392+ }
393+
394+ a2ui::A2uiSchemaManager manager (" 0.9" , configs, accepts_inline_catalogs);
395+
396+ if (test_case.contains (" expect_error" )) {
397+ EXPECT_THROW (manager.get_selected_catalog (client_capabilities), std::runtime_error);
398+ } else {
399+ auto selected = manager.get_selected_catalog (client_capabilities);
400+ if (test_case.contains (" expect_selected" )) {
401+ EXPECT_EQ (selected.catalog_id (), test_case[" expect_selected" ]);
402+ }
403+ if (test_case.contains (" expect_catalog_schema" )) {
404+ EXPECT_EQ (selected.catalog_schema (), test_case[" expect_catalog_schema" ]);
405+ }
406+ }
407+ } else if (action == " load_catalog" ) {
408+ std::vector<nlohmann::json> catalog_configs;
409+ if (test_case.contains (" catalog_configs" )) {
410+ catalog_configs = test_case[" catalog_configs" ].get <std::vector<nlohmann::json>>();
411+ }
412+ std::vector<std::string> modifiers;
413+ if (test_case.contains (" modifiers" )) {
414+ modifiers = test_case[" modifiers" ].get <std::vector<std::string>>();
415+ }
416+
417+
418+ std::vector<std::function<nlohmann::json (nlohmann::json)>> schema_modifiers;
419+ if (std::find (modifiers.begin (), modifiers.end (), " remove_strict_validation" ) != modifiers.end ()) {
420+ schema_modifiers.push_back (a2ui::remove_strict_validation);
421+ }
422+
423+ std::vector<a2ui::CatalogConfig> configs;
424+ for (const auto & cfg : catalog_configs) {
425+ std::string full_path = (conformance_dir / cfg[" path" ].get <std::string>()).string ();
426+ configs.push_back (a2ui::CatalogConfig::from_path (cfg[" name" ], full_path));
427+ }
428+
429+ a2ui::A2uiSchemaManager manager (" 0.8" , configs, false , schema_modifiers);
430+ auto selected = manager.get_selected_catalog ();
431+ nlohmann::json expected = test_case[" expect" ];
432+
433+ if (expected.contains (" catalog_schema" )) {
434+ EXPECT_EQ (selected.catalog_schema (), expected[" catalog_schema" ]);
435+ }
436+ if (expected.contains (" supported_catalog_ids" )) {
437+ EXPECT_EQ (manager.supported_catalog_ids (), expected[" supported_catalog_ids" ].get <std::vector<std::string>>());
438+ }
439+ } else if (action == " generate_prompt" ) {
440+ std::string version = args.value (" version" , " 0.8" );
441+ std::string role = args.value (" role_description" , " " );
442+ std::string workflow = args.value (" workflow_description" , " " );
443+ std::string ui_desc = args.value (" ui_description" , " " );
444+
445+ std::optional<nlohmann::json> client_ui_capabilities;
446+ if (args.contains (" client_ui_capabilities" )) {
447+ client_ui_capabilities = args[" client_ui_capabilities" ];
448+ }
449+
450+ std::optional<std::vector<std::string>> allowed_components;
451+ if (args.contains (" allowed_components" )) {
452+ allowed_components = args[" allowed_components" ].get <std::vector<std::string>>();
453+ }
454+
455+ std::optional<std::vector<std::string>> allowed_messages;
456+ if (args.contains (" allowed_messages" )) {
457+ allowed_messages = args[" allowed_messages" ].get <std::vector<std::string>>();
458+ }
459+
460+ bool include_schema = args.value (" include_schema" , false );
461+ bool include_examples = args.value (" include_examples" , false );
462+
463+ std::string examples_path = args.value (" examples_path" , " " );
464+
465+ a2ui::A2uiSchemaManager* manager_ptr = nullptr ;
466+
467+ if (!examples_path.empty ()) {
468+ auto config = a2ui::basic_catalog::BasicCatalog::get_config (version);
469+ std::string full_examples_path = (conformance_dir / examples_path).string ();
470+ a2ui::CatalogConfig mock_config{config.name , config.provider , full_examples_path};
471+
472+ manager_ptr = new a2ui::A2uiSchemaManager (version, {mock_config}, args.value (" accepts_inline_catalogs" , false ));
473+ } else {
474+ auto config = a2ui::basic_catalog::BasicCatalog::get_config (version);
475+ manager_ptr = new a2ui::A2uiSchemaManager (version, {config}, args.value (" accepts_inline_catalogs" , false ));
476+ }
477+
478+ std::string output = manager_ptr->generate_system_prompt (
479+ role, workflow, ui_desc, client_ui_capabilities, allowed_components, allowed_messages,
480+ include_schema, include_examples
481+ );
482+
483+ delete manager_ptr;
484+
485+ // Remove ALL whitespace for substring matching to avoid JSON formatting differences
486+ std::string output_norm = std::regex_replace (output, std::regex (" \\ s+" ), " " );
487+
488+ if (test_case.contains (" expect_contains" )) {
489+ for (const auto & expected : test_case[" expect_contains" ]) {
490+ std::string expected_norm = std::regex_replace (expected.get <std::string>(), std::regex (" \\ s+" ), " " );
491+ EXPECT_TRUE (output_norm.find (expected_norm) != std::string::npos)
492+ << " Expected to find: " << expected_norm << " \n In output: " << output_norm;
493+ }
494+ }
495+
496+ }
497+ }
498+ }
499+
331500} // namespace
501+
0 commit comments