Skip to content

Commit 1680b81

Browse files
committed
fix(map): write back transformed field values to parent table
cv_check_map iterated over properties, called cv_check_node for each field (which applied transform via lua_replace on the stack copy), but never wrote the result back into the parent table. The transformed value was silently discarded. cv_check_array already did this correctly via lua_rawseti. Fix cv_check_map to match: after cv_check_node succeeds, push val_idx and call cv_map_setfield to persist the transformed value. This also fixes the case where a oneof variant applies a transform — the parent map's own transform now sees the already-transformed field value, matching old validator behaviour. Add regression tests in test/check_test.lua: - test_map_field_transform_writeback (scalar + oneof) - test_array_item_transform_writeback (regression guard) - test_array_item_transform_writeback_oneof (guard)
1 parent cff3764 commit 1680b81

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

cv/cv.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,6 +2863,14 @@ cv_check_map(lua_State *L, struct cv_ctx *ctx,
28632863
if (ctx->depth > 0)
28642864
ctx->depth--;
28652865

2866+
if (r && !ctx->validate_only) {
2867+
/* write back: cv_check_node may have
2868+
* updated val_idx via transform */
2869+
lua_pushvalue(L, val_idx);
2870+
cv_map_setfield(L, data_idx,
2871+
&pp->key);
2872+
}
2873+
28662874
lua_pop(L, 1); /* pop value */
28672875

28682876
if (!r) {

test/check_test.lua

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,104 @@ g.test_enum_value_error = function()
537537
errs[1].details.enum_variants, 'table')
538538
end
539539

540+
-- -------------------------------------------------------
541+
-- transform on map field must be written back
542+
-- -------------------------------------------------------
543+
544+
-- Regression: transform applied to a map field must be
545+
-- written back into the parent table.
546+
-- Previously cv_check_node mutated the stack copy but
547+
-- never called cv_map_setfield to persist the change.
548+
g.test_map_field_transform_writeback = function()
549+
-- simple scalar field with transform
550+
local s = schema({
551+
type = 'map',
552+
properties = {
553+
x = {
554+
type = 'number',
555+
transform = function(v)
556+
return v * 10
557+
end,
558+
},
559+
},
560+
})
561+
local r, errs = s:check({x = 5})
562+
t.assert_equals(errs, {})
563+
t.assert_equals(r.x, 50)
564+
565+
-- oneof field with transform: parent map transform
566+
-- must see the already-transformed child value
567+
local seen_by_parent
568+
local s2 = schema({
569+
type = 'map',
570+
properties = {
571+
field = {
572+
type = 'oneof',
573+
variants = {
574+
{
575+
type = 'number',
576+
transform = function(v)
577+
return v * 2
578+
end,
579+
},
580+
'string',
581+
},
582+
},
583+
},
584+
transform = function(v)
585+
seen_by_parent = v.field
586+
return v
587+
end,
588+
})
589+
local r2, errs2 = s2:check({field = 3})
590+
t.assert_equals(errs2, {})
591+
-- child transform: 3 -> 6
592+
t.assert_equals(r2.field, 6)
593+
-- parent transform must see the child result
594+
t.assert_equals(seen_by_parent, 6)
595+
end
596+
597+
-- -------------------------------------------------------
598+
-- transform on array items must be written back
599+
-- -------------------------------------------------------
600+
601+
-- Verify that cv_check_array already correctly writes
602+
-- back transformed item values (regression guard).
603+
g.test_array_item_transform_writeback = function()
604+
local s = schema({
605+
type = 'array',
606+
items = {
607+
type = 'number',
608+
transform = function(v)
609+
return v * 10
610+
end,
611+
},
612+
})
613+
local r, errs = s:check({1, 2, 3})
614+
t.assert_equals(errs, {})
615+
t.assert_equals(r, {10, 20, 30})
616+
end
617+
618+
-- array items via oneof with transform in matched variant
619+
g.test_array_item_transform_writeback_oneof = function()
620+
local s = schema({
621+
type = 'array',
622+
items = {
623+
type = 'oneof',
624+
variants = {
625+
{
626+
type = 'number',
627+
transform = function(v)
628+
return v * 2
629+
end,
630+
},
631+
'string',
632+
},
633+
},
634+
})
635+
local r, errs = s:check({3, 'hello', 5})
636+
t.assert_equals(errs, {})
637+
t.assert_equals(r, {6, 'hello', 10})
638+
end
639+
540640
-- vim: ts=4 sts=4 sw=4 et

0 commit comments

Comments
 (0)