require 'spec_helper'

describe Configliere::ConfigFile do
  let(:default_params) { { my_param: 'default_val', also_a_param: true } }
  
  subject{ Configliere::Param.new default_params }

  it 'is included by default' do
    subject.class.included_modules.should include(described_class)
  end

  context '#read' do
    let(:file_params) { { my_param: 'val_from_file' } }
    let(:file_string) { file_params.to_yaml           }
    let(:file_path)   { '/absolute/path.yaml'         }
    
    before{ File.stub(:open).and_return(file_string) }

    it 'returns the config object for chaining' do
      subject.read(file_path).should == subject
    end

    context 'a yaml file' do
      let(:file_path) { '/absolute/path.yaml' }
      
      it 'reads successfully' do
        subject.should_receive(:read_yaml).with(file_string, {})
        subject.read file_path
      end      
      
      it 'merges the data' do
        subject.read(file_path).should == default_params.merge(file_params)
      end      
    end

    context 'a json file' do
      let(:file_path)   { '/absolute/path.json' }
      let(:file_string) { file_params.to_json   }
      
      it 'reads successfully' do
        subject.should_receive(:read_json).with(file_string, {})
        subject.read file_path
      end      

      it 'merges the data' do
        subject.read(file_path).should == default_params.merge(file_params) 
      end
    end
    
    context 'given a symbol' do
      let(:file_path) { :my_settings }

      it 'no longer provides a default config file' do
        expect{ subject.read(file_path) }.to raise_error(Configliere::DeprecatedError)
        defined?(Configliere::DEFAULT_CONFIG_FILE).should_not be_true
      end
    end
      
    context 'given a nonexistent file' do
      let(:file_path) { 'nonexistent.conf' } 
      
      it 'warns but does not fail if the file is missing' do
        File.stub(:open).and_raise(Errno::ENOENT)
        subject.should_receive(:warn).with("Loading empty configliere settings file #{subject.default_conf_dir}/#{file_path}")
        subject.read(file_path).should == subject
      end
    end

    context 'given an absolute path' do
      let(:file_path) { '/absolute/path.yaml' }
      
      it 'uses it directly' do
        File.should_receive(:open).with(file_path).and_return(file_string)
        subject.read file_path
      end      
    end
    
    context 'given a simple filename' do
      let(:file_path) { 'simple_path.yaml' }
      
      it 'references it to the default config dir' do
        File.should_receive(:open).with(File.join(subject.default_conf_dir, file_path)).and_return(file_string)
        subject.read file_path
      end    
    end
    
    context 'with options' do
      let(:file_params) { { development: { reload: true }, production: { reload: false } } }

      before{ subject.merge!(reload: 'whatever') }

      context ':env key' do
        context 'valid :env' do
          let(:opts) { { env: :development } }
          
          it 'slices out a subhash given by :env' do
            subject.read(file_path, opts)
            subject.should == default_params.merge(reload: true)
          end
        end

        context 'invalid :env' do
          let(:opts) { { env: :not_there } }
          
          it 'has no effect if the key given by :env option is absent' do
            subject.read(file_path, opts)
            subject.should == default_params.merge(reload: 'whatever')
          end     
        end         
      end
      
      context 'no :env key' do
        let(:opts) { Hash.new }
        
        it 'does no slicing without the :env option' do
          subject.read(file_path, opts)
          subject.should == default_params.merge(reload: 'whatever').merge(file_params) 
        end      
      end  
    end  
  end

  context '#save!' do
    let(:fake_file) { StringIO.new('', 'w') }

    context 'given an absolute pathname' do
      let(:file_path) { '/absolute/path.yaml' }
      
      it 'saves the filename as given' do
        File.should_receive(:open).with(file_path, 'w').and_yield(fake_file)
        fake_file.should_receive(:<<).with(default_params.to_yaml)
        subject.save! file_path
      end
    end 
    
    context 'given a simple pathname' do
      let(:file_path) { 'simple_path.yaml' }

      it 'saves the filename in the default config dir' do
        File.should_receive(:open).with(File.join(subject.default_conf_dir, file_path), 'w').and_yield(fake_file)
        fake_file.should_receive(:<<).with(default_params.to_yaml)
        subject.save! file_path
      end
      
      it 'ensures the directory exists' do
        File.should_receive(:open).with(File.join(subject.default_conf_dir, file_path), 'w').and_yield(fake_file)
        FileUtils.should_receive(:mkdir_p).with(subject.default_conf_dir.to_s)
        subject.save! file_path
      end
    end
  end
  
  context '#resolve!' do
    around do |example|
      Configliere::ParamParent.class_eval{ def resolve!() parent_method ; end }
      example.run
      Configliere::ParamParent.class_eval{ def resolve!() self ; end }
    end
    
    it 'calls super and returns self' do
      subject.should_receive(:parent_method)
      subject.resolve!.should equal(subject)
    end
  end
  
  describe '#validate!' do
    around do |example|
      Configliere::ParamParent.class_eval{ def validate!() parent_method ; end }
      example.run
      Configliere::ParamParent.class_eval{ def validate!() self ; end }
    end
    
    it 'calls super and returns self' do
      subject.should_receive(:parent_method)
      subject.validate!.should equal(subject)
    end
  end

  context '#load_configuration_in_order!' do
    let(:scope) { 'test' }
  
    before{ subject.stub(:determine_conf_location).and_return('conf_dir') }
    
    it 'resolves configuration in order' do
      subject.should_receive(:determine_conf_location).with(:machine, scope).ordered
      subject.should_receive(:determine_conf_location).with(:user, scope).ordered
      subject.should_receive(:determine_conf_location).with(:app, scope).ordered
      subject.should_receive(:resolve!).ordered
      subject.load_configuration_in_order!(scope)
    end
  end
end