# Copyright:: (c) Autotelik Media Ltd 2011
# Author ::   Tom Statter
# Date ::     Summer 2011
#
# License::   MIT - Free, OpenSource
#
# Details::   Specification for Spree aspect of datashift gem.
#
#             Provides Loaders and rake tasks specifically tailored for uploading or exporting
#             Spree Products, associations and Images
#
require File.dirname(__FILE__) + '/spec_helper'

require 'spree_helper'
require 'product_loader'

include DataShift
  
describe 'SpreeLoader' do

  before(:all) do

    # key to YAML db e.g  test_memory, test_mysql
    db_connect( 'test_spree_standalone' )    
    
    # See errors  #<NameError: uninitialized constant RAILS_CACHE> when doing save (AR without Rails)
    # so copied this from ... Rails::Initializer.initialize_cache
    Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store( :memory_store )

    RAILS_CACHE = ActiveSupport::Cache.lookup_store( :memory_store )

    # we are not a Spree project, nor is it practical to externally generate
    # a complete Spree application for testing so we implement a mini migrate/boot of our own
    Spree.load()          # require Spree gems
    Spree.boot            # create a sort-of Spree app
    
    Spree.migrate_up      # create an sqlite Spree database on the fly

    @klazz = Product

    $SpreeFixturePath = File.join($DataShiftFixturePath, 'spree')
    
    $SpreeNegativeFixturePath = File.join($DataShiftFixturePath, 'negative')

  end
  
  def spree_fix( source)
    File.join($SpreeFixturePath, source)
  end

  before(:each) do

    MethodMapper.clear
    MethodMapper.find_operators( @klazz )

    # Reset main tables - TODO should really purge properly, or roll back a transaction
    [OptionType, OptionValue, Product, Property, Variant, Taxonomy, Taxon, Zone].each { |x| x.delete_all }
        
    Product.count.should == 0
    
    Taxon.count.should == 0

    # want to test both lookup and dynamic creation - this Taxonomy should be found, rest created
    root = Taxonomy.create( :name => 'Paintings' )
    Taxon.create( :name => 'Landscape', :taxonomy => root )

    Taxon.count.should == 2
    
        
    @product_loader = DataShift::Spree::ProductLoader.new
  end


  it "should process a simple .xls spreadsheet" do

    Zone.delete_all

    loader = ExcelLoader.new(Zone)
    
    loader.perform_load( spree_fix('SpreeZoneExample.xls') )

    loader.loaded_count.should == Zone.count
  end

  it "should process a simple csv file" do

    Zone.delete_all

    loader = CsvLoader.new(Zone)

    loader.perform_load( spree_fix('SpreeZoneExample.csv') )

    loader.loaded_count.should == Zone.count
  end
  

  # Loader should perform identically regardless of source, whether csv, .xls etc
  
  it "should load basic Products .xls via Spree loader", :focus => true do
    test_basic_product('SpreeProductsSimple.xls')
  end

  it "should load basic Products from .csv via Spree loader" do
    test_basic_product('SpreeProductsSimple.csv')
  end

  it "should raise an error for unsupported file types" do
    lambda { test_basic_product('SpreeProductsSimple.xml') }.should raise_error UnsupportedFileType
  end
  
  def test_basic_product( source )
    
    @product_loader.perform_load( spree_fix(source), :mandatory => ['sku', 'name', 'price'] )

    Product.count.should == 3

    @product_loader.failed_objects.size.should == 0
    @product_loader.loaded_objects.size.should == 3

    @product_loader.loaded_count.should == Product.count

    p = Product.first
    
    p.sku.should == "SIMPLE_001"
    p.price.should == 345.78
    p.name.should == "Simple Product for AR Loader"
    p.description.should == "blah blah"
    p.cost_price.should == 320.00
    p.option_types.should have_exactly(1).items
    p.count_on_hand.should == 12
    
    Product.last.option_types.should have_exactly(2).items
    Product.last.count_on_hand.should == 23
  end


  # Operation and results should be identical when loading multiple associations
  # if using either single column embedded syntax, or one column per entry.

  it "should load Products and create Variants from single column" do
    test_variants_creation('SpreeProducts.xls')
  end

  
  it "should load Products and create Variants from multiple column" do
    test_variants_creation('SpreeProductsMultiColumn.xls')
  end
  
  def test_variants_creation( source )
    @product_loader.perform_load( spree_fix(source), :mandatory => ['sku', 'name', 'price'] )
    
    expected_multi_column_variants
  end
  
  
  def expected_multi_column_variants
      
    # 3 MASTER products, 11 VARIANTS
    Product.count.should == 3
    Variant.count.should == 14

    p = Product.first

    p.sku.should == "DEMO_001"

    p.sku.should == "DEMO_001"
    p.price.should == 399.99
    p.description.should == "blah blah"
    p.cost_price.should == 320.00

    Product.all.select {|m| m.is_master.should == true  }

    p.variants.should have_exactly(3).items
  
    Variant.all[1].sku.should == "DEMO_001_0"
    Variant.all[1].price.should == 399.99

    v = p.variants[0]

    v.sku.should == "DEMO_001_0"
    v.price.should == 399.99
    v.count_on_hand.should == 12

    p.variants[1].count_on_hand.should == 6
    p.variants[2].count_on_hand.should == 7

    Variant.last.price.should == 50.34
    Variant.last.count_on_hand.should == 18

    @product_loader.failed_objects.size.should == 0
  end


  # Operation and results should be identical when loading multiple associations
  # if using either single column embedded syntax, or one column per entry.

  it "should load Products and multiple Properties from single column" do
    test_properties_creation( 'SpreeProducts.xls' )
  end

  it "should load Products and multiple Properties from multiple column" do
    test_properties_creation( 'SpreeProductsMultiColumn.xls' )
  end

  def test_properties_creation( source )

    # want to test both lookup and dynamic creation - this Prop should be found, rest created
    Property.create( :name => 'test_pp_001', :presentation => 'Test PP 001' )

    Property.count.should == 1

    @product_loader.perform_load( spree_fix(source), :mandatory => ['sku', 'name', 'price'] )
    
    expected_multi_column_properties
  
  end
  
  def expected_multi_column_properties
    Property.count.should == 4

    Product.first.properties.should have_exactly(1).items

    p3 = Product.all.last

    p3.properties.should have_exactly(3).items

    p3.properties.should include Property.find_by_name('test_pp_002')

    # Test the optional text value got set on assigned product property
    p3.product_properties.select {|p| p.value == 'Example free value' }.should have_exactly(1).items

  end

  # Operation and results should be identical when loading multiple associations
  # if using either single column embedded syntax, or one column per entry.

  it "should load Products and multiple Taxons from single column" do
    test_taxon_creation( 'SpreeProducts.xls' )
  end

  it "should load Products and multiple Taxons from multiple columns" do
    test_taxon_creation( 'SpreeProductsMultiColumn.xls' )
  end

  def test_taxon_creation( source )

    @product_loader.perform_load( spree_fix(source), :mandatory => ['sku', 'name', 'price'] )
    
    expected_multi_column_taxons
  end
  
  def expected_multi_column_taxons
      
    Taxonomy.count.should == 4
    Taxon.count.should == 5

    Product.first.taxons.should have_exactly(2).items
    Product.last.taxons.should have_exactly(1).items

    p2 = Product.all[1]

    p2.taxons.should have_exactly(3).items

    t = Taxon.find_by_name('Oils')

    t.should_not be_nil
    
    p2.taxons.collect( &:id ).should include(t.id)

  end

  
  # REPEAT THE WHOLE TEST SUITE VIA CSV

  it "should load Products from single column csv as per .xls" do
    test_variants_creation('SpreeProducts.csv')
    
    expected_multi_column_properties
    
    expected_multi_column_taxons
  end
  
  
  it "should load Products from multiple column csv as per .xls" do
    test_variants_creation('SpreeProductsMultiColumn.csv')
    
    expected_multi_column_properties
    
    expected_multi_column_taxons
  end
  
  
  it "should raise exception when mandatory columns missing from .xls", :ex => true do
    expect {@product_loader.perform_load($SpreeNegativeFixturePath + '/SpreeProdMissManyMandatory.xls', :mandatory => ['sku', 'name', 'price'] )}.to raise_error(DataShift::MissingMandatoryError)
  end
  

  it "should raise exception when single mandatory column missing from .xls", :ex => true do
    expect {@product_loader.perform_load($SpreeNegativeFixturePath + '/SpreeProdMiss1Mandatory.xls', :mandatory => 'sku' )}.to raise_error(DataShift::MissingMandatoryError)
  end

    it "should raise exception when mandatory columns missing from .csv", :ex => true do
    expect {@product_loader.perform_load($SpreeNegativeFixturePath + '/SpreeProdMissManyMandatory.csv', :mandatory => ['sku', 'name', 'price'] )}.to raise_error(DataShift::MissingMandatoryError)
  end
  

  it "should raise exception when single mandatory column missing from .csv", :ex => true do
    expect {@product_loader.perform_load($SpreeNegativeFixturePath + '/SpreeProdMiss1Mandatory.csv', :mandatory => 'sku' )}.to raise_error(DataShift::MissingMandatoryError)
  end
  
end