Skip to content

Commit bb0ee09

Browse files
committed
(PUP-11663) Enable more types in max/min functions
Prior to this commit, the max and min core functions would compare SemVer, Timespan, and Timestamp types as Strings, which would lead to often incorrect results (e.g. max would state that a SemVer of 2.0.0 was greater than 10.0.0). This commit updates max and min to recognize SemVer, Timespan, and Timestamp types properly.
1 parent 76f7bbb commit bb0ee09

File tree

4 files changed

+192
-2
lines changed

4 files changed

+192
-2
lines changed

lib/puppet/functions/max.rb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@
8282
repeated_param 'String', :values
8383
end
8484

85+
dispatch :on_semver do
86+
repeated_param 'Semver', :values
87+
end
88+
89+
dispatch :on_timespan do
90+
repeated_param 'Timespan', :values
91+
end
92+
93+
dispatch :on_timestamp do
94+
repeated_param 'Timestamp', :values
95+
end
96+
8597
dispatch :on_single_numeric_array do
8698
param 'Array[Numeric]', :values
8799
optional_block_param 'Callable[2,2]', :block
@@ -92,6 +104,21 @@
92104
optional_block_param 'Callable[2,2]', :block
93105
end
94106

107+
dispatch :on_single_semver_array do
108+
param 'Array[Semver]', :values
109+
optional_block_param 'Callable[2,2]', :block
110+
end
111+
112+
dispatch :on_single_timespan_array do
113+
param 'Array[Timespan]', :values
114+
optional_block_param 'Callable[2,2]', :block
115+
end
116+
117+
dispatch :on_single_timestamp_array do
118+
param 'Array[Timestamp]', :values
119+
optional_block_param 'Callable[2,2]', :block
120+
end
121+
95122
dispatch :on_single_any_array do
96123
param 'Array', :values
97124
optional_block_param 'Callable[2,2]', :block
@@ -129,6 +156,21 @@ def on_string(*args)
129156
end
130157
end
131158

159+
def on_semver(*args)
160+
assert_arg_count(args)
161+
args.max
162+
end
163+
164+
def on_timespan(*args)
165+
assert_arg_count(args)
166+
args.max
167+
end
168+
169+
def on_timestamp(*args)
170+
assert_arg_count(args)
171+
args.max
172+
end
173+
132174
def on_any_with_block(*args, &block)
133175
args.max {|x,y| block.call(x,y) }
134176
end
@@ -149,6 +191,30 @@ def on_single_string_array(array, &block)
149191
end
150192
end
151193

194+
def on_single_semver_array(array, &block)
195+
if block_given?
196+
on_any_with_block(*array, &block)
197+
else
198+
on_semver(*array)
199+
end
200+
end
201+
202+
def on_single_timespan_array(array, &block)
203+
if block_given?
204+
on_any_with_block(*array, &block)
205+
else
206+
on_timespan(*array)
207+
end
208+
end
209+
210+
def on_single_timestamp_array(array, &block)
211+
if block_given?
212+
on_any_with_block(*array, &block)
213+
else
214+
on_timestamp(*array)
215+
end
216+
end
217+
152218
def on_single_any_array(array, &block)
153219
if block_given?
154220
on_any_with_block(*array, &block)

lib/puppet/functions/min.rb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,38 @@
8181
repeated_param 'String', :values
8282
end
8383

84+
dispatch :on_semver do
85+
repeated_param 'Semver', :values
86+
end
87+
88+
dispatch :on_timespan do
89+
repeated_param 'Timespan', :values
90+
end
91+
92+
dispatch :on_timestamp do
93+
repeated_param 'Timestamp', :values
94+
end
95+
8496
dispatch :on_single_numeric_array do
8597
param 'Array[Numeric]', :values
8698
optional_block_param 'Callable[2,2]', :block
8799
end
88100

101+
dispatch :on_single_semver_array do
102+
param 'Array[Semver]', :values
103+
optional_block_param 'Callable[2,2]', :block
104+
end
105+
106+
dispatch :on_single_timespan_array do
107+
param 'Array[Timespan]', :values
108+
optional_block_param 'Callable[2,2]', :block
109+
end
110+
111+
dispatch :on_single_timestamp_array do
112+
param 'Array[Timestamp]', :values
113+
optional_block_param 'Callable[2,2]', :block
114+
end
115+
89116
dispatch :on_single_string_array do
90117
param 'Array[String]', :values
91118
optional_block_param 'Callable[2,2]', :block
@@ -128,6 +155,21 @@ def on_string(*args)
128155
end
129156
end
130157

158+
def on_semver(*args)
159+
assert_arg_count(args)
160+
args.min
161+
end
162+
163+
def on_timespan(*args)
164+
assert_arg_count(args)
165+
args.min
166+
end
167+
168+
def on_timestamp(*args)
169+
assert_arg_count(args)
170+
args.min
171+
end
172+
131173
def on_any_with_block(*args, &block)
132174
args.min {|x,y| block.call(x,y) }
133175
end
@@ -148,6 +190,30 @@ def on_single_string_array(array, &block)
148190
end
149191
end
150192

193+
def on_single_semver_array(array, &block)
194+
if block_given?
195+
on_any_with_block(*array, &block)
196+
else
197+
on_semver(*array)
198+
end
199+
end
200+
201+
def on_single_timespan_array(array, &block)
202+
if block_given?
203+
on_any_with_block(*array, &block)
204+
else
205+
on_timespan(*array)
206+
end
207+
end
208+
209+
def on_single_timestamp_array(array, &block)
210+
if block_given?
211+
on_any_with_block(*array, &block)
212+
else
213+
on_timestamp(*array)
214+
end
215+
end
216+
151217
def on_single_any_array(array, &block)
152218
if block_given?
153219
on_any_with_block(*array, &block)

spec/unit/functions/max_spec.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,43 @@
6767

6868
end
6969

70+
context 'compares semver' do
71+
{ ["Semver('2.0.0')", "Semver('10.0.0')"] => "Semver('10.0.0')",
72+
["Semver('5.5.5')", "Semver('5.6.7')"] => "Semver('5.6.7')",
73+
}.each_pair do |values, expected|
74+
it "called as max(#{values[0]}, #{values[1]}) results in the value #{expected}" do
75+
expect(compile_to_catalog("notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }")).to have_resource("Notify[true]")
76+
end
77+
end
78+
end
79+
80+
context 'compares timespans' do
81+
{ ["Timespan(2)", "Timespan(77.3)"] => "Timespan(77.3)",
82+
["Timespan('1-00:00:00')", "Timespan('2-00:00:00')"] => "Timespan('2-00:00:00')",
83+
}.each_pair do |values, expected|
84+
it "called as max(#{values[0]}, #{values[1]}) results in the value #{expected}" do
85+
expect(compile_to_catalog("notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }")).to have_resource("Notify[true]")
86+
end
87+
end
88+
end
89+
90+
context 'compares timestamps' do
91+
{ ["Timestamp(0)", "Timestamp(298922400)"] => "Timestamp(298922400)",
92+
["Timestamp('1970-01-01T12:00:00.000')", "Timestamp('1979-06-22T18:00:00.000')"] => "Timestamp('1979-06-22T18:00:00.000')",
93+
}.each_pair do |values, expected|
94+
it "called as max(#{values[0]}, #{values[1]}) results in the value #{expected}" do
95+
expect(compile_to_catalog("notify { String( max(#{values[0]}, #{values[1]}) == #{expected}): }")).to have_resource("Notify[true]")
96+
end
97+
end
98+
end
99+
70100
context 'compares all except numeric and string by conversion to string and issues deprecation warning' do
71101
{
72102
[[20], "'a'"] => "'a'", # after since '[' is before 'a'
73103
["{'a' => 10}", "'a'"] => "{'a' => 10}", # after since '{' is after 'a'
74104
[false, 'fal'] => "false", # the boolean since text 'false' is longer
75105
['/b/', "'(?-mix:c)'"] => "'(?-mix:c)'", # because regexp to_s is a (?-mix:b) string
76106
["Timestamp(1)", "'1980 a.d'"] => "'1980 a.d'", # because timestamp to_s is a date-time string here starting with 1970
77-
["Semver('2.0.0')", "Semver('10.0.0')"] => "Semver('2.0.0')", # "10.0.0" is lexicographically before "2.0.0"
78107
}.each_pair do |values, expected|
79108
it "called as max(#{values[0]}, #{values[1]}) results in the value #{expected} and issues deprecation warning" do
80109
Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do

spec/unit/functions/min_spec.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,43 @@
6767

6868
end
6969

70+
context 'compares semver' do
71+
{ ["Semver('2.0.0')", "Semver('10.0.0')"] => "Semver('2.0.0')",
72+
["Semver('5.5.5')", "Semver('5.6.7')"] => "Semver('5.5.5')",
73+
}.each_pair do |values, expected|
74+
it "called as min(#{values[0]}, #{values[1]}) results in the value #{expected}" do
75+
expect(compile_to_catalog("notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }")).to have_resource("Notify[true]")
76+
end
77+
end
78+
end
79+
80+
context 'compares timespans' do
81+
{ ["Timespan(2)", "Timespan(77.3)"] => "Timespan(2)",
82+
["Timespan('1-00:00:00')", "Timespan('2-00:00:00')"] => "Timespan('1-00:00:00')",
83+
}.each_pair do |values, expected|
84+
it "called as min(#{values[0]}, #{values[1]}) results in the value #{expected}" do
85+
expect(compile_to_catalog("notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }")).to have_resource("Notify[true]")
86+
end
87+
end
88+
end
89+
90+
context 'compares timestamps' do
91+
{ ["Timestamp(0)", "Timestamp(298922400)"] => "Timestamp(0)",
92+
["Timestamp('1970-01-01T12:00:00.000')", "Timestamp('1979-06-22T18:00:00.000')"] => "Timestamp('1970-01-01T12:00:00.000')",
93+
}.each_pair do |values, expected|
94+
it "called as min(#{values[0]}, #{values[1]}) results in the value #{expected}" do
95+
expect(compile_to_catalog("notify { String( min(#{values[0]}, #{values[1]}) == #{expected}): }")).to have_resource("Notify[true]")
96+
end
97+
end
98+
end
99+
70100
context 'compares all except numeric and string by conversion to string (and issues deprecation warning)' do
71101
{
72102
[[20], "'a'"] => [20], # before since '[' is before 'a'
73103
["{'a' => 10}", "'|a'"] => "{'a' => 10}", # before since '{' is before '|'
74104
[false, 'fal'] => "'fal'", # 'fal' before since shorter than 'false'
75105
['/b/', "'(?-mix:a)'"] => "'(?-mix:a)'", # because regexp to_s is a (?-mix:b) string
76106
["Timestamp(1)", "'1556 a.d'"] => "'1556 a.d'", # because timestamp to_s is a date-time string here starting with 1970
77-
["Semver('2.0.0')", "Semver('10.0.0')"] => "Semver('10.0.0')", # "10.0.0" is lexicographically before "2.0.0"
78107
}.each_pair do |values, expected|
79108
it "called as min(#{values[0]}, #{values[1]}) results in the value #{expected} and issues deprecation warning" do
80109
Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do

0 commit comments

Comments
 (0)