#include "example_descriptors.hpp" #include "gallery.hpp" #include "adminive_test.hpp" #include #include #include namespace { using Json = adminive::example::Json; const Json* find_named_node(const Json& value, std::string_view name) { if(value.is_object()) { if(value.contains("name") && value.at("name").is_string() && value.at("name").get() == name) { return &value; } for(const auto& [key, child] : value.items()) { static_cast(key); if(const Json* result = find_named_node(child, name)) { return result; } } } else if(value.is_array()) { for(const auto& child : value) { if(const Json* result = find_named_node(child, name)) { return result; } } } return nullptr; } void assert_flex_items_renderable(const Json& value) { if(value.is_object()) { if(value.contains("type") && value.at("type") == "flex") { ADMINIVE_CHECK(value.contains("items")); for(const auto& item : value.at("items")) { ADMINIVE_CHECK(item.is_object()); ADMINIVE_CHECK(item.contains("type")); } } for(const auto& [key, child] : value.items()) { static_cast(key); assert_flex_items_renderable(child); } return; } if(value.is_array()) { for(const auto& child : value) { assert_flex_items_renderable(child); } } } const Json* find_labeled_node(const Json& value, std::string_view label) { if(value.is_object()) { if(value.contains("label") && value.at("label").is_string() && value.at("label").get() == label) { return &value; } for(const auto& [key, child] : value.items()) { static_cast(key); if(const Json* result = find_labeled_node(child, label)) { return result; } } } else if(value.is_array()) { for(const auto& child : value) { if(const Json* result = find_labeled_node(child, label)) { return result; } } } return nullptr; } const Json* find_titled_node(const Json& value, std::string_view title) { if(value.is_object()) { if(value.contains("title") && value.at("title").is_string() && value.at("title").get() == title) { return &value; } for(const auto& [key, child] : value.items()) { static_cast(key); if(const Json* result = find_titled_node(child, title)) { return result; } } } else if(value.is_array()) { for(const auto& child : value) { if(const Json* result = find_titled_node(child, title)) { return result; } } } return nullptr; } } int main() { using adminive::example::Alert_Config; using adminive::example::Radio_Item_Status; using adminive::example::Radio_Mode; using adminive::example::Radio_Service_Config; using adminive::example::Radio_State; Radio_State state; const auto descriptor = adminive::to_descriptor_json(); ADMINIVE_CHECK(descriptor.at("protocol") == "adminive.resource"); ADMINIVE_CHECK(descriptor.at("protocol_version") == 5); ADMINIVE_CHECK(descriptor.at("name") == "radio_state"); ADMINIVE_CHECK(descriptor.at("fields").size() == 5); const auto& mode_field = descriptor.at("fields").at(0); ADMINIVE_CHECK(mode_field.at("name") == "mode"); ADMINIVE_CHECK(mode_field.at("presentation").at("label") == "运行模式"); ADMINIVE_CHECK(mode_field.at("value_type") == "enum"); ADMINIVE_CHECK(mode_field.at("presentation").at("options").size() == 4); ADMINIVE_CHECK(mode_field.at("presentation").at("options").at(0).at("value") == "receive"); ADMINIVE_CHECK(mode_field.at("presentation").at("options").at(2).at("value") == "duplex"); const auto& port_field = descriptor.at("fields").at(1); ADMINIVE_CHECK(port_field.at("name") == "port"); ADMINIVE_CHECK(port_field.at("presentation").at("label") == "监听端口"); ADMINIVE_CHECK(port_field.at("minimum") == 1); ADMINIVE_CHECK(port_field.at("maximum") == 65535); ADMINIVE_CHECK(descriptor.at("fields").at(2).at("multiple_of") == 2); ADMINIVE_CHECK(!descriptor.contains("list")); const auto table_view = adminive::describe_table_view(); ADMINIVE_CHECK(table_view.columns.size() == 5); ADMINIVE_CHECK(table_view.columns.at(0).field == "mode"); ADMINIVE_CHECK(table_view.columns.at(0).label == "模式"); ADMINIVE_CHECK(table_view.columns.at(0).sortable); ADMINIVE_CHECK(table_view.columns.at(0).filterable); ADMINIVE_CHECK(table_view.columns.at(2).field == "port"); ADMINIVE_CHECK(table_view.columns.at(2).searchable); ADMINIVE_CHECK(table_view.default_order_by == "buffer_count"); const auto table_view_json = adminive::to_view_json(table_view); ADMINIVE_CHECK(table_view_json.at("protocol") == "adminive.view"); ADMINIVE_CHECK(table_view_json.at("kind") == "table"); ADMINIVE_CHECK(table_view_json.at("columns").at(0).at("field") == "mode"); ADMINIVE_CHECK(table_view_json.at("columns").at(0).at("filterable") == true); const auto initial = adminive::to_json(state); ADMINIVE_CHECK(initial.at("mode") == "receive"); ADMINIVE_CHECK(initial.at("port") == 9999); Radio_State assigned; auto result = adminive::assign_json(assigned, Json{{"mode", "transmit"}, {"port", 7000}, {"buffer_count", 6}, {"low_watermark", 10}, {"high_watermark", 20}}); ADMINIVE_CHECK(result.success); ADMINIVE_CHECK(assigned.mode == Radio_Mode::transmit); ADMINIVE_CHECK(assigned.port.value() == 7000); result = adminive::apply_json_patch(assigned, Json{{"mode", "duplex"}, {"port", 7200}}); ADMINIVE_CHECK(result.success); ADMINIVE_CHECK(assigned.mode == Radio_Mode::duplex); ADMINIVE_CHECK(assigned.port.value() == 7200); result = adminive::apply_patch(state, Json{{"buffer_count", 3}}); ADMINIVE_CHECK(!result.success); ADMINIVE_CHECK(state.buffer_count.value() == 2); result = adminive::apply_patch(state, Json{{"low_watermark", 64}, {"high_watermark", 32}}); ADMINIVE_CHECK(!result.success); ADMINIVE_CHECK(state.low_watermark == 0); result = adminive::apply_patch(state, Json{{"missing", 1}}); ADMINIVE_CHECK(!result.success); ADMINIVE_CHECK(result.field_errors.contains("missing")); Radio_State created; result = adminive::apply_frontend_create(created, Json{{"mode", "maintenance"}, {"port", 7300}, {"buffer_count", 10}, {"low_watermark", 12}, {"high_watermark", 24}}); ADMINIVE_CHECK(result.success); ADMINIVE_CHECK(created.mode == Radio_Mode::maintenance); result = adminive::apply_frontend_create(created, Json{{"port", 7300}}); ADMINIVE_CHECK(!result.success); ADMINIVE_CHECK(result.field_errors.contains("mode")); ADMINIVE_CHECK(result.field_errors.contains("buffer_count")); const auto form_view = adminive::edit_form().submit("Confirm Changes"); const auto form = adminive::to_amis_form_schema(state, form_view, "/admin/radio_state"); ADMINIVE_CHECK(form.at("type") == "form"); ADMINIVE_CHECK(form.at("body").size() == 5); ADMINIVE_CHECK(form.at("body").at(0).at("type") == "select"); ADMINIVE_CHECK(form.at("body").at(0).at("options").size() == 4); ADMINIVE_CHECK(form.at("body").at(1).at("type") == "input-number"); ADMINIVE_CHECK(form.at("body").at(2).at("step") == 2); ADMINIVE_CHECK(form.at("actions").at(1).at("label") == "Confirm Changes"); const auto crud = adminive::to_amis_table_schema("/admin/radio_states", table_view); ADMINIVE_CHECK(crud.at("type") == "page"); const auto& crud_body = crud.at("body").at(0); ADMINIVE_CHECK(crud_body.at("type") == "crud"); ADMINIVE_CHECK(crud_body.at("columns").size() == 7); ADMINIVE_CHECK(crud_body.at("columns").at(1).at("name") == "mode"); ADMINIVE_CHECK(crud_body.at("columns").at(1).at("type") == "mapping"); ADMINIVE_CHECK(crud_body.at("columns").at(1).at("map").at("duplex") == "双工"); ADMINIVE_CHECK(crud_body.at("columns").at(1).at("filterable") == true); ADMINIVE_CHECK(crud_body.at("columns").at(3).at("name") == "port"); ADMINIVE_CHECK(crud_body.at("columns").at(3).at("searchable") == true); const auto& create_form = crud_body.at("headerToolbar").at(2).at("dialog").at("body"); ADMINIVE_CHECK(create_form.at("actions").at(1).at("label") == "新增"); const auto status_descriptor = adminive::to_status_descriptor_json(); ADMINIVE_CHECK(status_descriptor.at("protocol") == "adminive.status"); ADMINIVE_CHECK(status_descriptor.at("fields").size() == 3); ADMINIVE_CHECK(status_descriptor.at("fields").at(0).at("name") == "operating_state"); ADMINIVE_CHECK(status_descriptor.at("fields").at(0).at("value_type") == "enum"); ADMINIVE_CHECK(status_descriptor.at("fields").at(0).at("color") == true); const auto first_status = adminive::to_status_json(state.status()); const auto second_status = adminive::to_status_json(state.status()); ADMINIVE_CHECK(first_status.at("operating_state").contains("color")); ADMINIVE_CHECK(!first_status.at("operating_state").at("color").get().empty()); ADMINIVE_CHECK(first_status.at("refresh_sequence").at("value") == 1); ADMINIVE_CHECK(second_status.at("refresh_sequence").at("value") == 2); const auto overview_descriptor = adminive::to_status_descriptor_json(); const auto status_crud = adminive::to_amis_table_schema("/admin/radio_states", table_view, &status_descriptor, 2000, &overview_descriptor, "/admin/status", 2000); ADMINIVE_CHECK(status_crud.at("body").is_array()); ADMINIVE_CHECK(status_crud.at("body").at(0).at("type") == "service"); ADMINIVE_CHECK(status_crud.at("body").at(1).at("columns").size() == 8); const auto& status_column = status_crud.at("body").at(1).at("columns").at(6); ADMINIVE_CHECK(status_column.at("label") == "实时状态"); ADMINIVE_CHECK(status_column.at("popOver").at("trigger") == "click"); ADMINIVE_CHECK(status_column.at("popOver").at("body").at("api").at("url") == "/admin/radio_states/${id}/status"); adminive::example::Http_Collection_Resource collection_resource("/admin/radio_states", std::vector{state}); collection_resource.register_overview_status("/admin/status", 2000); collection_resource.register_status<&Radio_State::status>(3000); const auto registered_schema = collection_resource.amis_schema(); const auto& registered_status_column = registered_schema.at("body").at(1).at("columns").at(6); ADMINIVE_CHECK(registered_status_column.at("popOver").at("body").at("interval") == 3000); Radio_Service_Config service_config; const auto service_descriptor = adminive::to_descriptor_json(); ADMINIVE_CHECK(service_descriptor.at("fields").size() == 7); ADMINIVE_CHECK(service_descriptor.at("fields").at(0).at("value_type") == "string"); ADMINIVE_CHECK(service_descriptor.at("fields").at(1).at("value_type") == "enum"); ADMINIVE_CHECK(service_descriptor.at("fields").at(2).at("value_type") == "integer"); ADMINIVE_CHECK(service_descriptor.at("fields").at(3).at("value_type") == "number"); ADMINIVE_CHECK(service_descriptor.at("fields").at(4).at("value_type") == "object"); ADMINIVE_CHECK(service_descriptor.at("fields").at(4).at("children").size() == 3); ADMINIVE_CHECK(service_descriptor.at("fields").at(5).at("presentation").at("visible_on") == "${$self.mode == 'duplex'}"); ADMINIVE_CHECK(service_descriptor.at("fields").at(6).at("children").at(1).at("presentation").at("control") == "date"); ADMINIVE_CHECK(service_descriptor.at("fields").at(6).at("children").at(2).at("presentation").at("control") == "color"); const auto service_view = adminive::describe_edit_view(); const auto service_view_json = adminive::to_view_json(service_view); ADMINIVE_CHECK(service_view_json.at("body").at("kind") == "tabs"); ADMINIVE_CHECK(service_view_json.at("body").at("children").size() == 3); const auto service_form = adminive::to_amis_form_schema(service_config, service_view, "/admin/config/radio/data"); ADMINIVE_CHECK(service_form.at("body").at(0).at("type") == "tabs"); const Json* profile_control = find_named_node(service_form, "profile_name"); const Json* mode_control = find_named_node(service_form, "mode"); const Json* worker_control = find_named_node(service_form, "worker_count"); const Json* date_control = find_named_node(service_form, "appearance.effective_date"); const Json* color_control = find_named_node(service_form, "appearance.accent_color"); ADMINIVE_CHECK(profile_control && profile_control->at("type") == "input-text"); ADMINIVE_CHECK(mode_control && mode_control->at("type") == "select"); ADMINIVE_CHECK(worker_control && worker_control->at("type") == "input-number"); ADMINIVE_CHECK(date_control && date_control->at("type") == "input-date"); ADMINIVE_CHECK(color_control && color_control->at("type") == "input-color"); ADMINIVE_CHECK(service_form.at("actions").at(1).at("label") == "确认修改"); result = adminive::apply_frontend_patch(service_config, Json{{"profile_name", "Duplex Profile"}, {"mode", "duplex"}, {"worker_count", 8}, {"receive_gain", 2.5}, {"primary_endpoint", Json{{"enabled", true}, {"host", "10.0.0.1"}, {"port", 9200}}}, {"backup_endpoint", Json{{"enabled", true}, {"host", "10.0.0.2"}, {"port", 9201}}}, {"appearance", Json{{"panel_title", "Duplex Radio"}, {"effective_date", "2026-08-07"}, {"accent_color", "#16a34a"}}}}); ADMINIVE_CHECK(result.success); ADMINIVE_CHECK(service_config.mode == Radio_Mode::duplex); ADMINIVE_CHECK(service_config.primary_endpoint.host == "10.0.0.1"); ADMINIVE_CHECK(service_config.appearance.accent_color == "#16a34a"); Alert_Config alert_config; const auto alert_view = adminive::describe_edit_view(); const auto alert_view_json = adminive::to_view_json(alert_view); ADMINIVE_CHECK(alert_view_json.at("body").at("kind") == "vertical"); const auto alert_form = adminive::to_amis_form_schema(alert_config, alert_view, "/admin/config/alerts/data"); ADMINIVE_CHECK(find_named_node(alert_form, "rule_name")); ADMINIVE_CHECK(find_named_node(alert_form, "effective_date")->at("type") == "input-date"); ADMINIVE_CHECK(find_named_node(alert_form, "highlight_color")->at("type") == "input-color"); const Json* webhook_group = find_titled_node(alert_form, "Webhook 端点"); ADMINIVE_CHECK(webhook_group); ADMINIVE_CHECK(webhook_group->at("visibleOn") == "${enabled && channel == 'webhook'}"); adminive::example::Http_Resource service_resource(service_config, "/admin/config/radio"); const auto resource_view = service_resource.view_schema(); ADMINIVE_CHECK(resource_view.at("protocol") == "adminive.view"); ADMINIVE_CHECK(resource_view.at("kind") == "form"); const auto resource_form = service_resource.amis_schema(); ADMINIVE_CHECK(resource_form.at("api").at("method") == "post"); ADMINIVE_CHECK(resource_form.at("api").at("url") == "/admin/config/radio/data"); ADMINIVE_CHECK(resource_form.at("actions").at(1).at("label") == "确认修改"); adminive::example::Component_Gallery gallery; const auto gallery_schema = gallery.amis_schema(); ADMINIVE_CHECK(gallery_schema.at("type") == "panel"); assert_flex_items_renderable(gallery_schema); const Json* automatic_control = find_named_node(gallery_schema, "automatic_text"); const Json* text_control = find_named_node(gallery_schema, "text_value"); const Json* multiline_control = find_named_node(gallery_schema, "multiline_value"); const Json* number_control = find_named_node(gallery_schema, "number_value"); const Json* even_control = find_named_node(gallery_schema, "even_number"); const Json* boolean_control = find_named_node(gallery_schema, "boolean_value"); const Json* select_control = find_named_node(gallery_schema, "select_value"); const Json* date_gallery_control = find_named_node(gallery_schema, "date_value"); const Json* color_gallery_control = find_named_node(gallery_schema, "color_value"); const Json* optional_control = find_named_node(gallery_schema, "optional_text"); const Json* readonly_control = find_named_node(gallery_schema, "read_only_text"); ADMINIVE_CHECK(automatic_control && automatic_control->at("type") == "input-text"); ADMINIVE_CHECK(text_control && text_control->at("type") == "input-text"); ADMINIVE_CHECK(multiline_control && multiline_control->at("type") == "textarea"); ADMINIVE_CHECK(number_control && number_control->at("type") == "input-number"); ADMINIVE_CHECK(number_control->at("min") == 0 && number_control->at("max") == 100); ADMINIVE_CHECK(even_control && even_control->at("type") == "input-number" && even_control->at("step") == 2); ADMINIVE_CHECK(boolean_control && boolean_control->at("type") == "switch"); ADMINIVE_CHECK(select_control && select_control->at("type") == "select"); ADMINIVE_CHECK(date_gallery_control && date_gallery_control->at("type") == "input-date"); ADMINIVE_CHECK(color_gallery_control && color_gallery_control->at("type") == "input-color"); ADMINIVE_CHECK(optional_control && optional_control->at("clearable") == true); ADMINIVE_CHECK(readonly_control && readonly_control->at("type") == "static"); const Json* save_gallery_button = find_labeled_node(gallery_schema, "保存画廊数据"); const Json* create_gallery_button = find_labeled_node(gallery_schema, "新增一行"); const Json* edit_gallery_button = find_labeled_node(gallery_schema, "编辑"); const Json* delete_gallery_button = find_labeled_node(gallery_schema, "删除"); ADMINIVE_CHECK(save_gallery_button && save_gallery_button->at("type") == "submit"); ADMINIVE_CHECK(create_gallery_button && create_gallery_button->at("type") == "button"); ADMINIVE_CHECK(edit_gallery_button && edit_gallery_button->at("actionType") == "dialog"); ADMINIVE_CHECK(delete_gallery_button && delete_gallery_button->at("actionType") == "ajax"); adminive::example::Server_Status server_status; server_status.server_time = "2026-08-06T00:00:00Z"; server_status.poll_sequence = 7; const auto status_json = adminive::to_frontend_json(server_status); ADMINIVE_CHECK(status_json.at("service_state") == "running"); ADMINIVE_CHECK(status_json.at("poll_sequence") == 7); return 0; }