holidays 4.6.0 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/CONTRIBUTING.md +5 -5
  4. data/Makefile +29 -0
  5. data/README.md +13 -1
  6. data/Rakefile +2 -2
  7. data/definitions/au.yaml +12 -9
  8. data/definitions/ca.yaml +17 -2
  9. data/definitions/ch.yaml +1 -1
  10. data/definitions/index.yaml +3 -0
  11. data/definitions/is.yaml +1 -1
  12. data/definitions/jp.yaml +19 -14
  13. data/definitions/kr.yaml +282 -0
  14. data/definitions/lu.yaml +56 -0
  15. data/definitions/my.yaml +51 -0
  16. data/definitions/ups.yaml +1 -1
  17. data/definitions/us.yaml +1 -1
  18. data/lib/generated_definitions/MANIFEST +3 -0
  19. data/lib/generated_definitions/REGIONS.rb +1 -1
  20. data/lib/generated_definitions/au.rb +11 -8
  21. data/lib/generated_definitions/ca.rb +1 -1
  22. data/lib/generated_definitions/ch.rb +1 -1
  23. data/lib/generated_definitions/europe.rb +2 -2
  24. data/lib/generated_definitions/is.rb +1 -1
  25. data/lib/generated_definitions/jp.rb +13 -15
  26. data/lib/generated_definitions/kr.rb +248 -0
  27. data/lib/generated_definitions/lu.rb +39 -0
  28. data/lib/generated_definitions/my.rb +36 -0
  29. data/lib/generated_definitions/north_america.rb +2 -2
  30. data/lib/generated_definitions/scandinavia.rb +1 -1
  31. data/lib/generated_definitions/ups.rb +1 -1
  32. data/lib/generated_definitions/us.rb +1 -1
  33. data/lib/holidays.rb +29 -51
  34. data/lib/holidays/core_extensions/date.rb +1 -1
  35. data/lib/holidays/definition/context/function_processor.rb +86 -0
  36. data/lib/holidays/definition/context/generator.rb +31 -6
  37. data/lib/holidays/definition/parser/custom_method.rb +1 -3
  38. data/lib/holidays/definition/repository/holidays_by_month.rb +1 -1
  39. data/lib/holidays/definition/validator/region.rb +1 -3
  40. data/lib/holidays/errors.rb +1 -0
  41. data/lib/holidays/factory/date_calculator.rb +37 -0
  42. data/lib/holidays/factory/definition.rb +96 -0
  43. data/lib/holidays/factory/finder.rb +70 -0
  44. data/lib/holidays/finder/context/between.rb +43 -0
  45. data/lib/holidays/{use_case → finder}/context/dates_driver_builder.rb +6 -4
  46. data/lib/holidays/finder/context/next_holiday.rb +57 -0
  47. data/lib/holidays/{option → finder}/context/parse_options.rb +6 -8
  48. data/lib/holidays/finder/context/search.rb +86 -0
  49. data/lib/holidays/finder/context/year_holiday.rb +57 -0
  50. data/lib/holidays/finder/rules/in_region.rb +23 -0
  51. data/lib/holidays/finder/rules/year_range.rb +82 -0
  52. data/lib/holidays/load_all_definitions.rb +7 -5
  53. data/lib/holidays/version.rb +1 -1
  54. data/test/coverage_report.rb +7 -0
  55. data/test/defs/test_defs_au.rb +1 -1
  56. data/test/defs/test_defs_ca.rb +16 -1
  57. data/test/defs/test_defs_jp.rb +4 -0
  58. data/test/defs/test_defs_kr.rb +26 -0
  59. data/test/defs/test_defs_lu.rb +24 -0
  60. data/test/defs/test_defs_my.rb +20 -0
  61. data/test/defs/test_defs_north_america.rb +16 -1
  62. data/test/holidays/core_extensions/test_date_time.rb +7 -7
  63. data/test/holidays/definition/context/test_function_processor.rb +175 -0
  64. data/test/holidays/definition/context/test_generator.rb +18 -11
  65. data/test/holidays/definition/parser/test_custom_method.rb +2 -2
  66. data/test/holidays/definition/repository/test_proc_result_cache.rb +1 -1
  67. data/test/holidays/{test_date_calculator_factory.rb → factory/test_date_calculator.rb} +3 -3
  68. data/test/holidays/factory/test_definition.rb +53 -0
  69. data/test/holidays/factory/test_finder.rb +25 -0
  70. data/test/holidays/finder/context/test_between.rb +172 -0
  71. data/test/holidays/{use_case → finder}/context/test_dates_driver_builder.rb +2 -2
  72. data/test/holidays/finder/context/test_next_holiday.rb +156 -0
  73. data/test/holidays/{option → finder}/context/test_parse_options.rb +9 -9
  74. data/test/holidays/finder/context/test_search.rb +203 -0
  75. data/test/holidays/finder/context/test_year_holiday.rb +202 -0
  76. data/test/holidays/finder/rules/test_in_region.rb +38 -0
  77. data/test/holidays/finder/rules/test_year_range.rb +170 -0
  78. data/test/integration/README.md +9 -0
  79. data/test/{test_all_regions.rb → integration/test_all_regions.rb} +16 -2
  80. data/test/{test_custom_holidays.rb → integration/test_custom_holidays.rb} +2 -2
  81. data/test/{test_custom_year_range_holidays.rb → integration/test_custom_year_range_holidays.rb} +1 -1
  82. data/test/{test_holidays.rb → integration/test_holidays.rb} +43 -32
  83. data/test/{test_holidays_between.rb → integration/test_holidays_between.rb} +8 -16
  84. data/test/{test_multiple_regions.rb → integration/test_multiple_regions.rb} +1 -1
  85. data/test/test_helper.rb +3 -4
  86. metadata +67 -39
  87. data/benchmark.rb +0 -8
  88. data/lib/holidays/date_calculator_factory.rb +0 -35
  89. data/lib/holidays/definition_factory.rb +0 -86
  90. data/lib/holidays/option_factory.rb +0 -15
  91. data/lib/holidays/use_case/context/between.rb +0 -45
  92. data/lib/holidays/use_case/context/context_common.rb +0 -123
  93. data/lib/holidays/use_case/context/next_holiday.rb +0 -54
  94. data/lib/holidays/use_case/context/year_holiday.rb +0 -51
  95. data/lib/holidays/use_case_factory.rb +0 -41
  96. data/test/holidays/test_definition_factory.rb +0 -49
  97. data/test/holidays/test_option_factory.rb +0 -9
  98. data/test/holidays/test_use_case_factory.rb +0 -13
  99. data/test/holidays/use_case/context/test_between.rb +0 -77
@@ -0,0 +1,57 @@
1
+ module Holidays
2
+ module Finder
3
+ module Context
4
+ class YearHoliday
5
+ def initialize(definition_search, dates_driver_builder, options_parser)
6
+ @definition_search = definition_search
7
+ @dates_driver_builder = dates_driver_builder
8
+ @options_parser = options_parser
9
+ end
10
+
11
+ def call(from_date, options)
12
+ validate!(from_date)
13
+
14
+ regions, observed, informal = @options_parser.call(options)
15
+
16
+ # This could be smarter but I don't have any evidence that just checking for
17
+ # the next 12 months will cause us issues. If it does we can implement something
18
+ # smarter here to check in smaller increments.
19
+ #
20
+ #FIXME Could this be until the to_date instead? Save us some processing?
21
+ # This is matching what was in holidays.rb currently so I'm keeping it. -pp
22
+ dates_driver = @dates_driver_builder.call(from_date, from_date >> 12)
23
+
24
+ to_date = Date.civil(from_date.year, 12, 31)
25
+ holidays = []
26
+ ret_holidays = []
27
+ opts = gather_options(observed, informal)
28
+
29
+ ret_holidays = @definition_search.call(dates_driver, regions, opts)
30
+
31
+ ret_holidays.each do |holiday|
32
+ if holiday[:date] >= from_date && holiday[:date] <= to_date
33
+ holidays << holiday
34
+ end
35
+ end
36
+
37
+ holidays.sort{|a, b| a[:date] <=> b[:date] }
38
+ end
39
+
40
+ private
41
+
42
+ def validate!(from_date)
43
+ raise ArgumentError unless from_date && from_date.is_a?(Date)
44
+ end
45
+
46
+ def gather_options(observed, informal)
47
+ opts = []
48
+
49
+ opts << :observed if observed == true
50
+ opts << :informal if informal == true
51
+
52
+ opts
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ module Holidays
2
+ module Finder
3
+ module Rules
4
+ class InRegion
5
+ class << self
6
+ def call(requested, available)
7
+ return true if requested.include?(:any)
8
+
9
+ # When an underscore is encountered, derive the parent regions
10
+ # symbol and check for both.
11
+ requested = requested.collect do |r|
12
+ r.to_s =~ /_/ ? [r, r.to_s.gsub(/_[\w]*$/, '').to_sym] : r
13
+ end
14
+
15
+ requested = requested.flatten.uniq
16
+
17
+ available.any? { |avail| requested.include?(avail) }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,82 @@
1
+ # Please note that only one condition needs to match in order for `call` to return `true.
2
+ # See the test file for this class for specific examples.
3
+ module Holidays
4
+ module Finder
5
+ module Rules
6
+ class YearRange
7
+ class << self
8
+ BEFORE = :before
9
+ AFTER = :after
10
+ LIMITED = :limited
11
+ BETWEEN = :between
12
+
13
+ #TODO Can we just accept symbols here? Why accept strings?
14
+ VALID_OPERATORS = [
15
+ BEFORE, BEFORE.to_s,
16
+ AFTER, AFTER.to_s,
17
+ LIMITED, LIMITED.to_s,
18
+ BETWEEN, BETWEEN.to_s
19
+ ]
20
+
21
+ def call(target_year, year_range_definitions)
22
+ validate!(target_year, year_range_definitions)
23
+
24
+ matched = false
25
+ year_range_definitions.each do |range_defs|
26
+ next unless range_defs.is_a?(Hash) && range_defs.length == 1
27
+
28
+ operator = range_defs.keys.first
29
+ year_range = range_defs.values.first
30
+
31
+ case operator
32
+ when BEFORE, BEFORE.to_s
33
+ matched = target_year <= year_range
34
+ when AFTER, AFTER.to_s
35
+ matched = target_year >= year_range
36
+ when LIMITED, LIMITED.to_s
37
+ if year_range.is_a?(Array)
38
+ matched = year_range.include?(target_year)
39
+ else
40
+ matched = year_range == target_year
41
+ end
42
+ when BETWEEN, BETWEEN.to_s
43
+ matched = year_range.cover?(target_year)
44
+ end
45
+
46
+ break if matched == true
47
+ end
48
+
49
+ matched
50
+ end
51
+
52
+ private
53
+
54
+ def validate!(target_year, year_ranges)
55
+ raise ArgumentError.new("target_year must be a number") unless target_year.is_a?(Integer)
56
+ raise ArgumentError.new("year_ranges cannot be missing") if year_ranges.nil? || year_ranges.empty?
57
+
58
+ year_ranges.each do |range|
59
+ raise ArgumentError.new("year_ranges must include only hashes") unless range.is_a?(Hash)
60
+ raise ArgumentError.new("year_ranges cannot include empty hashes") if range.empty?
61
+ raise ArgumentError.new("year_ranges entries can only include one operator") unless range.count == 1
62
+
63
+ operator = range.keys.first
64
+ range = range.values.first
65
+
66
+ raise ArgumentError.new("Invalid operator found: '#{operator}'") unless VALID_OPERATORS.include?(operator)
67
+
68
+ case operator
69
+ when BEFORE, BEFORE.to_s, AFTER, AFTER.to_s
70
+ raise ArgumentError.new(":before and :after operator value must be a number, received: '#{range}'") unless range.is_a?(Integer)
71
+ when LIMITED, LIMITED.to_s
72
+ raise ArgumentError.new(":limited operator value must be an array, received: '#{range}'") unless range.is_a?(Array) || range.is_a?(Integer)
73
+ when BETWEEN, BETWEEN.to_s
74
+ raise ArgumentError.new(":between operator value must be a range, received: '#{range}'") unless range.is_a?(Range)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -17,6 +17,8 @@ module Holidays
17
17
  # into some kind of definition file so it can be loaded automatically but I'm afraid
18
18
  # of making that big of a breaking API change since these are public. For the time
19
19
  # being I'll load them manually like this.
20
+ #
21
+ # NOTE: These are no longer public! We can do whatever we want here!
20
22
  global_methods = {
21
23
  "easter(year)" => gregorian_easter.method(:calculate_easter_for).to_proc,
22
24
  "orthodox_easter(year)" => gregorian_easter.method(:calculate_orthodox_easter_for).to_proc,
@@ -32,25 +34,25 @@ module Holidays
32
34
  "to_tuesday_if_sunday_or_monday_if_saturday(date)" => weekend_modifier.method(:to_tuesday_if_sunday_or_monday_if_saturday).to_proc,
33
35
  }
34
36
 
35
- Holidays::DefinitionFactory.custom_methods_repository.add(global_methods)
37
+ Factory::Definition.custom_methods_repository.add(global_methods)
36
38
  end
37
39
 
38
40
  private
39
41
 
40
42
  def gregorian_easter
41
- DateCalculatorFactory::Easter::Gregorian.easter_calculator
43
+ Factory::DateCalculator::Easter::Gregorian.easter_calculator
42
44
  end
43
45
 
44
46
  def julian_easter
45
- DateCalculatorFactory::Easter::Julian.easter_calculator
47
+ Factory::DateCalculator::Easter::Julian.easter_calculator
46
48
  end
47
49
 
48
50
  def weekend_modifier
49
- DateCalculatorFactory.weekend_modifier
51
+ Factory::DateCalculator.weekend_modifier
50
52
  end
51
53
 
52
54
  def day_of_month_calculator
53
- DateCalculatorFactory.day_of_month_calculator
55
+ Factory::DateCalculator.day_of_month_calculator
54
56
  end
55
57
  end
56
58
  end
@@ -1,3 +1,3 @@
1
1
  module Holidays
2
- VERSION = '4.6.0'
2
+ VERSION = '4.7.0'
3
3
  end
@@ -0,0 +1,7 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
5
+ SimpleCov.start do
6
+ add_filter 'lib/generated_definitions/'
7
+ end
@@ -75,7 +75,7 @@ assert_equal 'Melbourne Cup Day', Holidays.on(Date.civil(2014,11,4), :au_vic_mel
75
75
  assert_equal 'Melbourne Cup Day', Holidays.on(Date.civil(2015,11,3), :au_vic_melbourne)[0][:name]
76
76
 
77
77
  assert_equal 'Friday before the AFL Grand Final', Date.civil(2015,10,2).holidays(:au_vic)[0][:name]
78
- assert_nil Date.civil(2016,10,2).holidays(:au_vic)[0]
78
+ assert_equal 'Friday before the AFL Grand Final', Date.civil(2016,9, 30).holidays(:au_vic)[0][:name]
79
79
 
80
80
  assert_equal "May Public Holiday", Date.civil(2005, 5, 16).holidays(:au_sa)[0][:name]
81
81
  assert_equal [], Date.civil(2014, 5, 19).holidays(:au_sa)
@@ -15,7 +15,6 @@ class CaDefinitionTests < Test::Unit::TestCase # :nodoc:
15
15
  Date.civil(2008,7,1) => 'Canada Day',
16
16
  Date.civil(2008,9,1) => 'Labour Day',
17
17
  Date.civil(2008,10,13) => 'Thanksgiving',
18
- Date.civil(2008,11,11) => 'Remembrance Day',
19
18
  Date.civil(2008,12,25) => 'Christmas Day',
20
19
  Date.civil(2008,12,26) => 'Boxing Day'}.each do |date, name|
21
20
  assert_equal name, (Holidays.on(date, :ca, :informal)[0] || {})[:name]
@@ -145,6 +144,22 @@ end
145
144
  end
146
145
  end
147
146
 
147
+ # Remembrance Day in all Canadian provinces
148
+ # except (Nova Scotia, Manitoba, Ontario, and Quebec)
149
+ [
150
+ :ca_ab,
151
+ :ca_sk,
152
+ :ca_bc,
153
+ :ca_pe,
154
+ :ca_nf,
155
+ :ca_nt,
156
+ :ca_nu,
157
+ :ca_nb,
158
+ :ca_yk
159
+ ].each do |province|
160
+ assert_equal "Remembrance Day", Holidays.on(Date.civil(2016,11,11), province)[0][:name]
161
+ end
162
+
148
163
 
149
164
  {Date.civil(2013,2,2) => 'Groundhog Day',
150
165
  Date.civil(2013,2,14) => 'Valentine\'s Day',
@@ -69,5 +69,9 @@ end
69
69
  # before 2016, there is no mountain holiday.
70
70
  assert_nil Holidays.on(Date.civil(2015,8,11), :jp)[0]
71
71
 
72
+ # before 2003, there is no citizens holiday.
73
+ # [note] citizens holiday requires that jp_national_culture_day is wednesday.
74
+ # Before 2003, the closest past year that mathches above condition is 1998.
75
+ assert_nil Holidays.on(Date.civil(1998,9,22), :jp)[0]
72
76
  end
73
77
  end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
3
+
4
+ # This file is generated by the Ruby Holiday gem.
5
+ #
6
+ # Definitions loaded: definitions/kr.yaml
7
+ class KrDefinitionTests < Test::Unit::TestCase # :nodoc:
8
+
9
+ def test_kr
10
+ {Date.civil(2016,2,8) => "Korean New Year",
11
+ Date.civil(2016,5,14) => "Buddah\'s Birthday",
12
+ Date.civil(2016,9,12) => "Korean Thanksgiving",
13
+ Date.civil(2016,1,1) => "New Year\'s Day",
14
+ Date.civil(2016,3,1) => "Independence Movement Day",
15
+ Date.civil(2016,5,5) => "Children\'s Day",
16
+ Date.civil(2016,6,6) => "Memorial Day",
17
+ Date.civil(2016,7,17) => "Constitution Day",
18
+ Date.civil(2016,8,15) => "Liberation Day",
19
+ Date.civil(2016,10,3) => "National Foundation Day",
20
+ Date.civil(2016,10,9) => "Hangul Day",
21
+ Date.civil(2016,12,25) => "Christmas Day"}.each do |date, name|
22
+ assert_equal name, (Holidays.on(date, :kr, :informal)[0] || {})[:name]
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
3
+
4
+ # This file is generated by the Ruby Holiday gem.
5
+ #
6
+ # Definitions loaded: definitions/lu.yaml
7
+ class LuDefinitionTests < Test::Unit::TestCase # :nodoc:
8
+
9
+ def test_lu
10
+ {Date.civil(2008,1,1) => 'Neijoerschdag',
11
+ Date.civil(2008,3,24) => 'Ouschterméindeg',
12
+ Date.civil(2008,5,1) => 'Christi Himmelfaart', # Ascension, Easter+39
13
+ Date.civil(2008,6,23) => 'Nationalfeierdag',
14
+ Date.civil(2008,8,15) => 'Léiffrawëschdag',
15
+ Date.civil(2008,11,1) => 'Allerhellgen',
16
+ Date.civil(2008,12,25) => 'Chrëschtdag',
17
+ Date.civil(2008,12,26) => 'Stiefesdag',
18
+ Date.civil(2008,5,11) => 'Péngschtméindeg'
19
+ }.each do |date, name|
20
+ assert_equal name, (Holidays.on(date, :lu, :informal)[0] || {})[:name]
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
3
+
4
+ # This file is generated by the Ruby Holiday gem.
5
+ #
6
+ # Definitions loaded: definitions/my.yaml
7
+ class MyDefinitionTests < Test::Unit::TestCase # :nodoc:
8
+
9
+ def test_my
10
+ {Date.civil(2016,1,1) => "New Year's Day",
11
+ Date.civil(2016,5,1) => 'Labour Day',
12
+ Date.civil(2016,6,4) => "Agong's Birthday",
13
+ Date.civil(2016,8,31) => 'Independence Day',
14
+ Date.civil(2016,9,16) => 'Malaysia Day',
15
+ Date.civil(2016,12,25) => 'Christmas Day'}.each do |date, name|
16
+ assert_equal name, (Holidays.on(date, :my, :informal)[0] || {})[:name]
17
+ end
18
+
19
+ end
20
+ end
@@ -15,7 +15,6 @@ class North_americaDefinitionTests < Test::Unit::TestCase # :nodoc:
15
15
  Date.civil(2008,7,1) => 'Canada Day',
16
16
  Date.civil(2008,9,1) => 'Labour Day',
17
17
  Date.civil(2008,10,13) => 'Thanksgiving',
18
- Date.civil(2008,11,11) => 'Remembrance Day',
19
18
  Date.civil(2008,12,25) => 'Christmas Day',
20
19
  Date.civil(2008,12,26) => 'Boxing Day'}.each do |date, name|
21
20
  assert_equal name, (Holidays.on(date, :ca, :informal)[0] || {})[:name]
@@ -145,6 +144,22 @@ end
145
144
  end
146
145
  end
147
146
 
147
+ # Remembrance Day in all Canadian provinces
148
+ # except (Nova Scotia, Manitoba, Ontario, and Quebec)
149
+ [
150
+ :ca_ab,
151
+ :ca_sk,
152
+ :ca_bc,
153
+ :ca_pe,
154
+ :ca_nf,
155
+ :ca_nt,
156
+ :ca_nu,
157
+ :ca_nb,
158
+ :ca_yk
159
+ ].each do |province|
160
+ assert_equal "Remembrance Day", Holidays.on(Date.civil(2016,11,11), province)[0][:name]
161
+ end
162
+
148
163
 
149
164
  {Date.civil(2007,1,1) => 'Año nuevo',
150
165
  Date.civil(2007,2,5) => 'Día de la Constitución',
@@ -7,16 +7,16 @@ class Date
7
7
  include Holidays::CoreExtensions::Date
8
8
  end
9
9
 
10
- class Time
10
+ class Time
11
11
  include Holidays::CoreExtensions::Time
12
- end
12
+ end
13
13
 
14
14
  class CoreExtensionDateTimeTests < Test::Unit::TestCase
15
15
  def setup
16
16
  @date = Date.civil(2008,1,1)
17
17
  end
18
18
 
19
- def test_change_method
19
+ def test_change_method
20
20
  actual = @date.change(day: 5)
21
21
  assert_equal Date.civil(2008,1,5), actual
22
22
 
@@ -28,7 +28,7 @@ class CoreExtensionDateTimeTests < Test::Unit::TestCase
28
28
 
29
29
  actual = @date.change(year: 2015, month: 5, day: 3)
30
30
  assert_equal Date.civil(2015,5,3), actual
31
- end
31
+ end
32
32
 
33
33
  def test_end_of_month_method
34
34
  # Works for month with 31 days
@@ -41,7 +41,7 @@ class CoreExtensionDateTimeTests < Test::Unit::TestCase
41
41
 
42
42
  # Works for leap year
43
43
  actual = Date.civil(2016,2,1).end_of_month
44
- assert_equal = Date.civil(2016,2,29), actual
44
+ assert_equal Date.civil(2016,2,29), actual
45
45
  end
46
46
 
47
47
  def test_days_in_month_method
@@ -56,5 +56,5 @@ class CoreExtensionDateTimeTests < Test::Unit::TestCase
56
56
  # Works for leap year
57
57
  actual = Time.days_in_month(2, 2016)
58
58
  assert_equal 29, actual
59
- end
60
- end
59
+ end
60
+ end
@@ -0,0 +1,175 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/../../../test_helper'
2
+
3
+ require 'holidays/definition/context/function_processor'
4
+
5
+ class FunctionProcessorTests < Test::Unit::TestCase
6
+ def setup
7
+ @year = 2016
8
+ @month = 1
9
+ @day = 15
10
+ @func_id = "custom_function_id"
11
+ @func_args = [:year]
12
+ @func_modifier = 1
13
+
14
+ @custom_methods_repo = mock()
15
+ @proc_result_cache_repo = mock()
16
+
17
+ @custom_func = mock()
18
+
19
+ @custom_methods_repo.expects(:find).at_most_once.with(@func_id).returns(@custom_func)
20
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns(Date.civil(@year, @month, @day))
21
+
22
+ @subject = Holidays::Definition::Context::FunctionProcessor.new(
23
+ @custom_methods_repo,
24
+ @proc_result_cache_repo,
25
+ )
26
+ end
27
+
28
+ def test_no_function_arguments_returns_error
29
+ assert_raises ArgumentError do
30
+ @subject.call(@year, @month, @day, @func_id, nil, @func_modifier)
31
+ end
32
+
33
+ assert_raises ArgumentError do
34
+ @subject.call(@year, @month, @day, @func_id, [], @func_modifier)
35
+ end
36
+ end
37
+
38
+ def test_unknown_function_argument_returns_error
39
+ assert_raises ArgumentError do
40
+ @subject.call(@year, @month, @day, @func_id, [:something], @func_modifier)
41
+ end
42
+ end
43
+
44
+ def test_unknown_function_id_returns_error
45
+ bad_id = "some-bad-id"
46
+ @custom_methods_repo.expects(:find).at_most_once.with(bad_id).returns(nil)
47
+
48
+ assert_raises Holidays::FunctionNotFound do
49
+ @subject.call(@year, @month, @day, bad_id, @func_args, @func_modifier)
50
+ end
51
+ end
52
+
53
+ def test_year_arg_passed_to_func_call
54
+ @func_args = [:year]
55
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns(Date.civil(2016, 1, 15))
56
+
57
+ @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
58
+ end
59
+
60
+ def test_month_arg_passed_to_func_call
61
+ @func_args = [:month]
62
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @month).returns(Date.civil(2016, 1, 15))
63
+
64
+ @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
65
+ end
66
+
67
+ def test_day_arg_passed_to_func_call
68
+ @func_args = [:day]
69
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @day).returns(Date.civil(2016, 1, 15))
70
+
71
+ @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
72
+ end
73
+
74
+ def test_date_arg_passed_to_func_call
75
+ @func_args = [:date]
76
+ date = Date.civil(@year, @month, @day)
77
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, date).returns(date)
78
+
79
+ @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
80
+ end
81
+
82
+ def test_multiple_args_passed_to_func_call
83
+ @func_args = [:month, :day]
84
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @month, @day).returns(Date.civil(2016, 1, 15))
85
+
86
+ @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
87
+ end
88
+
89
+ def test_call_returns_error_if_target_function_returns_unknown_value
90
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns("bad-response")
91
+
92
+ assert_raises Holidays::InvalidFunctionResponse do
93
+ @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
94
+ end
95
+ end
96
+
97
+ def test_call_returns_date_with_modifier
98
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns(Date.civil(2016, 3, 10))
99
+
100
+ result = @subject.call(@year, @month, @day, @func_id, @func_args, @func_modifier)
101
+
102
+ assert_equal(Date.civil(2016, 3, 10) + @func_modifier, result)
103
+ end
104
+
105
+ def test_call_returns_date_no_modifier
106
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns(Date.civil(2016, 3, 10))
107
+
108
+ result = @subject.call(@year, @month, @day, @func_id, @func_args, nil)
109
+
110
+ assert_equal(Date.civil(2016, 3, 10), result)
111
+ end
112
+
113
+ def test_call_returns_errors_when_custom_function_returns_non_date
114
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns("bad")
115
+
116
+ assert_raises Holidays::InvalidFunctionResponse do
117
+ @subject.call(@year, @month, @day, @func_id, @func_args, nil)
118
+ end
119
+ end
120
+
121
+ def test_call_returns_error_when_custom_function_returns_mday_but_resulting_date_is_invalid
122
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns(32)
123
+
124
+ assert_raises Holidays::InvalidFunctionResponse do
125
+ @subject.call(@year, @month, @day, @func_id, @func_args, nil)
126
+ end
127
+ end
128
+
129
+ def test_call_returns_integer_returns_modified_date
130
+ @proc_result_cache_repo.expects(:lookup).at_most_once.with(@custom_func, @year).returns(7)
131
+
132
+ result = @subject.call(@year, @month, @day, @func_id, @func_args, nil)
133
+
134
+ assert_equal(Date.civil(2016, 1, 7), result)
135
+ end
136
+
137
+ def test_func_modifier_not_required
138
+ result = @subject.call(@year, @month, @day, @func_id, @func_args)
139
+ assert_equal(Date.civil(2016, 1, 15), result)
140
+ end
141
+
142
+ def test_validate_returns_error_if_year_not_a_number
143
+ assert_raises ArgumentError do
144
+ @subject.call("bad-year", @month, @day, @func_id, @func_args)
145
+ end
146
+ end
147
+
148
+ def test_validate_returns_error_if_month_not_valid
149
+ assert_raises ArgumentError do
150
+ @subject.call(@year, "bad-month", @day, @func_id, [:month])
151
+ end
152
+
153
+ assert_raises ArgumentError do
154
+ @subject.call(@year, -1, @day, @func_id, [:month])
155
+ end
156
+
157
+ assert_raises ArgumentError do
158
+ @subject.call(@year, 13, @day, @func_id, [:month])
159
+ end
160
+ end
161
+
162
+ def test_validate_returns_error_if_day_is_not_valid
163
+ assert_raises ArgumentError do
164
+ @subject.call(@year, @month, 0, @func_id, [:day])
165
+ end
166
+
167
+ assert_raises ArgumentError do
168
+ @subject.call(@year, @month, 32, @func_id, [:day])
169
+ end
170
+
171
+ assert_raises ArgumentError do
172
+ @subject.call(@year, @month, "bad-day", @func_id, [:day])
173
+ end
174
+ end
175
+ end