Skip to content

Commit 25ba960

Browse files
chore(validation): Refactor Expiration Date Validation to Use Centralized Validator (#3312)
1 parent 4c6441e commit 25ba960

File tree

7 files changed

+123
-52
lines changed

7 files changed

+123
-52
lines changed

app/services/coupons/validate_service.rb

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,9 @@ def valid?
1616
private
1717

1818
def valid_expiration_at?
19-
return true if args[:expiration_at].blank?
20-
21-
expiration_at = if args[:expiration_at].is_a?(Time)
22-
args[:expiration_at]
23-
elsif args[:expiration_at].is_a?(String) && Date._strptime(args[:expiration_at]).present?
24-
Date.strptime(args[:expiration_at])
25-
end
26-
27-
return true if expiration_at && expiration_at.to_date >= Time.current.to_date
19+
return true if Validators::ExpirationDateValidator.valid?(args[:expiration_at])
2820

2921
add_error(field: :expiration_at, error_code: "invalid_date")
30-
3122
false
3223
end
3324
end

app/services/utils/datetime.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ def self.valid_format?(datetime)
88
false
99
end
1010

11+
def self.future_date?(datetime)
12+
return true if datetime.is_a?(ActiveSupport::TimeWithZone) && datetime.future?
13+
return false unless valid_format?(datetime)
14+
15+
parsed_date = Time.zone.parse(datetime.to_s)
16+
parsed_date&.future? || false
17+
end
18+
1119
def self.date_diff_with_timezone(from_datetime, to_datetime, timezone)
1220
from = from_datetime
1321
from = Time.zone.parse(from.to_s) unless from.is_a?(ActiveSupport::TimeWithZone)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
module Validators
4+
module ExpirationDateValidator
5+
def self.valid?(expiration_at)
6+
return true if expiration_at.blank?
7+
8+
Utils::Datetime.valid_format?(expiration_at) &&
9+
Utils::Datetime.future_date?(expiration_at)
10+
end
11+
end
12+
end

app/services/wallets/update_service.rb

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,9 @@ def valid_recurring_transaction_rules?
4646
end
4747

4848
def valid_expiration_at?(expiration_at:)
49-
return true if expiration_at.blank?
50-
51-
if Utils::Datetime.valid_format?(expiration_at)
52-
parsed_expiration_at = if expiration_at.is_a?(String)
53-
DateTime.strptime(expiration_at)
54-
else
55-
expiration_at
56-
end
57-
58-
return true if parsed_expiration_at.to_date > Time.current.to_date
59-
end
49+
return true if Validators::ExpirationDateValidator.valid?(expiration_at)
6050

6151
result.single_validation_failure!(field: :expiration_at, error_code: "invalid_date")
62-
6352
false
6453
end
6554
end

app/services/wallets/validate_service.rb

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,12 @@ def valid_granted_credits_amount?
4848
end
4949

5050
def valid_expiration_at?
51-
return true if args[:expiration_at].blank?
52-
53-
future = Utils::Datetime.valid_format?(args[:expiration_at]) && expiration_at.to_date > Time.current.to_date
54-
return true if future
51+
return true if Validators::ExpirationDateValidator.valid?(args[:expiration_at])
5552

5653
add_error(field: :expiration_at, error_code: "invalid_date")
57-
5854
false
5955
end
6056

61-
def expiration_at
62-
@expiration_at ||= if args[:expiration_at].is_a?(String)
63-
DateTime.strptime(args[:expiration_at])
64-
else
65-
args[:expiration_at]
66-
end
67-
end
68-
6957
def valid_recurring_transaction_rules?
7058
if args[:recurring_transaction_rules].count > 1
7159
return add_error(field: :recurring_transaction_rules, error_code: "invalid_number_of_recurring_rules")
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# frozen_string_literal: true
2+
3+
require "rails_helper"
4+
5+
RSpec.describe Validators::ExpirationDateValidator do
6+
describe ".valid?" do
7+
subject { described_class.valid?(expiration_at) }
8+
9+
context "when expiration_at is blank" do
10+
let(:expiration_at) { nil }
11+
12+
it { is_expected.to be true }
13+
end
14+
15+
context "when expiration_at is an empty string" do
16+
let(:expiration_at) { "" }
17+
18+
it { is_expected.to be true }
19+
end
20+
21+
context "when expiration_at is an invalid format" do
22+
let(:expiration_at) { "invalid-date" }
23+
24+
it { is_expected.to be false }
25+
end
26+
27+
context "when expiration_at is an integer" do
28+
let(:expiration_at) { 123 }
29+
30+
it { is_expected.to be false }
31+
end
32+
33+
context "when expiration_at is a past date" do
34+
let(:expiration_at) { (Time.current - 1.day).iso8601 }
35+
36+
it { is_expected.to be false }
37+
end
38+
39+
context "when expiration_at is a past datetime" do
40+
let(:expiration_at) { (Time.current - 1.hour).iso8601 }
41+
42+
it { is_expected.to be false }
43+
end
44+
45+
context "when expiration_at is today but not in the future" do
46+
let(:expiration_at) { Time.current.beginning_of_day.iso8601 }
47+
48+
it { is_expected.to be false }
49+
end
50+
51+
context "when expiration_at is a valid future date" do
52+
let(:expiration_at) { (Time.current + 1.day).iso8601 }
53+
54+
it { is_expected.to be true }
55+
end
56+
57+
context "when expiration_at is a valid future datetime" do
58+
let(:expiration_at) { (Time.current + 1.hour).iso8601 }
59+
60+
it { is_expected.to be true }
61+
end
62+
63+
context "when expiration_at is an ActiveSupport::TimeWithZone object in the future" do
64+
let(:expiration_at) { Time.zone.now + 1.day }
65+
66+
it { is_expected.to be true }
67+
end
68+
69+
context "when expiration_at is an ActiveSupport::TimeWithZone object in the past" do
70+
let(:expiration_at) { Time.zone.now - 1.day }
71+
72+
it { is_expected.to be false }
73+
end
74+
end
75+
end

spec/services/wallets/validate_service_spec.rb

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
end
8585
end
8686

87-
context "when expiration_at is integer" do
87+
context "when expiration_at is an integer" do
8888
let(:expiration_at) { 123 }
8989

9090
it "returns false and result has errors" do
@@ -93,34 +93,42 @@
9393
end
9494
end
9595

96-
context "when expiration_at is less than current time" do
97-
let(:expiration_at) { (Time.current - 1.year).iso8601 }
96+
context "when expiration_at is in the past" do
97+
let(:expiration_at) { (Time.current - 1.hour).iso8601 }
9898

9999
it "returns false and result has errors" do
100100
expect(validate_service).not_to be_valid
101101
expect(result.error.messages[:expiration_at]).to eq(["invalid_date"])
102102
end
103103
end
104104

105-
context "with invalid transaction metadata" do
106-
let(:args) do
107-
{
108-
customer:,
109-
organization_id: organization.id,
110-
paid_credits:,
111-
granted_credits:,
112-
expiration_at:,
113-
transaction_metadata: [{key: "valid key", value1: "invalid value"}]
114-
}
115-
end
105+
context "when expiration_at is a valid datetime string but in the future" do
106+
let(:expiration_at) { (Time.current + 1.hour).iso8601 }
116107

117-
it "returns false and result has errors" do
118-
expect(validate_service).not_to be_valid
119-
expect(result.error.messages[:metadata]).to eq(["invalid_key_value_pair"])
108+
it "returns true and has no errors" do
109+
expect(validate_service).to be_valid
120110
end
121111
end
122112
end
123113

114+
context "with invalid transaction metadata" do
115+
let(:args) do
116+
{
117+
customer:,
118+
organization_id: organization.id,
119+
paid_credits:,
120+
granted_credits:,
121+
expiration_at:,
122+
transaction_metadata: [{key: "valid key", value1: "invalid value"}]
123+
}
124+
end
125+
126+
it "returns false and result has errors" do
127+
expect(validate_service).not_to be_valid
128+
expect(result.error.messages[:metadata]).to eq(["invalid_key_value_pair"])
129+
end
130+
end
131+
124132
context "with recurring transaction rules" do
125133
let(:rules) do
126134
[

0 commit comments

Comments
 (0)