spec/calculator_spec.rb in dentaku-3.5.1 vs spec/calculator_spec.rb in dentaku-3.5.2

- old
+ new

@@ -20,10 +20,11 @@ expect(calculator.evaluate('2 ^ - 1')).to eq(0.5) expect(calculator.evaluate('2 ^ -(3 - 2)')).to eq(0.5) expect(calculator.evaluate('(2 + 3) - 1')).to eq(4) expect(calculator.evaluate('(-2 + 3) - 1')).to eq(0) expect(calculator.evaluate('(-2 - 3) - 1')).to eq(-6) + expect(calculator.evaluate('1353+91-1-3322-22')).to eq(-1901) expect(calculator.evaluate('1 + -(2 ^ 2)')).to eq(-3) expect(calculator.evaluate('3 + -num', num: 2)).to eq(1) expect(calculator.evaluate('-num + 3', num: 2)).to eq(1) expect(calculator.evaluate('10 ^ 2')).to eq(100) expect(calculator.evaluate('0 * 10 ^ -5')).to eq(0) @@ -327,10 +328,15 @@ have_money: :undefined ) }.not_to raise_error end + it 'allows to compare "-" or "-."' do + expect { calculator.solve("IF('-' = '-', 0, 1)") }.not_to raise_error + expect { calculator.solve("IF('-.'= '-.', 0, 1)") }.not_to raise_error + end + it "integrates with custom functions" do calculator.add_function(:custom, :integer, -> { 1 }) result = calculator.solve( a: "1", @@ -438,25 +444,48 @@ expect(calculator.evaluate('t1 < 2017-01-02', t1: Time.local(2017, 1, 3).to_datetime)).to be_falsy expect(calculator.evaluate('t1 > 2017-01-02', t1: Time.local(2017, 1, 1).to_datetime)).to be_falsy expect(calculator.evaluate('t1 > 2017-01-02', t1: Time.local(2017, 1, 3).to_datetime)).to be_truthy end - it 'supports date arithmetic' do - expect(calculator.evaluate!('2020-01-01 + 30').to_date).to eq(Time.local(2020, 1, 31).to_date) - expect(calculator.evaluate!('2020-01-01 - 1').to_date).to eq(Time.local(2019, 12, 31).to_date) - expect(calculator.evaluate!('2020-01-01 - 2019-12-31')).to eq(1) - expect(calculator.evaluate!('2020-01-01 + duration(1, day)').to_date).to eq(Time.local(2020, 1, 2).to_date) - expect(calculator.evaluate!('2020-01-01 - duration(1, day)').to_date).to eq(Time.local(2019, 12, 31).to_date) - expect(calculator.evaluate!('2020-01-01 + duration(30, days)').to_date).to eq(Time.local(2020, 1, 31).to_date) - expect(calculator.evaluate!('2020-01-01 + duration(1, month)').to_date).to eq(Time.local(2020, 2, 1).to_date) - expect(calculator.evaluate!('2020-01-01 - duration(1, month)').to_date).to eq(Time.local(2019, 12, 1).to_date) - expect(calculator.evaluate!('2020-01-01 + duration(30, months)').to_date).to eq(Time.local(2022, 7, 1).to_date) - expect(calculator.evaluate!('2020-01-01 + duration(1, year)').to_date).to eq(Time.local(2021, 1, 1).to_date) - expect(calculator.evaluate!('2020-01-01 - duration(1, year)').to_date).to eq(Time.local(2019, 1, 1).to_date) - expect(calculator.evaluate!('2020-01-01 + duration(30, years)').to_date).to eq(Time.local(2050, 1, 1).to_date) + describe 'disabling date literals' do + it 'does not parse formulas with minus signs as dates' do + calculator = described_class.new(raw_date_literals: false) + expect(calculator.evaluate!('2020-01-01')).to eq(2018) + end end + describe 'supports date arithmetic' do + it 'from hardcoded string' do + expect(calculator.evaluate!('2020-01-01 + 30').to_date).to eq(Time.local(2020, 1, 31).to_date) + expect(calculator.evaluate!('2020-01-01 - 1').to_date).to eq(Time.local(2019, 12, 31).to_date) + expect(calculator.evaluate!('2020-01-01 - 2019-12-31')).to eq(1) + expect(calculator.evaluate!('2020-01-01 + duration(1, day)').to_date).to eq(Time.local(2020, 1, 2).to_date) + expect(calculator.evaluate!('2020-01-01 - duration(1, day)').to_date).to eq(Time.local(2019, 12, 31).to_date) + expect(calculator.evaluate!('2020-01-01 + duration(30, days)').to_date).to eq(Time.local(2020, 1, 31).to_date) + expect(calculator.evaluate!('2020-01-01 + duration(1, month)').to_date).to eq(Time.local(2020, 2, 1).to_date) + expect(calculator.evaluate!('2020-01-01 - duration(1, month)').to_date).to eq(Time.local(2019, 12, 1).to_date) + expect(calculator.evaluate!('2020-01-01 + duration(30, months)').to_date).to eq(Time.local(2022, 7, 1).to_date) + expect(calculator.evaluate!('2020-01-01 + duration(1, year)').to_date).to eq(Time.local(2021, 1, 1).to_date) + expect(calculator.evaluate!('2020-01-01 - duration(1, year)').to_date).to eq(Time.local(2019, 1, 1).to_date) + expect(calculator.evaluate!('2020-01-01 + duration(30, years)').to_date).to eq(Time.local(2050, 1, 1).to_date) + end + + it 'from string variable' do + value = '2023-01-01' + + expect(calculator.evaluate!('value + duration(1, month)', { value: value }).to_date).to eql(Date.parse('2023-02-01')) + expect(calculator.evaluate!('value - duration(1, month)', { value: value }).to_date).to eql(Date.parse('2022-12-01')) + end + + it 'from date object' do + value = Date.parse('2023-01-01').to_date + + expect(calculator.evaluate!('value + duration(1, month)', { value: value }).to_date).to eql(Date.parse('2023-02-01')) + expect(calculator.evaluate!('value - duration(1, month)', { value: value }).to_date).to eql(Date.parse('2022-12-01')) + end + end + describe 'functions' do it 'include IF' do expect(calculator.evaluate('if(foo < 8, 10, 20)', foo: 2)).to eq(10) expect(calculator.evaluate('if(foo < 8, 10, 20)', foo: 9)).to eq(20) expect(calculator.evaluate('if (foo < 8, 10, 20)', foo: 2)).to eq(10) @@ -469,10 +498,17 @@ expect(calculator.evaluate('round(8.75, 1)')).to eq(BigDecimal('8.8')) expect(calculator.evaluate('ROUND(apples * 0.93)', apples: 10)).to eq(9) end + it 'include ABS' do + expect(calculator.evaluate('abs(-2.2)')).to eq(2.2) + expect(calculator.evaluate('abs(5)')).to eq(5) + + expect(calculator.evaluate('ABS(x * -1)', x: 10)).to eq(10) + end + it 'include NOT' do expect(calculator.evaluate('NOT(some_boolean)', some_boolean: true)).to be_falsey expect(calculator.evaluate('NOT(some_boolean)', some_boolean: false)).to be_truthy expect(calculator.evaluate('NOT(some_boolean) AND 7 > 5', some_boolean: true)).to be_falsey @@ -756,13 +792,13 @@ fruit: 'apple') expect(value).to eq(3) end end - describe 'math functions' do + describe 'math support' do Math.methods(false).each do |method| - it method do + it "includes `#{method}`" do if Math.method(method).arity == 2 expect(calculator.evaluate("#{method}(x,y)", x: 1, y: '2')).to eq(Math.send(method, 1, 2)) expect(calculator.evaluate("#{method}(x,y) + 1", x: 1, y: '2')).to be_within(0.00001).of(Math.send(method, 1, 2) + 1) expect { calculator.evaluate!("#{method}(x)", x: 1) }.to raise_error(Dentaku::ParseError) else @@ -772,13 +808,18 @@ end end end end - it 'are defined with a properly named class that represents it to support AST marshaling' do + it 'defines a properly named class to support AST marshaling' do expect { Marshal.dump(calculator.ast('SQRT(20)')) }.not_to raise_error + end + + it 'properly handles a Math::DomainError' do + expect(calculator.evaluate('asin(2)')).to be_nil + expect { calculator.evaluate!('asin(2)') }.to raise_error(Dentaku::MathDomainError) end end describe 'disable_cache' do before do