diff --git a/lib/active_shipping.rb b/lib/active_shipping.rb index d261ec625..0c4302009 100644 --- a/lib/active_shipping.rb +++ b/lib/active_shipping.rb @@ -11,6 +11,7 @@ require 'active_shipping/shipping_response' require 'active_shipping/label_response' require 'active_shipping/label' +require 'active_shipping/package_item' require 'active_shipping/package' require 'active_shipping/location' require 'active_shipping/rate_estimate' diff --git a/lib/active_shipping/package.rb b/lib/active_shipping/package.rb index 34e304a3f..9e3db08d5 100644 --- a/lib/active_shipping/package.rb +++ b/lib/active_shipping/package.rb @@ -1,78 +1,4 @@ module ActiveShipping #:nodoc: - # A package item is a unique item(s) that is physically in a package. - # A single package can have many items. This is only required - # for shipping methods (label creation) right now. - class PackageItem - include Quantified - - attr_reader :sku, :hs_code, :value, :name, :weight, :quantity, :options - - def initialize(name, grams_or_ounces, value, quantity, options = {}) - @name = name - - imperial = (options[:units] == :imperial) || - (grams_or_ounces.respond_to?(:unit) && m.unit.to_sym == :imperial) - - @unit_system = imperial ? :imperial : :metric - - @weight = attribute_from_metric_or_imperial(grams_or_ounces, Mass, :grams, :ounces) - - @value = Package.cents_from(value) - @quantity = quantity > 0 ? quantity : 1 - - @sku = options[:sku] - @hs_code = options[:hs_code] - @options = options - end - - def weight(options = {}) - case options[:type] - when nil, :actual - @weight - when :volumetric, :dimensional - @volumetric_weight ||= begin - m = Mass.new((centimetres(:box_volume) / 6.0), :grams) - @unit_system == :imperial ? m.in_ounces : m - end - when :billable - [weight, weight(:type => :volumetric)].max - end - end - alias_method :mass, :weight - - def ounces(options = {}) - weight(options).in_ounces.amount - end - alias_method :oz, :ounces - - def grams(options = {}) - weight(options).in_grams.amount - end - alias_method :g, :grams - - def pounds(options = {}) - weight(options).in_pounds.amount - end - alias_method :lb, :pounds - alias_method :lbs, :pounds - - def kilograms(options = {}) - weight(options).in_kilograms.amount - end - alias_method :kg, :kilograms - alias_method :kgs, :kilograms - - private - - def attribute_from_metric_or_imperial(obj, klass, metric_unit, imperial_unit) - if obj.is_a?(klass) - return value - else - return klass.new(obj, (@unit_system == :imperial ? imperial_unit : metric_unit)) - end - end - end - class Package include Quantified @@ -136,7 +62,9 @@ def cylinder? end alias_method :tube?, :cylinder? - def gift?; @gift end + def gift? + @gift + end def ounces(options = {}) weight(options).in_ounces.amount diff --git a/lib/active_shipping/package_item.rb b/lib/active_shipping/package_item.rb new file mode 100644 index 000000000..edfcee90d --- /dev/null +++ b/lib/active_shipping/package_item.rb @@ -0,0 +1,72 @@ +module ActiveShipping #:nodoc: + class PackageItem + include Quantified + + attr_reader :sku, :hs_code, :value, :name, :weight, :quantity, :options + + def initialize(name, grams_or_ounces, value, quantity, options = {}) + @name = name + + imperial = (options[:units] == :imperial) || + (grams_or_ounces.respond_to?(:unit) && m.unit.to_sym == :imperial) + + @unit_system = imperial ? :imperial : :metric + + @weight = attribute_from_metric_or_imperial(grams_or_ounces, Mass, :grams, :ounces) + + @value = Package.cents_from(value) + @quantity = quantity > 0 ? quantity : 1 + + @sku = options[:sku] + @hs_code = options[:hs_code] + @options = options + end + + def weight(options = {}) + case options[:type] + when nil, :actual + @weight + when :volumetric, :dimensional + @volumetric_weight ||= begin + m = Mass.new((centimetres(:box_volume) / 6.0), :grams) + @unit_system == :imperial ? m.in_ounces : m + end + when :billable + [weight, weight(:type => :volumetric)].max + end + end + alias_method :mass, :weight + + def ounces(options = {}) + weight(options).in_ounces.amount + end + alias_method :oz, :ounces + + def grams(options = {}) + weight(options).in_grams.amount + end + alias_method :g, :grams + + def pounds(options = {}) + weight(options).in_pounds.amount + end + alias_method :lb, :pounds + alias_method :lbs, :pounds + + def kilograms(options = {}) + weight(options).in_kilograms.amount + end + alias_method :kg, :kilograms + alias_method :kgs, :kilograms + + private + + def attribute_from_metric_or_imperial(obj, klass, metric_unit, imperial_unit) + if obj.is_a?(klass) + return value + else + return klass.new(obj, (@unit_system == :imperial ? imperial_unit : metric_unit)) + end + end + end +end diff --git a/test/unit/package_item_test.rb b/test/unit/package_item_test.rb new file mode 100644 index 000000000..2e0d51677 --- /dev/null +++ b/test/unit/package_item_test.rb @@ -0,0 +1,237 @@ +require 'test_helper' + +class PackageItemTest < Minitest::Test + def setup + @name = "Fancy Pants" + @weight = 100 + @value = 1299 + @quantity = 3 + @sku = "1234567890" + @hs_code = "1234.56.78" + @options = { + units: :metric, + sku: @sku, + hs_code: @hs_code, + type: :actual, + } + + @item = PackageItem.new(@name, @weight, @value, @quantity, @options) + @mass = ::Quantified::Mass.new(@weight, :grams) + end + + def test_initialize_with_all_attributes + assert_equal @name, @item.name + assert_equal @options, @item.options + assert_equal @hs_code, @item.hs_code + assert_equal @sku, @item.sku + assert_equal @value, @item.value + assert_equal @quantity, @item.quantity + end + + def test_initialize_assumes_symbol_keys + options = { + "units" => :imperial, + "sku" => @sku, + "hs_code" => @hs_code, + } + @item = PackageItem.new(@name, @weight, @value, @quantity, options) + + assert_nil @item.hs_code + assert_nil @item.sku + refute_equal @weight, @item.ounces + assert_equal @weight, @item.grams + end + + def test_initialize_negative_quantity + assert_equal 1, PackageItem.new(@name, @weight, @value, -1).quantity + assert_equal 1, PackageItem.new(@name, @weight, @value, 0).quantity + end + + def test_initialize_weight_mass_object + begin + @item = PackageItem.new(@name, @mass, @value, @quantity, @options) + assert_equal @mass, @item.weight + flunk "This code path is broken but passed unexpectedly" + rescue NameError + skip "This code path is broken" + end + end + + def test_initialize_weight_default_metric + assert_equal @weight, @item.grams + refute_equal @weight, @item.ounces + end + + def test_initialize_weight_accepts_imperial + @item = PackageItem.new(@name, @weight, @value, @quantity, @options.merge(units: :imperial)) + + assert_equal @weight, @item.ounces + refute_equal @weight, @item.grams + end + + def test_initialize_weight_accepts_metric + @item = PackageItem.new(@name, @weight, @value, @quantity, @options.merge(units: :metric)) + + assert_equal @weight, @item.grams + refute_equal @weight, @item.ounces + end + + def test_initialize_weight_does_not_accept_strings + @item = PackageItem.new(@name, @weight, @value, @quantity, @options.merge(units: "imperial")) + + assert_equal @weight, @item.grams + refute_equal @weight, @item.ounces + end + + def test_initialize_value_from_cents + @item = PackageItem.new(@name, @weight, "1.23", @quantity, @options) + + assert_equal 123, @item.value + end + + def test_weight + assert_equal @mass, @item.weight + assert_instance_of ::Quantified::Mass, @item.weight + end + + def test_weight_actual + assert_equal @mass, @item.weight(type: :actual) + assert_instance_of ::Quantified::Mass, @item.weight(type: :actual) + end + + def test_weight_volumetric + begin + assert_equal :todo, @item.weight(type: :volumetric) + assert_instance_of ::Quantified::Mass, @item.weight(type: :volumetric) + flunk "This code path is broken but passed unexpectedly" + rescue NoMethodError + skip "This code path is broken" + end + end + + def test_weight_dimensional + begin + assert_equal :todo, @item.weight(type: :dimensional) + assert_instance_of ::Quantified::Mass, @item.weight(type: :dimensional) + flunk "This code path is broken but passed unexpectedly" + rescue NoMethodError + skip "This code path is broken" + end + end + + def test_weight_billable_max_weight_and_volumetric + begin + assert_equal :todo, @item.weight(type: :billable) + assert_instance_of ::Quantified::Mass, @item.weight(type: :billable) + flunk "This code path is broken but passed unexpectedly" + rescue NoMethodError + skip "This code path is broken" + end + end + + def test_grams_value + assert_equal 100, @item.grams + end + + def test_grams_accepts_options_with_type + begin + assert_equal :todo, @item.grams(type: :volumetric) + flunk "This code path is broken but passed unexpectedly" + rescue NoMethodError + skip "This code path is broken" + end + end + + def test_grams_converts + @item = PackageItem.new(@name, 100, @value, @quantity, @options.merge(units: :imperial)) + + assert_in_delta 2834.9, @item.grams, 0.1 + end + + def test_grams_alias_g + assert_equal @item.grams, @item.g + end + + def test_ounces_value + @item = PackageItem.new(@name, @weight, @value, @quantity, @options.merge(units: :imperial)) + + assert_equal 100, @item.ounces + end + + def test_ounces_accepts_options_with_type + begin + assert_equal :todo, @item.ounces(type: :volumetric) + flunk "This code path is broken but passed unexpectedly" + rescue NoMethodError + skip "This code path is broken" + end + end + + def test_ounces_converts + @item = PackageItem.new(@name, @weight, @value, @quantity, @options.merge(units: :metric)) + + assert_in_delta 3.5, @item.ounces, 0.1 + end + + def test_ounces_alias_oz + assert_equal @item.ounces, @item.oz + end + + def test_pounds_value + @item = PackageItem.new(@name, 32, @value, @quantity, @options.merge(units: :imperial)) + + assert_equal 2, @item.pounds + end + + def test_pounds_accepts_options_with_type + begin + assert_equal :todo, @item.pounds(type: :volumetric) + flunk "This code path is broken but passed unexpectedly" + rescue + skip "This code path is broken" + end + end + + def test_pounds_converts + @item = PackageItem.new(@name, 1000, @value, @quantity, @options.merge(units: :metric)) + + assert_in_delta 2.2, @item.pounds, 0.1 + end + + def test_pounds_alias_lb + assert_equal @item.pounds, @item.lb + end + + def test_pounds_alias_lbs + assert_equal @item.pounds, @item.lbs + end + + def test_kilograms_value + @item = PackageItem.new(@name, 1000, @value, @quantity, @options.merge(units: :metric)) + + assert_equal 1, @item.kilograms + end + + def test_kilograms_accepts_options_with_type + begin + assert_equal :todo, @item.kilograms(type: :volumetric) + flunk "This code path is broken but passed unexpectedly" + rescue + skip "This code path is broken" + end + end + + def test_kilograms_converts + @item = PackageItem.new(@name, 1000, @value, @quantity, @options.merge(units: :imperial)) + + assert_in_delta 28.3, @item.kilograms, 0.1 + end + + def test_kilograms_alias_kg + assert_equal @item.kilograms, @item.kg + end + + def test_kilograms_alias_kgs + assert_equal @item.kilograms, @item.kgs + end +end diff --git a/test/unit/package_test.rb b/test/unit/package_test.rb index 29a70576e..8ec00d00d 100644 --- a/test/unit/package_test.rb +++ b/test/unit/package_test.rb @@ -1,68 +1,403 @@ require 'test_helper' class PackageTest < Minitest::Test - include ActiveShipping::Test::Fixtures - - GRAMS_IN_AN_OUNCE = 28.349523125 - OUNCES_IN_A_GRAM = 0.0352739619495804 - INCHES_IN_A_CM = 0.393700787401575 - CM_IN_AN_INCH = 2.54 - def setup - @imperial_package = Package.new(65, [3, 6, 8.5], - :units => :imperial, - :value => 10.65, - :currency => 'USD' - ) - - @metric_package = Package.new(100, [5, 18.5, 40], - :value => 860, - :currency => 'CAD' - ) - end - - def test_accessors - # :wii => Package.new((7.5 * 16), [15, 10, 4.5], :units => :imperial, :value => 269.99, :currency => 'GBP') - wii = package_fixtures[:wii] - [:x, :max, :long, :length].each do |sym| - assert_equal 15, wii.inches(sym) - assert_equal 15, wii.in(sym) - assert_equal 15 * CM_IN_AN_INCH, wii.centimetres(sym) - assert_equal 15 * CM_IN_AN_INCH, wii.cm(sym) - end - [:y, :mid, :width, :wide].each do |sym| - assert_equal 10, wii.inches(sym) - assert_equal 10, wii.in(sym) - assert_equal 10 * CM_IN_AN_INCH, wii.centimetres(sym) - assert_equal 10 * CM_IN_AN_INCH, wii.cm(sym) - end - [:z, :min, :height, :high, :depth, :deep].each do |sym| - assert_equal 4.5, wii.inches(sym) - assert_equal 4.5, wii.in(sym) - assert_equal 4.5 * CM_IN_AN_INCH, wii.centimetres(sym) - assert_equal 4.5 * CM_IN_AN_INCH, wii.cm(sym) - end - [:pounds, :lbs, :lb].each do |sym| - assert_equal 7.5, wii.send(sym) - end - [:ounces, :oz].each do |sym| - assert_equal 120, wii.send(sym) - end - [:grams, :g].each do |sym| - assert_equal 120 * GRAMS_IN_AN_OUNCE, wii.send(sym) - end - [:kilograms, :kgs, :kg].each do |sym| - assert_equal 120 * GRAMS_IN_AN_OUNCE / 1000, wii.send(sym) - end - assert_equal 675.0, wii.inches(:volume) - assert_equal 675.0, wii.inches(:box_volume) + @weight = 100 + @dimensions = [5, 6, 7] + @value = 1299 + @currency = "USD" + @cylinder = false + @tube = false + @gift = false + @oversized = false + @unpackaged = false + @dim_units = :metric + @units = :metric + @weight_units = :metric + @options = { + value: @value, + currency: @currency, + cylinder: @cylinder, + tube: @tube, + gift: @gift, + oversized: @oversized, + unpackaged: @unpackaged, + dim_units: @dim_units, + units: @units, + weight_units: @weight_units, + } + + @package = Package.new(@weight, @dimensions, @options) + @imperial_package = Package.new(@weight, @dimensions, @options.merge(units: :imperial, dim_units: :imperial, weight_units: :imperial)) - assert_equal 'GBP', wii.currency - assert_equal 26999, wii.value + @mass = ::Quantified::Mass.new(@weight, :grams) end def test_package_from_mass - pkg = Package.new(Quantified::Mass.new(10, :pounds), []) - assert_equal 10, pkg.weight + package = Package.new(Quantified::Mass.new(10, :pounds), []) + assert_equal 10, package.weight + end + + def test_initialize_defaults + assert_equal @value, @package.value + assert_equal @currency, @package.currency + assert_equal @cylinder, @package.cylinder? + assert_equal @tube, @package.tube? + assert_equal @oversized, @package.oversized? + assert_equal @unpackaged, @package.unpackaged? + assert_equal @gift, @package.gift? + end + + def test_currency_cents + @package = Package.new(@weight, @dimensions, value: money) + assert_equal @currency, @package.currency + assert_equal @value, @package.value + end + + def test_initialize_sorted_dimensions + @package = Package.new(@weight, [9, 8, 7], @options) + + assert_equal [7, 8, 9], @package.centimetres + end + + def test_initialize_blank_dimensions + @package = Package.new(@weight, [], @options) + + assert_equal [0, 0, 0], @package.centimetres + end + + def test_initialize_increases_dimension_size_to_three + @package = Package.new(@weight, [2], @options) + + assert_equal [2, 2, 2], @package.centimetres + end + + def test_initialize_default_units + assert_equal @dimensions, @package.centimetres + assert_equal @weight, @package.grams + end + + def test_initialize_units + assert_equal @dimensions, @imperial_package.inches + end + + def test_initialize_weight_units + @package = Package.new(@weight, @dimensions, @options.merge(weight_units: :imperial)) + + assert_equal @weight, @package.ounces + end + + def test_unpackaged + assert_predicate Package.new(@weight, @dimensions, unpackaged: true), :unpackaged? + refute_predicate Package.new(@weight, @dimensions, unpackaged: false), :unpackaged? + end + + def test_oversized + assert_predicate Package.new(@weight, @dimensions, oversized: true), :oversized? + refute_predicate Package.new(@weight, @dimensions, oversized: false), :oversized? + end + + def test_gift + assert_predicate Package.new(@weight, @dimensions, gift: true), :gift? + refute_predicate Package.new(@weight, @dimensions, gift: false), :gift? + end + + def test_cylinder_tube + @package = Package.new(@weight, @dimensions, cylinder: false, tube: false) + refute_predicate @package, :cylinder? + refute_predicate @package, :tube? + + @package = Package.new(@weight, @dimensions, cylinder: true, tube: false) + assert_predicate @package, :cylinder? + assert_predicate @package, :tube? + + @package = Package.new(@weight, @dimensions, cylinder: false, tube: true) + assert_predicate @package, :cylinder? + assert_predicate @package, :tube? + + @package = Package.new(@weight, @dimensions, cylinder: true, tube: true) + assert_predicate @package, :cylinder? + assert_predicate @package, :tube? + end + + def test_inches_number_index + assert_equal @dimensions[0], @imperial_package.inches(0) + assert_equal @dimensions[1], @imperial_package.inches(1) + assert_equal @dimensions[2], @imperial_package.inches(2) + assert_nil @imperial_package.inches(3) + end + + def test_inches_x + assert_equal @dimensions[2], @imperial_package.inches(:x) + assert_equal @dimensions[2], @imperial_package.inches(:max) + assert_equal @dimensions[2], @imperial_package.inches(:length) + assert_equal @dimensions[2], @imperial_package.inches(:long) + end + + def test_inches_y + assert_equal @dimensions[1], @imperial_package.inches(:y) + assert_equal @dimensions[1], @imperial_package.inches(:mid) + assert_equal @dimensions[1], @imperial_package.inches(:width) + assert_equal @dimensions[1], @imperial_package.inches(:wide) + end + + def test_inches_z + assert_equal @dimensions[0], @imperial_package.inches(:z) + assert_equal @dimensions[0], @imperial_package.inches(:min) + assert_equal @dimensions[0], @imperial_package.inches(:height) + assert_equal @dimensions[0], @imperial_package.inches(:depth) + assert_equal @dimensions[0], @imperial_package.inches(:high) + assert_equal @dimensions[0], @imperial_package.inches(:deep) + end + + def test_inches_girth_cylinder + @imperial_package = Package.new(@weight, @dimensions, @options.merge(cylinder: true, units: :imperial, dim_units: :imperial)) + + assert_predicate @imperial_package, :cylinder? + assert_in_delta 17.2, @imperial_package.inches(:girth), 1 + assert_in_delta 17.2, @imperial_package.inches(:around), 1 + assert_in_delta 17.2, @imperial_package.inches(:circumference), 1 + + end + + def test_inches_girth + refute_predicate @imperial_package, :cylinder? + assert_in_delta 22, @imperial_package.inches(:girth), 1 + assert_in_delta 22, @imperial_package.inches(:around), 1 + assert_in_delta 22, @imperial_package.inches(:circumference), 1 + end + + def test_inches_volume_cylinder + @imperial_package = Package.new(@weight, @dimensions, @options.merge(cylinder: true, units: :imperial, dim_units: :imperial)) + + assert_predicate @imperial_package, :cylinder? + assert_in_delta 522.4, @imperial_package.inches(:volume), 1 + end + + def test_inches_volume + refute_predicate @imperial_package, :cylinder? + assert_equal 210, @imperial_package.inches(:volume) + end + + def test_inches_box_volume + assert_equal 210, @imperial_package.inches(:box_volume) + end + + def test_inches_unknown + assert_nil @imperial_package.inches(:unknown) + end + + def test_inches_alias_in + assert_equal @dimensions, @imperial_package.inches + assert_equal @dimensions, @imperial_package.in + end + + def test_centimetres_number_index + assert_equal @dimensions[0], @package.centimetres(0) + assert_equal @dimensions[1], @package.centimetres(1) + assert_equal @dimensions[2], @package.centimetres(2) + assert_nil @package.centimetres(3) + end + + def test_centimetres_x + assert_equal @dimensions[2], @package.centimetres(:x) + assert_equal @dimensions[2], @package.centimetres(:max) + assert_equal @dimensions[2], @package.centimetres(:length) + assert_equal @dimensions[2], @package.centimetres(:long) + end + + def test_centimetres_y + assert_equal @dimensions[1], @package.centimetres(:y) + assert_equal @dimensions[1], @package.centimetres(:mid) + assert_equal @dimensions[1], @package.centimetres(:width) + assert_equal @dimensions[1], @package.centimetres(:wide) + end + + def test_centimetres_z + assert_equal @dimensions[0], @package.centimetres(:z) + assert_equal @dimensions[0], @package.centimetres(:min) + assert_equal @dimensions[0], @package.centimetres(:height) + assert_equal @dimensions[0], @package.centimetres(:depth) + assert_equal @dimensions[0], @package.centimetres(:high) + assert_equal @dimensions[0], @package.centimetres(:deep) + end + + def test_centimetres_girth_cylinder + @package = Package.new(@weight, @dimensions, @options.merge(cylinder: true, units: :metric, dim_units: :metric)) + + assert_predicate @package, :cylinder? + assert_in_delta 17.2, @package.centimetres(:girth), 1 + assert_in_delta 17.2, @package.centimetres(:around), 1 + assert_in_delta 17.2, @package.centimetres(:circumference), 1 + + end + + def test_centimetres_girth + refute_predicate @package, :cylinder? + assert_in_delta 22, @package.centimetres(:girth), 1 + assert_in_delta 22, @package.centimetres(:around), 1 + assert_in_delta 22, @package.centimetres(:circumference), 1 + end + + def test_centimetres_volume_cylinder + @package = Package.new(@weight, @dimensions, @options.merge(cylinder: true, units: :metric, dim_units: :metric)) + + assert_predicate @package, :cylinder? + assert_in_delta 522.4, @package.centimetres(:volume), 1 + end + + def test_centimetres_volume + refute_predicate @package, :cylinder? + assert_equal 210, @package.centimetres(:volume) + end + + def test_centimetres_box_volume + assert_equal 210, @package.centimetres(:box_volume) + end + + def test_centimetres_unknown + assert_nil @package.centimetres(:unknown) + end + + def test_centimetres_alias_cm + assert_equal @dimensions, @package.centimetres + assert_equal @dimensions, @package.cm + end + + def test_weight + assert_equal @mass, @package.weight + assert_instance_of ::Quantified::Mass, @package.weight + end + + def test_weight_actual + assert_equal @mass, @package.weight(type: :actual) + assert_instance_of ::Quantified::Mass, @package.weight(type: :actual) + end + + def test_weight_volumetric + assert_equal ::Quantified::Mass.new(35, :grams), @package.weight(type: :volumetric) + end + + def test_weight_dimensional + assert_equal ::Quantified::Mass.new(35, :grams), @package.weight(type: :dimensional) + end + + def test_weight_billable_max_weight_and_volumetric + assert_equal ::Quantified::Mass.new(100, :grams), @package.weight(type: :billable) + + @package = Package.new(500, [1, 1, 1], @options) + assert_equal ::Quantified::Mass.new(500, :grams), @package.weight(type: :billable) + end + + def test_grams_value + assert_equal 100, @package.grams + end + + def test_grams_accepts_options_with_type + assert_in_delta 35, @package.grams(type: :volumetric), 1 + end + + def test_grams_converts + @package = Package.new(@weight, @dimensions, weight_units: :imperial) + + assert_in_delta 2834.9, @package.grams, 1 + end + + def test_grams_alias_g + assert_equal @package.grams, @package.g + end + + def test_ounces_value + assert_equal 100, @imperial_package.ounces + end + + def test_ounces_accepts_options_with_type + assert_in_delta 20.2, @imperial_package.ounces(type: :volumetric), 1 + end + + def test_ounces_converts + assert_in_delta 3.5, @package.ounces, 1 + end + + def test_ounces_alias_oz + assert_equal @imperial_package.ounces, @imperial_package.oz + end + + def test_pounds_value + assert_equal 6.25, @imperial_package.pounds + end + + def test_pounds_accepts_options_with_type + assert_in_delta 0.07, @package.pounds(type: :volumetric), 0.01 + end + + def test_pounds_converts + assert_in_delta 0.22, @package.pounds, 0.01 + end + + def test_pounds_alias_lb + assert_equal @package.pounds, @package.lb + end + + def test_pounds_alias_lbs + assert_equal @package.pounds, @package.lbs + end + + def test_kilograms_value + assert_equal 0.1, @package.kilograms + end + + def test_kilograms_accepts_options_with_type + assert_equal 0.035, @package.kilograms(type: :volumetric) + end + + def test_kilograms_converts + assert_in_delta 2.8, @imperial_package.kilograms, 1 + end + + def test_kilograms_alias_kg + assert_equal 0.1, @package.kg + end + + def test_kilograms_alias_kgs + assert_equal @package.kilograms, @package.kgs + end + + def test_cents_from_nil + assert_nil Package.cents_from(nil) + end + + def test_cents_from_cents + assert_equal @value, Package.cents_from(money) + end + + def test_cents_from_float + assert_equal 120, Package.cents_from(1.2) + end + + def test_cents_from_string + assert_equal 120, Package.cents_from("1.20") + end + + def test_cents_from_int + assert_equal 12, Package.cents_from(12) + end + + def test_cents_from_nonsense + exception = assert_raises NoMethodError do + Package.cents_from(Object.new) + end + assert_match /undefined method `to_i'/, exception.message + end + + private + + def money + @money ||= begin + value = Class.new { attr_accessor :currency, :cents }.new + value.currency = @currency + value.cents = @value + value + end end end