holidays 9.1.1 → 9.2.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +2 -1
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile +5 -0
  5. data/Makefile +17 -2
  6. data/README.md +1 -2
  7. data/Rakefile +28 -0
  8. data/holidays.gemspec +3 -3
  9. data/lib/generated_definitions/MANIFEST +2 -0
  10. data/lib/generated_definitions/REGIONS.rb +2 -2
  11. data/lib/generated_definitions/europe.rb +5 -5
  12. data/lib/generated_definitions/fr.rb +2 -2
  13. data/lib/generated_definitions/gb.rb +3 -3
  14. data/lib/generated_definitions/il.rb +74 -0
  15. data/lib/generated_definitions/mc.rb +36 -0
  16. data/lib/generated_definitions/northamerica.rb +3 -2
  17. data/lib/generated_definitions/us.rb +3 -2
  18. data/lib/holidays/definition/context/function_processor.rb +1 -1
  19. data/lib/holidays/definition/context/merger.rb +20 -4
  20. data/lib/holidays/definition/repository/custom_methods.rb +37 -9
  21. data/lib/holidays/finder/context/search.rb +43 -24
  22. data/lib/holidays/version.rb +1 -1
  23. data/lib/holidays.rb +9 -4
  24. data/test/coverage_report.rb +3 -5
  25. data/test/data/test_date_transform_conflict_region_1.yaml +14 -0
  26. data/test/data/test_date_transform_conflict_region_2.yaml +14 -0
  27. data/test/defs/test_defs_europe.rb +7 -3
  28. data/test/defs/test_defs_fr.rb +3 -3
  29. data/test/defs/test_defs_gb.rb +4 -0
  30. data/test/defs/test_defs_il.rb +35 -0
  31. data/test/defs/test_defs_mc.rb +43 -0
  32. data/test/defs/test_defs_northamerica.rb +6 -1
  33. data/test/defs/test_defs_us.rb +6 -1
  34. data/test/e2e/README.md +52 -0
  35. data/test/e2e/data/test_multiple_regions_with_conflicts_region_3.yaml +38 -0
  36. data/test/{integration → e2e}/test_holidays.rb +3 -2
  37. data/test/e2e/test_multiple_regions_with_conflict.rb +228 -0
  38. data/test/holidays/definition/context/test_function_processor.rb +2 -2
  39. data/test/holidays/definition/context/test_merger.rb +1 -1
  40. data/test/holidays/finder/context/test_search.rb +58 -0
  41. data/test/integration/README.md +45 -6
  42. data/test/integration/test_custom_holidays.rb +1 -1
  43. data/test/integration/test_custom_informal_holidays.rb +1 -1
  44. data/test/smoke/README.md +31 -0
  45. data/test/{integration → smoke}/test_available_regions.rb +0 -5
  46. data/test/smoke/test_smoke.rb +74 -0
  47. metadata +51 -33
  48. data/test/integration/test_multiple_regions_with_conflict.rb +0 -29
  49. /data/test/{data → e2e/data}/test_multiple_regions_with_conflicts_region_1.yaml +0 -0
  50. /data/test/{data → e2e/data}/test_multiple_regions_with_conflicts_region_2.yaml +0 -0
  51. /data/test/{integration → e2e}/test_all_regions.rb +0 -0
  52. /data/test/{integration → e2e}/test_any_holidays_during_work_week.rb +0 -0
  53. /data/test/{integration → e2e}/test_holidays_between.rb +0 -0
  54. /data/test/{integration → e2e}/test_multiple_regions.rb +0 -0
  55. /data/test/{integration → e2e}/test_nonstandard_regions.rb +0 -0
  56. /data/test/{data → integration/data}/test_custom_govt_holiday_defs.yaml +0 -0
  57. /data/test/{data → integration/data}/test_custom_informal_holidays_defs.yaml +0 -0
  58. /data/test/{data → integration/data}/test_multiple_custom_holiday_defs.yaml +0 -0
@@ -24,14 +24,19 @@ module Holidays
24
24
  next unless @rules[:year_range].call(year, h[:year_ranges])
25
25
  end
26
26
 
27
- date = build_date(year, month, h)
28
- next unless date
29
-
30
- if observed_set?(options) && h[:observed]
31
- date = build_observed_date(date, regions, h)
27
+ dates = if h[:function]
28
+ custom_holidays(year, month, h, regions)
29
+ else
30
+ date = build_date(year, month, h, regions)
31
+ date ? [date] : []
32
32
  end
33
33
 
34
- holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
34
+ dates.each do |d|
35
+ if observed_set?(options) && h[:observed]
36
+ d = build_observed_date(d, regions, h)
37
+ end
38
+ holidays << {:date => d, :name => h[:name], :regions => h[:regions]}
39
+ end
35
40
  end
36
41
  end
37
42
  end
@@ -68,36 +73,50 @@ module Holidays
68
73
  options && options.include?(:observed) == true
69
74
  end
70
75
 
71
- def build_date(year, month, h)
72
- if h[:function]
73
- holiday = custom_holiday(year, month, h)
74
- #FIXME The result should always be present, see https://github.com/holidays/holidays/issues/204 for more information
75
- current_month = holiday&.month
76
- current_day = holiday&.mday
77
- else
78
- current_month = month
79
- current_day = h[:mday] || @day_of_month_calculator.call(year, month, h[:week], h[:wday])
80
- end
76
+ def build_date(year, month, h, queried_regions)
77
+ current_month = month
78
+ current_day = h[:mday] || @day_of_month_calculator.call(year, month, h[:week], h[:wday])
81
79
 
82
80
  # Silently skip bad mdays
83
81
  #TODO Should we be doing something different here? We have no concept of logging right now. Maybe we should add it?
84
82
  Date.civil(year, current_month, current_day) rescue nil
85
83
  end
86
84
 
87
- def custom_holiday(year, month, h)
88
- @custom_method_processor.call(
89
- #FIXME This seems like a bug, we seem to expect the day in here in the au defs?
90
- build_custom_method_input(year, month, h[:mday], h[:regions]),
91
- h[:function], h[:function_arguments], h[:function_modifier],
92
- )
85
+ def custom_holidays(year, month, h, queried_regions)
86
+ effective_regions = queried_regions & h[:regions]
87
+ effective_regions = [queried_regions.first] if effective_regions.empty?
88
+
89
+ effective_regions.each_with_object([]) do |region, dates|
90
+ result = @custom_method_processor.call(
91
+ { year: year, month: month, day: h[:mday], region: region },
92
+ h[:function], h[:function_arguments], h[:function_modifier],
93
+ )
94
+
95
+ #FIXME The result should always be present, see https://github.com/holidays/holidays/issues/204 for more information
96
+ next if result.nil?
97
+
98
+ date = Date.civil(year, result.month, result.mday) rescue nil
99
+ dates << date if date
100
+ end.uniq
93
101
  end
94
102
 
95
- def build_custom_method_input(year, month, day, regions)
103
+ # When the queried region is :any (or no holiday_regions are provided), fall back
104
+ # to the holiday's own first region. Otherwise use the first queried region that
105
+ # also appears in the holiday's region list so that region-specific function
106
+ # implementations resolve correctly even when a holiday definition is shared across
107
+ # multiple regions.
108
+ def build_custom_method_input(year, month, day, queried_regions, holiday_regions = nil)
109
+ effective_region = if holiday_regions.nil? || queried_regions.include?(:any)
110
+ queried_regions.first
111
+ else
112
+ (queried_regions & holiday_regions).first || holiday_regions.first
113
+ end
114
+
96
115
  {
97
116
  year: year,
98
117
  month: month,
99
118
  day: day,
100
- region: regions.first, #FIXME This isn't ideal but will work for our current use case...
119
+ region: effective_region,
101
120
  }
102
121
  end
103
122
 
@@ -1,3 +1,3 @@
1
1
  module Holidays
2
- VERSION = '9.1.1'
2
+ VERSION = '9.2.0'
3
3
  end
data/lib/holidays.rb CHANGED
@@ -42,9 +42,8 @@ module Holidays
42
42
 
43
43
  raise ArgumentError if end_date < start_date
44
44
 
45
- if cached_holidays = Factory::Definition.cache_repository.find(start_date, end_date, options)
46
- return cached_holidays
47
- end
45
+ cached_holidays = Factory::Definition.cache_repository.find(start_date, end_date, options)
46
+ return cached_holidays unless cached_holidays.nil?
48
47
 
49
48
  Factory::Finder.between.call(start_date, end_date, options)
50
49
  end
@@ -93,11 +92,17 @@ module Holidays
93
92
  def load_custom(*files)
94
93
  regions, rules_by_month, custom_methods, _ = Factory::Definition.file_parser.parse_definition_files(files)
95
94
 
95
+ # Capture source code before converting entities to Procs so the merger
96
+ # can detect genuine conflicts (same name, different logic).
97
+ method_sources = custom_methods.each_with_object({}) do |(key, entity), h|
98
+ h[key] = entity.source
99
+ end
100
+
96
101
  custom_methods.each do |method_key, method_entity|
97
102
  custom_methods[method_key] = Factory::Definition.custom_method_proc_decorator.call(method_entity)
98
103
  end
99
104
 
100
- Factory::Definition.merger.call(regions, rules_by_month, custom_methods)
105
+ Factory::Definition.merger.call(regions, rules_by_month, custom_methods, method_sources)
101
106
 
102
107
  rules_by_month
103
108
  end
@@ -1,10 +1,8 @@
1
1
  require 'simplecov'
2
2
 
3
- # For reasons I don't understand jruby implementations report lower coverage
4
- # than other ruby versions. Ruby 2.5.3, for instance, is at 92%.
5
- #
6
- # We set the floor based on jruby so that all automated tests pass on Travis CI.
7
- SimpleCov.minimum_coverage 89
3
+ # JRuby coverage reporting is inaccurate without --debug mode, resulting in
4
+ # artificially low numbers. Skip the minimum coverage check under JRuby.
5
+ SimpleCov.minimum_coverage 90 unless RUBY_PLATFORM == 'java' || RUBY_PATCHLEVEL == -1 || ENV['SMOKE_TEST'] || ENV['CONTRACT_TEST']
8
6
 
9
7
  SimpleCov.add_filter [
10
8
  # Apparently simplecov doesn't automatically filter 'spec' or 'test' so we
@@ -0,0 +1,14 @@
1
+ months:
2
+ 1:
3
+ - name: Weekend Holiday
4
+ regions: [date_transform_conflict_1]
5
+ mday: 1
6
+ function: to_nearest_upcoming_weekend_day(date)
7
+
8
+ methods:
9
+ to_nearest_upcoming_weekend_day:
10
+ arguments: date
11
+ ruby: |
12
+ days_until = (6 - date.wday) % 7
13
+ days_until = 7 if days_until == 0
14
+ date + days_until
@@ -0,0 +1,14 @@
1
+ months:
2
+ 1:
3
+ - name: Weekend Holiday
4
+ regions: [date_transform_conflict_2]
5
+ mday: 1
6
+ function: to_nearest_upcoming_weekend_day(date)
7
+
8
+ methods:
9
+ to_nearest_upcoming_weekend_day:
10
+ arguments: date
11
+ ruby: |
12
+ days_until = (7 - date.wday) % 7
13
+ days_until = 7 if days_until == 0
14
+ date + days_until
@@ -413,7 +413,7 @@ class EuropeDefinitionTests < Test::Unit::TestCase # :nodoc:
413
413
 
414
414
  assert_equal "Ascension", (Holidays.on(Date.civil(2007, 5, 17), [:fr])[0] || {})[:name]
415
415
 
416
- assert_equal "Pentecôte", (Holidays.on(Date.civil(2007, 5, 27), [:fr])[0] || {})[:name]
416
+ assert_nil (Holidays.on(Date.civil(2007, 5, 27), [:fr])[0] || {})[:name]
417
417
 
418
418
  assert_equal "Fête nationale", (Holidays.on(Date.civil(2007, 7, 14), [:fr])[0] || {})[:name]
419
419
 
@@ -427,11 +427,11 @@ class EuropeDefinitionTests < Test::Unit::TestCase # :nodoc:
427
427
 
428
428
  assert_nil (Holidays.on(Date.civil(2007, 4, 8), [:fr])[0] || {})[:name]
429
429
 
430
- assert_nil (Holidays.on(Date.civil(2007, 5, 28), [:fr])[0] || {})[:name]
430
+ assert_equal "Lundi de Pentecôte", (Holidays.on(Date.civil(2007, 5, 28), [:fr])[0] || {})[:name]
431
431
 
432
432
  assert_equal "Pâques", (Holidays.on(Date.civil(2007, 4, 8), [:fr], [:informal])[0] || {})[:name]
433
433
 
434
- assert_equal "Lundi de Pentecôte", (Holidays.on(Date.civil(2007, 5, 28), [:fr], [:informal])[0] || {})[:name]
434
+ assert_equal "Pentecôte", (Holidays.on(Date.civil(2007, 5, 27), [:fr], [:informal])[0] || {})[:name]
435
435
 
436
436
  assert_equal "Saint-Étienne", (Holidays.on(Date.civil(2017, 12, 26), [:fr_a, :fr_m], [:informal])[0] || {})[:name]
437
437
 
@@ -575,6 +575,10 @@ class EuropeDefinitionTests < Test::Unit::TestCase # :nodoc:
575
575
 
576
576
  assert_equal "Bank Holiday for the Coronation of King Charles III", (Holidays.on(Date.civil(2023, 5, 8), [:gb])[0] || {})[:name]
577
577
 
578
+ assert_equal "Easter Monday", (Holidays.on(Date.civil(2016, 3, 28), [:gb_con])[0] || {})[:name]
579
+
580
+ assert_equal "Bank Holiday", (Holidays.on(Date.civil(2016, 8, 29), [:gb_con])[0] || {})[:name]
581
+
578
582
  assert_nil (Holidays.on(Date.civil(2024, 5, 8), [:gb])[0] || {})[:name]
579
583
 
580
584
  assert_nil (Holidays.on(Date.civil(2022, 5, 8), [:gb])[0] || {})[:name]
@@ -17,7 +17,7 @@ class FrDefinitionTests < Test::Unit::TestCase # :nodoc:
17
17
 
18
18
  assert_equal "Ascension", (Holidays.on(Date.civil(2007, 5, 17), [:fr])[0] || {})[:name]
19
19
 
20
- assert_equal "Pentecôte", (Holidays.on(Date.civil(2007, 5, 27), [:fr])[0] || {})[:name]
20
+ assert_nil (Holidays.on(Date.civil(2007, 5, 27), [:fr])[0] || {})[:name]
21
21
 
22
22
  assert_equal "Fête nationale", (Holidays.on(Date.civil(2007, 7, 14), [:fr])[0] || {})[:name]
23
23
 
@@ -31,11 +31,11 @@ class FrDefinitionTests < Test::Unit::TestCase # :nodoc:
31
31
 
32
32
  assert_nil (Holidays.on(Date.civil(2007, 4, 8), [:fr])[0] || {})[:name]
33
33
 
34
- assert_nil (Holidays.on(Date.civil(2007, 5, 28), [:fr])[0] || {})[:name]
34
+ assert_equal "Lundi de Pentecôte", (Holidays.on(Date.civil(2007, 5, 28), [:fr])[0] || {})[:name]
35
35
 
36
36
  assert_equal "Pâques", (Holidays.on(Date.civil(2007, 4, 8), [:fr], [:informal])[0] || {})[:name]
37
37
 
38
- assert_equal "Lundi de Pentecôte", (Holidays.on(Date.civil(2007, 5, 28), [:fr], [:informal])[0] || {})[:name]
38
+ assert_equal "Pentecôte", (Holidays.on(Date.civil(2007, 5, 27), [:fr], [:informal])[0] || {})[:name]
39
39
 
40
40
  assert_equal "Saint-Étienne", (Holidays.on(Date.civil(2017, 12, 26), [:fr_a, :fr_m], [:informal])[0] || {})[:name]
41
41
 
@@ -147,6 +147,10 @@ class GbDefinitionTests < Test::Unit::TestCase # :nodoc:
147
147
 
148
148
  assert_equal "Bank Holiday for the Coronation of King Charles III", (Holidays.on(Date.civil(2023, 5, 8), [:gb])[0] || {})[:name]
149
149
 
150
+ assert_equal "Easter Monday", (Holidays.on(Date.civil(2016, 3, 28), [:gb_con])[0] || {})[:name]
151
+
152
+ assert_equal "Bank Holiday", (Holidays.on(Date.civil(2016, 8, 29), [:gb_con])[0] || {})[:name]
153
+
150
154
  assert_nil (Holidays.on(Date.civil(2024, 5, 8), [:gb])[0] || {})[:name]
151
155
 
152
156
  assert_nil (Holidays.on(Date.civil(2022, 5, 8), [:gb])[0] || {})[:name]
@@ -0,0 +1,35 @@
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/il.yaml
7
+ class IlDefinitionTests < Test::Unit::TestCase # :nodoc:
8
+
9
+ def test_il
10
+ assert_equal "Exodus from Egypt", (Holidays.on(Date.civil(2024, 3, 23), [:il])[0] || {})[:name]
11
+
12
+ assert_equal "Exodus from Egypt (day 7)", (Holidays.on(Date.civil(2023, 3, 12), [:il])[0] || {})[:name]
13
+
14
+ assert_equal "Independence Day", (Holidays.on(Date.civil(2025, 4, 1), [:il])[0] || {})[:name]
15
+
16
+ assert_equal "Thanksgiving", (Holidays.on(Date.civil(2026, 4, 22), [:il])[0] || {})[:name]
17
+
18
+ assert_equal "Thanksgiving (day 2)", (Holidays.on(Date.civil(2024, 5, 13), [:il])[0] || {})[:name]
19
+
20
+ assert_equal "Jewish New Year", (Holidays.on(Date.civil(2023, 9, 16), [:il])[0] || {})[:name]
21
+
22
+ assert_equal "2nd New Year's Day", (Holidays.on(Date.civil(2025, 9, 24), [:il])[0] || {})[:name]
23
+
24
+ assert_equal "Day of Atonement", (Holidays.on(Date.civil(2026, 9, 21), [:il])[0] || {})[:name]
25
+
26
+ assert_equal "Feast of Tabernacles", (Holidays.on(Date.civil(2024, 10, 17), [:il])[0] || {})[:name]
27
+
28
+ assert_equal "Rejoicing of the Torah", (Holidays.on(Date.civil(2025, 10, 15), [:il])[0] || {})[:name]
29
+
30
+ assert_nil (Holidays.on(Date.civil(2024, 7, 9), [:il])[0] || {})[:name]
31
+
32
+ assert_nil (Holidays.on(Date.civil(2026, 8, 15), [:il])[0] || {})[:name]
33
+
34
+ end
35
+ end
@@ -0,0 +1,43 @@
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/mc.yaml
7
+ class McDefinitionTests < Test::Unit::TestCase # :nodoc:
8
+
9
+ def test_mc
10
+ assert_equal "Jour de l'an", (Holidays.on(Date.civil(2026, 1, 1), [:mc])[0] || {})[:name]
11
+
12
+ assert_equal "Sainte Dévote", (Holidays.on(Date.civil(2026, 1, 27), [:mc])[0] || {})[:name]
13
+
14
+ assert_equal "Lundi de Pâques", (Holidays.on(Date.civil(2026, 4, 6), [:mc])[0] || {})[:name]
15
+
16
+ assert_equal "Fête du Travail", (Holidays.on(Date.civil(2026, 5, 1), [:mc])[0] || {})[:name]
17
+
18
+ assert_equal "Ascension", (Holidays.on(Date.civil(2026, 5, 14), [:mc])[0] || {})[:name]
19
+
20
+ assert_equal "Lundi de Pentecôte", (Holidays.on(Date.civil(2026, 5, 25), [:mc])[0] || {})[:name]
21
+
22
+ assert_equal "Fête Dieu", (Holidays.on(Date.civil(2026, 6, 4), [:mc])[0] || {})[:name]
23
+
24
+ assert_equal "Assomption", (Holidays.on(Date.civil(2026, 8, 15), [:mc])[0] || {})[:name]
25
+
26
+ assert_equal "Toussaint", (Holidays.on(Date.civil(2026, 11, 1), [:mc])[0] || {})[:name]
27
+
28
+ assert_equal "Fête du Prince", (Holidays.on(Date.civil(2026, 11, 19), [:mc])[0] || {})[:name]
29
+
30
+ assert_equal "Immaculée Conception", (Holidays.on(Date.civil(2026, 12, 8), [:mc])[0] || {})[:name]
31
+
32
+ assert_equal "Noël", (Holidays.on(Date.civil(2026, 12, 25), [:mc])[0] || {})[:name]
33
+
34
+ assert_equal "Immaculée Conception", (Holidays.on(Date.civil(2024, 12, 8), [:mc])[0] || {})[:name]
35
+
36
+ assert_equal "Immaculée Conception", (Holidays.on(Date.civil(2024, 12, 9), [:mc], [:observed])[0] || {})[:name]
37
+
38
+ assert_equal "Toussaint", (Holidays.on(Date.civil(2026, 11, 1), [:mc])[0] || {})[:name]
39
+
40
+ assert_equal "Toussaint", (Holidays.on(Date.civil(2026, 11, 2), [:mc], [:observed])[0] || {})[:name]
41
+
42
+ end
43
+ end
@@ -443,7 +443,12 @@ assert_equal "Confederate Memorial Day", (Holidays.on(Date.civil(2026, 4, 27), [
443
443
  assert_nil (Holidays.on(Date.civil(2021, 4, 26), [:us])[0] || {})[:name]
444
444
 
445
445
  assert_equal "State Holiday", (Holidays.on(Date.civil(2015, 4, 20), [:us_ga])[0] || {})[:name]
446
- assert_equal "State Holiday", (Holidays.on(Date.civil(2021, 4, 26), [:us_ga])[0] || {})[:name]
446
+
447
+ assert_equal "State Holiday", (Holidays.on(Date.civil(2020, 4, 10), [:us_ga])[0] || {})[:name]
448
+ assert_equal "State Holiday", (Holidays.on(Date.civil(2021, 4, 2), [:us_ga])[0] || {})[:name]
449
+ assert_equal "State Holiday", (Holidays.on(Date.civil(2022, 4, 15), [:us_ga])[0] || {})[:name]
450
+
451
+ assert_nil (Holidays.on(Date.civil(2021, 4, 26), [:us_ga])[0] || {})[:name]
447
452
 
448
453
  assert_equal "Arbor Day", (Holidays.on(Date.civil(2017, 4, 28), [:us_ne])[0] || {})[:name]
449
454
  assert_equal "Arbor Day", (Holidays.on(Date.civil(2025, 4, 25), [:us_ne])[0] || {})[:name]
@@ -163,7 +163,12 @@ assert_equal "Confederate Memorial Day", (Holidays.on(Date.civil(2026, 4, 27), [
163
163
  assert_nil (Holidays.on(Date.civil(2021, 4, 26), [:us])[0] || {})[:name]
164
164
 
165
165
  assert_equal "State Holiday", (Holidays.on(Date.civil(2015, 4, 20), [:us_ga])[0] || {})[:name]
166
- assert_equal "State Holiday", (Holidays.on(Date.civil(2021, 4, 26), [:us_ga])[0] || {})[:name]
166
+
167
+ assert_equal "State Holiday", (Holidays.on(Date.civil(2020, 4, 10), [:us_ga])[0] || {})[:name]
168
+ assert_equal "State Holiday", (Holidays.on(Date.civil(2021, 4, 2), [:us_ga])[0] || {})[:name]
169
+ assert_equal "State Holiday", (Holidays.on(Date.civil(2022, 4, 15), [:us_ga])[0] || {})[:name]
170
+
171
+ assert_nil (Holidays.on(Date.civil(2021, 4, 26), [:us_ga])[0] || {})[:name]
167
172
 
168
173
  assert_equal "Arbor Day", (Holidays.on(Date.civil(2017, 4, 28), [:us_ne])[0] || {})[:name]
169
174
  assert_equal "Arbor Day", (Holidays.on(Date.civil(2025, 4, 25), [:us_ne])[0] || {})[:name]
@@ -0,0 +1,52 @@
1
+ # End-to-end tests
2
+
3
+ End-user behavioral checks against the **real upstream definitions** in
4
+ `/definitions`. These tests load actual region data and assert specific
5
+ holiday names, dates, counts, and observed-date behavior, the kinds of
6
+ expectations a consumer of the gem would care about.
7
+
8
+ ## Purpose
9
+
10
+ These tests verify that the full pipeline, from upstream YAML through
11
+ generation through gem processing to the public API, produces the results
12
+ an end user expects for real-world regions. They complement fixture-based
13
+ integration tests by catching content regressions in the gem's interaction
14
+ with real definitions.
15
+
16
+ ## E2E tests vs. integration vs. smoke
17
+
18
+ - **Smoke** (`test/smoke/`): only asserts nothing crashes. Run from the
19
+ upstream `holidays/definitions` CI.
20
+ - **Integration** (`test/integration/`): verifies gem-wide behavior using
21
+ controlled fixture YAMLs. Stable across definitions changes.
22
+ - **E2E** (this directory): verifies end-user-perceived behavior using real
23
+ upstream definitions. Expected to break when upstream definitions change,
24
+ on purpose.
25
+
26
+ ## Important
27
+
28
+ These tests are tightly coupled to the contents of `/definitions`. When you
29
+ update the definitions submodule and an upstream change shifts a holiday name,
30
+ date, or count, the corresponding e2e test must be updated to match. That is
31
+ working as intended: the test is the gem's record of what end users see, so
32
+ updating it is part of accepting the upstream change.
33
+
34
+ This directory is **not** run by the upstream `holidays/definitions` CI. Doing
35
+ so would create a dependency loop where the definitions repo cannot ship a
36
+ legitimate content change without also updating tests in this repo first.
37
+
38
+ ## What belongs here
39
+
40
+ - Tests that assert specific holidays exist on specific dates for specific
41
+ regions using real definitions (e.g. `:ca` returns "Labour Day" on Sept 1, 2008).
42
+ - Tests of observed-date behavior against real region rules.
43
+ - Tests of sub-region inheritance, wildcards (`:ca_`), `:any`, and cross-region
44
+ conflicts using real data.
45
+ - Region-count or holiday-count assertions for specific year/region combinations.
46
+
47
+ ## What does NOT belong here
48
+
49
+ - Tests that don't depend on real definitions content. Those belong in
50
+ `integration/` (with fixtures) or `smoke/` (structural only).
51
+ - Tests of a single file or class in isolation. Those are unit tests and
52
+ belong next to the code they cover.
@@ -0,0 +1,38 @@
1
+ months:
2
+ 0:
3
+ - name: With Function Modifier
4
+ regions: [multiple_with_conflict_3]
5
+ function: easter(year)
6
+ function_modifier: 70
7
+ - name: With Function Only Different Function Name
8
+ regions: [multiple_with_conflict_3]
9
+ function: conflict_custom_method_3(year)
10
+ - name: With Function Only Same Function Name
11
+ regions: [multiple_with_conflict_3]
12
+ function: conflict_custom_method_identical_name_between_regions(year)
13
+ - name: With Function Only Same Function Name - Region 3
14
+ regions: [multiple_with_conflict_3]
15
+ function: conflict_custom_method_identical_name_between_regions_but_different_holiday_names(year)
16
+ 1:
17
+ - name: New Year's Day
18
+ regions: [multiple_with_conflict_3]
19
+ mday: 1
20
+ observed: to_monday_if_weekend(date)
21
+ 10:
22
+ - name: Testing Conflict Month 10
23
+ regions: [multiple_with_conflict_3]
24
+ mday: 9
25
+
26
+ methods:
27
+ conflict_custom_method_3:
28
+ arguments: year
29
+ ruby: |
30
+ Date.civil(year, 4, 1)
31
+ conflict_custom_method_identical_name_between_regions:
32
+ arguments: year
33
+ ruby: |
34
+ Date.civil(year, 3, 1)
35
+ conflict_custom_method_identical_name_between_regions_but_different_holiday_names:
36
+ arguments: year
37
+ ruby: |
38
+ Date.civil(year, 3, 15)
@@ -237,7 +237,8 @@ class HolidaysTests < Test::Unit::TestCase
237
237
  end
238
238
 
239
239
  def test_load_all
240
- Holidays.load_all
241
- assert_equal 286, Holidays.available_regions.count
240
+ assert_nothing_raised do
241
+ Holidays.load_all
242
+ end
242
243
  end
243
244
  end