holidays 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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