Back to Repositories

Testing TimeSeries Data Handling Implementation in maybe-finance

This test suite validates the TimeSeries model functionality in the Maybe Finance application, focusing on handling time-based financial data with both monetary and numeric values. The tests ensure proper series initialization, trend calculation, and JSON serialization.

Test Coverage Overview

The test suite provides comprehensive coverage of TimeSeries functionality including:

  • Money value series handling and validation
  • Numeric value series processing
  • Empty series handling
  • JSON serialization for both money and numeric series
  • Input validation and error handling

Implementation Analysis

The testing approach utilizes Minitest’s assertion framework to validate TimeSeries behavior. It employs systematic test cases for different data types and edge cases, leveraging Ruby’s Money gem integration and ActiveSupport::TestCase functionality.

Key patterns include setup of test data with date-based series, validation of trend calculations, and error handling verification.

Technical Details

Testing implementation utilizes:

  • Minitest framework with ActiveSupport::TestCase
  • Money gem for currency handling
  • ActiveModel validations
  • Ruby date manipulation methods
  • JSON serialization testing

Best Practices Demonstrated

The test suite demonstrates strong testing practices including:

  • Isolated test cases for specific functionality
  • Comprehensive edge case coverage
  • Clear test naming conventions
  • Proper assertion usage
  • Error condition validation

maybe-finance/maybe

test/models/time_series_test.rb

            
require "test_helper"

class TimeSeriesTest < ActiveSupport::TestCase
  test "it can accept array of money values" do
    series = TimeSeries.new([ { date: 1.day.ago.to_date, value: Money.new(100) }, { date: Date.current, value: Money.new(200) } ])

    assert_equal Money.new(100), series.first.value
    assert_equal Money.new(200), series.last.value
    assert_equal "up", series.favorable_direction
    assert_equal "up", series.trend.direction
    assert_equal Money.new(100), series.trend.value
    assert_equal 100.0, series.trend.percent
  end

  test "it can accept array of numeric values" do
    series = TimeSeries.new([ { date: 1.day.ago.to_date, value: 100 }, { date: Date.current, value: 200 } ])

    assert_equal 100, series.first.value
    assert_equal 200, series.last.value
    assert_equal 100, series.on(1.day.ago.to_date).value
    assert_equal "up", series.favorable_direction
    assert_equal "up", series.trend.direction
    assert_equal 100, series.trend.value
    assert_equal 100.0, series.trend.percent
  end

  test "when empty array passed, it returns empty series" do
    series = TimeSeries.new([])

    assert_nil series.first
    assert_nil series.last
    assert_equal({ values: [], trend: { favorable_direction: "up", direction: "flat", value: 0, percent: 0.0 }, favorable_direction: "up" }.to_json, series.to_json)
  end

  test "money series can be serialized to json" do
    expected_values = {
      values: [
        {
          date: 1.day.ago.to_date,
          value: { amount: "100.0", currency: "USD" },
          trend: { favorable_direction: "up", direction: "flat", value: { amount: "0.0", currency: "USD" }, percent: 0.0 }
        },
        {
          date: Date.current,
          value: { amount: "200.0", currency: "USD" },
          trend: { favorable_direction: "up", direction: "up", value: { amount: "100.0", currency: "USD" }, percent: 100.0 }
        }
      ],
      trend: { favorable_direction: "up", direction: "up", value: { amount: "100.0", currency: "USD" }, percent: 100.0 },
      favorable_direction: "up"
    }.to_json

    series = TimeSeries.new([ { date: 1.day.ago.to_date, value: Money.new(100) }, { date: Date.current, value: Money.new(200) } ])

    assert_equal expected_values, series.to_json
  end

  test "numeric series can be serialized to json" do
    expected_values = {
      values: [
        { date: 1.day.ago.to_date, value: 100, trend: { favorable_direction: "up", direction: "flat", value: 0, percent: 0.0 } },
        { date: Date.current, value: 200, trend: { favorable_direction: "up", direction: "up", value: 100, percent: 100.0 } }
      ],
      trend: { favorable_direction: "up", direction: "up", value: 100, percent: 100.0 },
      favorable_direction: "up"
    }.to_json

    series = TimeSeries.new([ { date: 1.day.ago.to_date, value: 100 }, { date: Date.current, value: 200 } ])

    assert_equal expected_values, series.to_json
  end

  test "it does not accept invalid values in Time Series Trend" do
    error = assert_raises(ActiveModel::ValidationError) do
      TimeSeries.new(
        [
          { date: 1.day.ago.to_date, value: 100 },
          { date: Date.current, value: "two hundred" }
        ]
      )
    end
    assert_match(/Current must be of the same type as previous/, error.message)
    assert_match(/Previous must be of the same type as current/, error.message)
    assert_match(/Current must be of type Money, Numeric, or nil/, error.message)
  end


  test "it does not accept invalid values in Time Series Value" do
    # We need to stub trend otherwise an error is raised before TimeSeries::Value validation
    TimeSeries::Trend.stub(:new, nil) do
      error = assert_raises(ActiveModel::ValidationError) do
        TimeSeries.new(
          [
            { date: 1.day.ago.to_date, value: 100 },
            { date: Date.current, value: "two hundred" }
          ]
        )
      end
      assert_equal "Validation failed: Value must be a Money or Numeric", error.message
    end
  end
end