|
| 1 | +// SPDX-FileCopyrightText: 2025 VTT Technical Research Centre of Finland Ltd |
| 2 | +// SPDX-License-Identifier: AGPL-3.0-or-later |
| 3 | + |
| 4 | +#include "openpfc/ui/parameter_validator.hpp" |
| 5 | +#include <catch2/catch_all.hpp> |
| 6 | + |
| 7 | +using namespace pfc::ui; |
| 8 | + |
| 9 | +TEST_CASE("ParameterValidator with required parameter", |
| 10 | + "[parameter_validator][unit]") { |
| 11 | + ParameterValidator validator; |
| 12 | + validator.add_metadata(ParameterMetadata<double>::builder() |
| 13 | + .name("temperature") |
| 14 | + .required(true) |
| 15 | + .range(0.0, 10000.0) |
| 16 | + .build()); |
| 17 | + |
| 18 | + SECTION("Valid configuration with required parameter") { |
| 19 | + nlohmann::json config = {{"temperature", 3300.0}}; |
| 20 | + auto result = validator.validate(config); |
| 21 | + REQUIRE(result.is_valid()); |
| 22 | + } |
| 23 | + |
| 24 | + SECTION("Missing required parameter") { |
| 25 | + nlohmann::json config = {}; |
| 26 | + auto result = validator.validate(config); |
| 27 | + REQUIRE_FALSE(result.is_valid()); |
| 28 | + REQUIRE_FALSE(result.errors.empty()); |
| 29 | + auto errors_str = result.format_errors(); |
| 30 | + REQUIRE(errors_str.find("temperature") != std::string::npos); |
| 31 | + } |
| 32 | + |
| 33 | + SECTION("Required parameter out of range") { |
| 34 | + nlohmann::json config = {{"temperature", -100.0}}; |
| 35 | + auto result = validator.validate(config); |
| 36 | + REQUIRE_FALSE(result.is_valid()); |
| 37 | + auto errors_str = result.format_errors(); |
| 38 | + REQUIRE(errors_str.find("below minimum") != std::string::npos); |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +TEST_CASE("ParameterValidator with optional parameter", |
| 43 | + "[parameter_validator][unit]") { |
| 44 | + ParameterValidator validator; |
| 45 | + validator.add_metadata(ParameterMetadata<double>::builder() |
| 46 | + .name("mobility") |
| 47 | + .required(false) |
| 48 | + .range(0.0, 100.0) |
| 49 | + .build()); |
| 50 | + |
| 51 | + SECTION("Valid configuration without optional parameter") { |
| 52 | + nlohmann::json config = {}; |
| 53 | + auto result = validator.validate(config); |
| 54 | + REQUIRE(result.is_valid()); |
| 55 | + } |
| 56 | + |
| 57 | + SECTION("Valid configuration with optional parameter") { |
| 58 | + nlohmann::json config = {{"mobility", 50.0}}; |
| 59 | + auto result = validator.validate(config); |
| 60 | + REQUIRE(result.is_valid()); |
| 61 | + } |
| 62 | + |
| 63 | + SECTION("Optional parameter out of range") { |
| 64 | + nlohmann::json config = {{"mobility", 150.0}}; |
| 65 | + auto result = validator.validate(config); |
| 66 | + REQUIRE_FALSE(result.is_valid()); |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +TEST_CASE("ParameterValidator with default value", "[parameter_validator][unit]") { |
| 71 | + ParameterValidator validator; |
| 72 | + validator.add_metadata(ParameterMetadata<double>::builder() |
| 73 | + .name("tolerance") |
| 74 | + .default_val(1e-6) |
| 75 | + .range(1e-12, 1e-3) |
| 76 | + .build()); |
| 77 | + |
| 78 | + SECTION("Missing parameter uses default") { |
| 79 | + nlohmann::json config = {}; |
| 80 | + auto result = validator.validate(config); |
| 81 | + REQUIRE(result.is_valid()); |
| 82 | + // Default value handling is internal to validator |
| 83 | + REQUIRE(result.validated_params.find("tolerance") != |
| 84 | + result.validated_params.end()); |
| 85 | + } |
| 86 | + |
| 87 | + SECTION("Explicit value overrides default") { |
| 88 | + nlohmann::json config = {{"tolerance", 1e-8}}; |
| 89 | + auto result = validator.validate(config); |
| 90 | + REQUIRE(result.is_valid()); |
| 91 | + REQUIRE(result.validated_params.find("tolerance") != |
| 92 | + result.validated_params.end()); |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +TEST_CASE("Special floating point values", "[parameter_validator][unit]") { |
| 97 | + ParameterValidator validator; |
| 98 | + validator.add_metadata(ParameterMetadata<double>::builder() |
| 99 | + .name("temperature") |
| 100 | + .range(0.0, 10000.0) |
| 101 | + .build()); |
| 102 | + |
| 103 | + SECTION("NaN is rejected") { |
| 104 | + nlohmann::json config = { |
| 105 | + {"temperature", std::numeric_limits<double>::quiet_NaN()}}; |
| 106 | + auto result = validator.validate(config); |
| 107 | + REQUIRE_FALSE(result.is_valid()); |
| 108 | + } |
| 109 | + |
| 110 | + SECTION("Infinity is rejected") { |
| 111 | + nlohmann::json config = { |
| 112 | + {"temperature", std::numeric_limits<double>::infinity()}}; |
| 113 | + auto result = validator.validate(config); |
| 114 | + REQUIRE_FALSE(result.is_valid()); |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +TEST_CASE("Nested parameter validation", "[parameter_validator][unit]") { |
| 119 | + ParameterValidator validator; |
| 120 | + validator.add_metadata(ParameterMetadata<double>::builder() |
| 121 | + .name("model.temperature") |
| 122 | + .required(true) |
| 123 | + .range(0.0, 10000.0) |
| 124 | + .build()); |
| 125 | + |
| 126 | + SECTION("Valid nested parameter") { |
| 127 | + nlohmann::json config = {{"model", {{"temperature", 3300.0}}}}; |
| 128 | + auto result = validator.validate(config); |
| 129 | + REQUIRE(result.is_valid()); |
| 130 | + } |
| 131 | + |
| 132 | + SECTION("Missing nested parameter") { |
| 133 | + nlohmann::json config = {{"model", {}}}; |
| 134 | + auto result = validator.validate(config); |
| 135 | + REQUIRE_FALSE(result.is_valid()); |
| 136 | + auto errors_str = result.format_errors(); |
| 137 | + REQUIRE(errors_str.find("model.temperature") != std::string::npos); |
| 138 | + } |
| 139 | + |
| 140 | + SECTION("Deeply nested parameters") { |
| 141 | + ParameterValidator deep_validator; |
| 142 | + deep_validator.add_metadata(ParameterMetadata<double>::builder() |
| 143 | + .name("a.b.c.d.value") |
| 144 | + .required(true) |
| 145 | + .build()); |
| 146 | + |
| 147 | + nlohmann::json config = {{"a", {{"b", {{"c", {{"d", {{"value", 42.0}}}}}}}}}}; |
| 148 | + auto result = deep_validator.validate(config); |
| 149 | + REQUIRE(result.is_valid()); |
| 150 | + } |
| 151 | +} |
0 commit comments