@@ -21,6 +21,47 @@ def initialize(games: nil)
2121 end
2222 end
2323
24+ # Used in ".coerce_params" context
25+ class Int64Params < Stripe ::RequestParams
26+ attr_accessor :amount , :name , :items
27+
28+ def initialize ( amount : nil , name : nil , items : nil )
29+ @amount = amount
30+ @name = name
31+ @items = items
32+ end
33+
34+ def self . field_encodings
35+ {
36+ amount : :int64_string ,
37+ items : { kind : :array , element : { kind : :object , fields : { qty : :int64_string } } } ,
38+ }
39+ end
40+ end
41+
42+ # Used in ".coerce_params" context
43+ class PlainParams < Stripe ::RequestParams
44+ attr_accessor :foo
45+
46+ def initialize ( foo : nil )
47+ @foo = foo
48+ end
49+ end
50+
51+ # Used in "#to_h with field_encodings" context
52+ class AmountParams < Stripe ::RequestParams
53+ attr_accessor :amount , :label
54+
55+ def initialize ( amount : nil , label : nil )
56+ @amount = amount
57+ @label = label
58+ end
59+
60+ def self . field_encodings
61+ { amount : :int64_string }
62+ end
63+ end
64+
2465 context "#to_h" do
2566 should "convert to hash" do
2667 params = FooCreateParams . new (
@@ -261,7 +302,204 @@ def initialize(games: nil)
261302 end
262303 end
263304
264- context "RequestParams explicit key tracking" do
305+ context ".coerce_value" do
306+ should "return nil as-is" do
307+ assert_nil Stripe ::RequestParams . coerce_value ( nil , :int64_string )
308+ assert_nil Stripe ::RequestParams . coerce_value ( nil , { kind : :object , fields : { } } )
309+ end
310+
311+ should "convert Integer to String for int64_string encoding" do
312+ assert_equal "42" , Stripe ::RequestParams . coerce_value ( 42 , :int64_string )
313+ assert_equal "0" , Stripe ::RequestParams . coerce_value ( 0 , :int64_string )
314+ assert_equal "-7" , Stripe ::RequestParams . coerce_value ( -7 , :int64_string )
315+ end
316+
317+ should "pass through String for int64_string encoding" do
318+ assert_equal "42" , Stripe ::RequestParams . coerce_value ( "42" , :int64_string )
319+ assert_equal "hello" , Stripe ::RequestParams . coerce_value ( "hello" , :int64_string )
320+ end
321+
322+ should "convert Array of Integers for int64_string encoding" do
323+ assert_equal %w[ 1 2 3 ] , Stripe ::RequestParams . coerce_value ( [ 1 , 2 , 3 ] , :int64_string )
324+ end
325+
326+ should "handle mixed Array for int64_string encoding" do
327+ assert_equal %w[ 1 already 3 ] , Stripe ::RequestParams . coerce_value ( [ 1 , "already" , 3 ] , :int64_string )
328+ end
329+
330+ should "return value as-is for unknown encoding symbol" do
331+ assert_equal 42 , Stripe ::RequestParams . coerce_value ( 42 , :unknown_encoding )
332+ assert_equal "hello" , Stripe ::RequestParams . coerce_value ( "hello" , :something_else )
333+ end
334+
335+ should "coerce object fields recursively" do
336+ encoding = {
337+ kind : :object ,
338+ fields : { amount : :int64_string , name : nil } ,
339+ }
340+ input = { amount : 100 , name : "test" , extra : "untouched" }
341+ expected = { amount : "100" , name : "test" , extra : "untouched" }
342+ assert_equal expected , Stripe ::RequestParams . coerce_value ( input , encoding )
343+ end
344+
345+ should "coerce object fields with string keys" do
346+ encoding = {
347+ kind : :object ,
348+ fields : { amount : :int64_string , name : nil } ,
349+ }
350+ input = { "amount" => 100 , "name" => "test" , "extra" => "untouched" }
351+ expected = { "amount" => "100" , "name" => "test" , "extra" => "untouched" }
352+ assert_equal expected , Stripe ::RequestParams . coerce_value ( input , encoding )
353+ end
354+
355+ should "coerce deeply nested objects with string keys" do
356+ encoding = {
357+ kind : :object ,
358+ fields : {
359+ inner : { kind : :object , fields : { val : :int64_string } } ,
360+ } ,
361+ }
362+ input = { "inner" => { "val" => 99 , "other" => "keep" } , "top" => "also keep" }
363+ expected = { "inner" => { "val" => "99" , "other" => "keep" } , "top" => "also keep" }
364+ assert_equal expected , Stripe ::RequestParams . coerce_value ( input , encoding )
365+ end
366+
367+ should "coerce array of objects with string keys" do
368+ encoding = {
369+ kind : :array ,
370+ element : { kind : :object , fields : { id : :int64_string } } ,
371+ }
372+ input = [ { "id" => 1 , "name" => "a" } , { "id" => 2 , "name" => "b" } ]
373+ expected = [ { "id" => "1" , "name" => "a" } , { "id" => "2" , "name" => "b" } ]
374+ assert_equal expected , Stripe ::RequestParams . coerce_value ( input , encoding )
375+ end
376+
377+ should "return non-Hash as-is for object encoding" do
378+ encoding = { kind : :object , fields : { amount : :int64_string } }
379+ assert_equal "not a hash" , Stripe ::RequestParams . coerce_value ( "not a hash" , encoding )
380+ assert_equal 42 , Stripe ::RequestParams . coerce_value ( 42 , encoding )
381+ end
382+
383+ should "coerce array elements" do
384+ encoding = { kind : :array , element : :int64_string }
385+ assert_equal %w[ 1 2 3 ] , Stripe ::RequestParams . coerce_value ( [ 1 , 2 , 3 ] , encoding )
386+ end
387+
388+ should "return non-Array as-is for array encoding" do
389+ encoding = { kind : :array , element : :int64_string }
390+ assert_equal "not an array" , Stripe ::RequestParams . coerce_value ( "not an array" , encoding )
391+ end
392+
393+ should "coerce array of objects" do
394+ encoding = {
395+ kind : :array ,
396+ element : { kind : :object , fields : { id : :int64_string } } ,
397+ }
398+ input = [ { id : 1 , name : "a" } , { id : 2 , name : "b" } ]
399+ expected = [ { id : "1" , name : "a" } , { id : "2" , name : "b" } ]
400+ assert_equal expected , Stripe ::RequestParams . coerce_value ( input , encoding )
401+ end
402+
403+ should "coerce deeply nested object-in-object" do
404+ encoding = {
405+ kind : :object ,
406+ fields : {
407+ inner : { kind : :object , fields : { val : :int64_string } } ,
408+ } ,
409+ }
410+ input = { inner : { val : 99 , other : "keep" } , top : "also keep" }
411+ expected = { inner : { val : "99" , other : "keep" } , top : "also keep" }
412+ assert_equal expected , Stripe ::RequestParams . coerce_value ( input , encoding )
413+ end
414+
415+ should "handle object encoding with missing fields key" do
416+ encoding = { kind : :object }
417+ input = { amount : 100 }
418+ assert_equal ( { amount : 100 } , Stripe ::RequestParams . coerce_value ( input , encoding ) )
419+ end
420+
421+ should "return value as-is for unknown composite kind" do
422+ encoding = { kind : :unknown }
423+ assert_equal 42 , Stripe ::RequestParams . coerce_value ( 42 , encoding )
424+ end
425+ end
426+
427+ context ".coerce_params" do
428+ should "return non-Hash input as-is" do
429+ assert_nil Int64Params . coerce_params ( nil )
430+ assert_equal "string" , Int64Params . coerce_params ( "string" )
431+ assert_equal 42 , Int64Params . coerce_params ( 42 )
432+ end
433+
434+ should "return Hash as-is when no encodings are defined" do
435+ input = { foo : 123 , bar : "hello" }
436+ assert_equal input , PlainParams . coerce_params ( input )
437+ end
438+
439+ should "coerce fields with encodings and pass through others" do
440+ input = { amount : 500 , name : "test" }
441+ expected = { amount : "500" , name : "test" }
442+ assert_equal expected , Int64Params . coerce_params ( input )
443+ end
444+
445+ should "coerce nested array-of-objects encoding" do
446+ input = {
447+ amount : 42 ,
448+ name : "order" ,
449+ items : [ { qty : 10 , label : "a" } , { qty : 20 , label : "b" } ] ,
450+ }
451+ expected = {
452+ amount : "42" ,
453+ name : "order" ,
454+ items : [ { qty : "10" , label : "a" } , { qty : "20" , label : "b" } ] ,
455+ }
456+ assert_equal expected , Int64Params . coerce_params ( input )
457+ end
458+
459+ should "handle nil values in encoded fields" do
460+ input = { amount : nil , name : "test" }
461+ expected = { amount : nil , name : "test" }
462+ assert_equal expected , Int64Params . coerce_params ( input )
463+ end
464+
465+ should "coerce fields with string keys" do
466+ input = { "amount" => 500 , "name" => "test" }
467+ expected = { "amount" => "500" , "name" => "test" }
468+ assert_equal expected , Int64Params . coerce_params ( input )
469+ end
470+
471+ should "coerce nested array-of-objects with string keys" do
472+ input = {
473+ "amount" => 42 ,
474+ "name" => "order" ,
475+ "items" => [ { "qty" => 10 , "label" => "a" } , { "qty" => 20 , "label" => "b" } ] ,
476+ }
477+ expected = {
478+ "amount" => "42" ,
479+ "name" => "order" ,
480+ "items" => [ { "qty" => "10" , "label" => "a" } , { "qty" => "20" , "label" => "b" } ] ,
481+ }
482+ assert_equal expected , Int64Params . coerce_params ( input )
483+ end
484+ end
485+
486+ context "#to_h with field_encodings" do
487+ should "coerce int64_string fields when serializing via to_h" do
488+ params = AmountParams . new ( amount : 9_999_999_999 , label : "big" )
489+ result = params . to_h
490+ assert_equal "9999999999" , result [ :amount ]
491+ assert_equal "big" , result [ :label ]
492+ end
493+
494+ should "leave non-integer amount alone in to_h" do
495+ params = AmountParams . new ( amount : "already_string" , label : "ok" )
496+ result = params . to_h
497+ assert_equal "already_string" , result [ :amount ]
498+ assert_equal "ok" , result [ :label ]
499+ end
500+ end
501+
502+ context "V2 RequestParams explicit key tracking" do
265503 should "only serialize explicitly set fields for V2 classes" do
266504 params = Stripe ::V2 ::Billing ::MeterEventCreateParams . new (
267505 event_name : "my_event" ,
0 commit comments