Skip to content

Commit

Permalink
Fix behavior of Cycles::EndOf
Browse files Browse the repository at this point in the history
EndOf#expiration_of(...) always returns #final_date
  • Loading branch information
jdowd committed Sep 1, 2024
1 parent ecc0e0f commit a09df12
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 43 deletions.
34 changes: 34 additions & 0 deletions lib/sof/cycles/end_of.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# frozen_string_literal: true

# Captures the logic for enforcing the EndOf cycle variant
# E.g. "V1E18MF2020-01-05" means:
# You're good until the end of the 17th subsequent month from 2020-01-05.
# Complete 1 by that date to reset the cycle.
#
# Some of the calculations are quite different from other cycles.
# Whereas other cycles look at completion dates to determine if the cycle is
# satisfied, this cycle checks whether the anchor date is prior to the final date.
module SOF
module Cycles
class EndOf < Cycle
Expand All @@ -16,6 +24,32 @@ def to_s
"#{volume}x by #{final_date.to_fs(:american)}"
end

# Returns the expiration date for the cycle
#
# @param [nil] _ Unused parameter, maintained for compatibility
# @param anchor [nil] _ Unused parameter, maintained for compatibility
# @return [Date] The final date of the cycle
#
# @example
# Cycle.for("V1E18MF2020-01-09")
# .expiration_of(anchor: "2020-06-04".to_date)
# # => #<Date: 2021-06-30>
def expiration_of(_ = nil, anchor: nil) = final_date

# Is the supplied anchor date prior to the final date?
#
# @return [Boolean] true if the cycle is satisfied, false otherwise
def satisfied_by?(_ = nil, anchor: Date.current) = anchor <= final_date

# Calculates the final date of the cycle
#
# @param [nil] _ Unused parameter, maintained for compatibility
# @return [Date] The final date of the cycle calculated as the end of the
# nth subsequent period after the FROM date, where n = (period count - 1)
#
# @example
# Cycle.for("V1E18MF2020-01-09").final_date
# # => #<Date: 2021-06-30>
def final_date(_ = nil) = time_span
.end_date(start_date - 1.send(period))
.end_of_month
Expand Down
87 changes: 44 additions & 43 deletions spec/sof/cycles/end_of_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,7 @@ module SOF
let(:end_date) { (from_date.to_date + 17.months).end_of_month }
let(:from_date) { "2020-01-01" }

let(:completed_dates) do
[
recent_date,
middle_date,
early_date,
early_date,
too_early_date,
too_late_date
]
end
let(:recent_date) { from_date.to_date + 17.months }
let(:middle_date) { from_date.to_date + 2.months }
let(:early_date) { from_date.to_date + 1.month }
let(:too_early_date) { from_date.to_date - 1.day }
let(:too_late_date) { end_date + 1.day }
let(:completed_dates) { [] }

it_behaves_like "#kind returns", :end_of
it_behaves_like "#valid_periods are", %w[W M Q Y]
Expand Down Expand Up @@ -55,6 +41,23 @@ module SOF
given: nil, returns: ("2020-01-01".to_date + 17.months).end_of_month

describe "#covered_dates" do
let(:completed_dates) do
[
recent_date,
middle_date,
early_date,
early_date,
too_early_date,
too_late_date
]
end
let(:recent_date) { from_date.to_date + 17.months }
let(:middle_date) { from_date.to_date + 2.months }
let(:early_date) { from_date.to_date + 1.month }
let(:too_early_date) { from_date.to_date - 1.day }
let(:too_late_date) { end_date + 1.day }

let(:anchor) { "2021-06-29".to_date }
it "given an anchor date, returns dates that fall within it's window" do
expect(cycle.covered_dates(completed_dates, anchor:)).to eq([
recent_date,
Expand All @@ -65,56 +68,54 @@ module SOF
end
end

describe "#satisfied_by?(completed_dates, anchor:)" do
context "when the completions--judged from the <from_date>--satisfy the cycle" do
describe "#satisfied_by?(anchor:)" do
context "when the anchor date is < the final date" do
let(:anchor) { "2021-06-29".to_date }

it "returns true" do
expect(cycle).to be_satisfied_by(completed_dates, anchor:)
expect(cycle).to be_satisfied_by(anchor:)
end
end

context "when the completions are irrelevant to the given from_date" do
let(:completed_dates) do
[
10.years.ago,
Date.current,
10.years.from_now
]
end
context "when the anchor date is = the final date" do
let(:anchor) { "2021-06-30".to_date }

it "returns false" do
expect(cycle).not_to be_satisfied_by(completed_dates, anchor:)
it "returns true" do
expect(cycle).to be_satisfied_by(anchor:)
end
end

context "when the completions currently do not satisfy the cycle" do
let(:notation) { "V5E18M" }
context "when the anchor date is > the final date" do
let(:anchor) { "2021-07-01".to_date }

it "returns false" do
expect(cycle).not_to be_satisfied_by(completed_dates, anchor:)
end
end
end

context "when there are no completions" do
let(:completed_dates) { [] }
describe "#expiration_of(completion_dates)" do
context "when the anchor date is < the final date" do
let(:anchor) { "2021-06-29".to_date }

it "returns false" do
expect(cycle).not_to be_satisfied_by(completed_dates, anchor:)
it "returns the final date" do
expect(cycle.expiration_of(anchor:)).to eq "2021-06-30".to_date
end
end
end

describe "#expiration_of(completion_dates)" do
context "when the completions currently satisfy the cycle" do
it "returns the date on which the completions will no longer satisfy the cycle" do
expect(cycle.expiration_of(completed_dates)).to be nil
context "when the anchor date = the final date" do
let(:anchor) { "2021-06-30".to_date }

it "returns the final date" do
expect(cycle.expiration_of(anchor:)).to eq "2021-06-30".to_date
end
end

context "when the completions currently do not satisfy the cycle" do
let(:notation) { "V5E18M" }
context "when the anchor date > the final date" do
let(:anchor) { "2021-07-31".to_date }

it "returns nil" do
expect(cycle.expiration_of(completed_dates)).to be_nil
it "returns the final date" do
expect(cycle.expiration_of(anchor:)).to eq "2021-06-30".to_date
end
end
end
Expand Down

0 comments on commit a09df12

Please sign in to comment.