require 'spec_helper'
require 'r10k/environment'

describe R10K::Environment::SVN do

  subject do
    described_class.new(
      'myenv',
      '/some/nonexistent/environmentdir',
      'svn-dirname',
      {
        :remote => 'https://svn-server.site/svn-repo/trunk'
      }
    )
  end

  let(:working_dir) { subject.working_dir }

  describe "initializing" do
    subject do
      described_class.new('name', '/dir', 'ref', {
        :puppetfile_name => 'Puppetfile',
      })
    end

    it "accepts valid base class initialization arguments" do
      expect(subject.name).to eq 'name'
    end
  end

  describe "storing attributes" do
    it "can return the environment name" do
      expect(subject.name).to eq 'myenv'
    end

    it "can return the environment basedir" do
      expect(subject.basedir).to eq '/some/nonexistent/environmentdir'
    end

    it "can return the environment dirname" do
      expect(subject.dirname).to eq 'svn-dirname'
    end

    it "can return the environment remote" do
      expect(subject.remote).to eq 'https://svn-server.site/svn-repo/trunk'
    end
  end

  describe "synchronizing the environment" do
    it "checks out the working directory when creating a new environment" do
      allow(working_dir).to receive(:is_svn?).and_return(false)
      expect(working_dir).to receive(:checkout)
      subject.sync
    end

    it "updates the working directory when updating an existing environment" do
      allow(working_dir).to receive(:is_svn?).and_return(true)
      expect(working_dir).to receive(:update)
      subject.sync
    end
  end

  describe "generating a puppetfile for the environment" do
    let(:puppetfile) { subject.puppetfile }

    it "creates a puppetfile at the full path to the environment" do
      expect(puppetfile.basedir).to eq '/some/nonexistent/environmentdir/svn-dirname'
    end

    it "sets the moduledir to 'modules' relative to the environment path" do
      expect(puppetfile.moduledir).to eq '/some/nonexistent/environmentdir/svn-dirname/modules'
    end

    it "sets the puppetfile path to 'Puppetfile' relative to the environment path" do
      expect(puppetfile.puppetfile_path).to eq '/some/nonexistent/environmentdir/svn-dirname/Puppetfile'
    end
  end

  describe "enumerating modules" do
    it "loads the Puppetfile and returns modules in that puppetfile" do
      loaded = { managed_directories: [], desired_contents: [], purge_exclusions: [] }
      mod = double('A module', :name => 'dbl')
      expect(subject.loader).to receive(:load).and_return(loaded.merge(modules: [mod]))
      expect(subject.modules).to eq([mod])
    end
  end

  describe "determining the status" do
    it "is absent if the working directory is absent" do
      expect(subject.path).to receive(:exist?).and_return(false)
      expect(subject.status).to eq :absent
    end

    it "is mismatched if the working directory is not an SVN repo" do
      expect(subject.path).to receive(:exist?).and_return(true)
      expect(working_dir).to receive(:is_svn?).and_return(false)
      expect(subject.status).to eq :mismatched
    end

    it "is mismatched if the working directory remote doesn't match the expected remote" do
      expect(subject.path).to receive(:exist?).and_return(true)
      expect(working_dir).to receive(:is_svn?).and_return(true)
      expect(working_dir).to receive(:url).and_return 'https://svn-server.site/another-svn-repo/trunk'
      expect(subject.status).to eq :mismatched
    end

    it "is outdated when the the working directory has not synced" do
      expect(subject.path).to receive(:exist?).and_return(true)
      expect(working_dir).to receive(:is_svn?).and_return(true)
      expect(working_dir).to receive(:url).and_return 'https://svn-server.site/svn-repo/trunk'
      expect(subject.status).to eq :outdated
    end

    it "is insync when the working directory has been synced" do
      expect(subject.path).to receive(:exist?).and_return(true)
      expect(working_dir).to receive(:is_svn?).twice.and_return(true)
      expect(working_dir).to receive(:url).and_return 'https://svn-server.site/svn-repo/trunk'

      expect(working_dir).to receive(:update)

      subject.sync

      expect(subject.status).to eq :insync
    end
  end

  describe "environment signature" do
    it "returns the svn revision of the branch" do
      expect(working_dir).to receive(:revision).and_return '1337'
      expect(subject.signature).to eq '1337'
    end
  end

  describe "info hash" do
    let(:info_hash) { subject.info }

    before(:each) do
      expect(working_dir).to receive(:revision).and_return '1337'
    end

    it "includes name and signature" do
      expect(info_hash.keys).to include :name, :signature
      expect(info_hash).not_to have_value(nil)
    end
  end
end
