fix(stock-pool): scan decoded native conditions instead of serialized source echoes
This commit is contained in:
@@ -221,7 +221,15 @@ pub fn specs_in_value(value: &Value) -> Result<Vec<PatternSpec>, String> {
|
||||
}
|
||||
}
|
||||
Value::Object(items) => {
|
||||
for v in items.values() {
|
||||
let typed_pool = items.get("stockPool").or_else(|| items.get("stock_pool"))
|
||||
.is_some_and(Value::is_object);
|
||||
for (key, v) in items {
|
||||
// The executable pool already supplies decoded expressions.
|
||||
// Its display/source serialization escapes those expressions
|
||||
// one more time and is not another executable program.
|
||||
if typed_pool && matches!(key.as_str(), "sourceCode" | "source_code") {
|
||||
continue;
|
||||
}
|
||||
specs.extend(specs_in_value(v)?);
|
||||
}
|
||||
}
|
||||
@@ -248,6 +256,25 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{BenchmarkSnapshot, DailyFactorSnapshot, DailyMarketSnapshot, Instrument};
|
||||
use serde_json::json;
|
||||
#[test]
|
||||
fn structured_pool_conditions_are_not_rescanned_inside_serialized_source_code() {
|
||||
let pattern = json!({"template":"expression","parameters":{"history_window":20},
|
||||
"expression":{"kind":"operator","name":"GT","args":[{"kind":"field","name":"amount"},{"kind":"number","value":0}]}});
|
||||
let expr = format!("pattern_signal({})", serde_json::to_string(&pattern.to_string()).unwrap());
|
||||
let pool = json!({"schema_version":1,"pool_id":"fixture","version_id":"v1","members":[],
|
||||
"allocation_policy":{},"timing_policy":{},"stop_take_policy":{},"out_of_pool_policy":"hold",
|
||||
"exit_signals":[{"role":"risk_exit","when_expr":expr,"remaining_position_bps":5000,"reason":"fixture"}]});
|
||||
let source = format!("stock_pool.config({pool})");
|
||||
for (pool_key, source_key) in [("stockPool", "sourceCode"), ("stock_pool", "source_code")] {
|
||||
let value = json!({pool_key:pool,source_key:source,"runtimeExpressions":{"trading":{"buyFilterExpr":expr}}});
|
||||
assert_eq!(specs_in_value(&value).unwrap().len(), 2);
|
||||
let mut invalid = value.clone();
|
||||
invalid[pool_key]["exit_signals"][0]["when_expr"] = json!("pattern_signal(not-json)");
|
||||
assert!(specs_in_value(&invalid).is_err(), "invalid actual conditions must still fail");
|
||||
}
|
||||
assert_eq!(specs_in_value(&json!({"sourceCode":format!("risk.stop_loss({expr})")})).unwrap().len(),1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalized_rule_does_not_turn_an_omitted_window_into_explicit_null() {
|
||||
let expression:Expr=serde_json::from_value(json!({"kind":"operator","name":"GT","args":[{"kind":"field","name":"close"},{"kind":"number","value":1}]})).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user