module Fuelcell::Action
  RSpec.describe Command do
    context '.new' do
      it 'accepts name as its only argument' do
        expect(new_command('foo').name).to eq('foo')
      end

      it 'converts a symbol name to a string' do
        expect(new_command(:foo).name).to eq('foo')
      end

      it 'accepts hyphens in the name' do
        expect(new_command('foo-bar').name).to eq('foo-bar')
      end
    end

    context '#usage' do
      it 'defaults to nil' do
        expect(new_command('foo').usage).to eq nil
      end

      it 'returns the usage text saved' do
        cmd  = new_command('foo')
        text = 'command <COMMAND>'
        expect(cmd.usage(text)).to eq(text)
      end

      it 'assigns the text to usage' do
        cmd  = new_command('foo')
        text = 'foobar'
        cmd.usage(text)
        expect(cmd.usage).to eq(text)
      end

      it 'assigns desc with the second param' do
        cmd  = new_command('foo')
        usage = 'some <USAGE>'
        desc  = 'some desc'
        cmd.usage(usage, desc)
        expect(cmd.desc).to eq(desc)
      end
    end

    context '#desc' do
      it 'defaults to nil' do
        expect(new_command('foo').desc).to eq(nil)
      end

      it 'returns the description it saves' do
        cmd  = new_command('foo')
        text = 'some description'
        expect(cmd.desc(text)).to eq(text)
      end

      it 'assigns the text to desc' do
        cmd  = new_command('foo')
        text = 'some kind of description'
        cmd.desc(text)
        expect(cmd.desc).to eq(text)
      end
    end

    context '#opt' do
      it 'adds an opt instance to the options array' do
        cmd = new_command('foo')
        cmd.opt 'version'
        opt_instance = OptDefinition
        expect(cmd.opts['version']).to be_an_instance_of(opt_instance)
      end

      it 'adds an opt intance with the correct name' do
        cmd = new_command('foo')
        cmd.opt 'version'
        opt = cmd.opts['version']
        expect(opt.name).to eq 'version'
      end
    end

    describe 'subcommands' do
      context '#search' do
        it 'returns NotFound cmd when there are no subcommands' do
          root = new_command('foo')
          expect(root.search(['bar'])).to be_an_instance_of(NotFound)
        end

        it 'returns nil when command is not found' do
          root = new_command('foo')
          sub  = new_command('bar')
          root << sub
          expect(root.search(['biz'])).to be_an_instance_of(NotFound)
        end

        it 'searchs for and finds single sub command that exists' do
          root = new_command('foo')
          sub  = new_command('bar')
          root << sub
          expect(root.search(['bar'])).to eq(sub)
        end

        it 'returns the sub sub command' do
          root   = new_command('foo')
          sub    = new_command('bar')
          subsub = new_command('baz')
          sub  << subsub
          root << sub
          expect(root.search(['bar', 'baz'])).to eq(subsub)
        end

        it 'returns the sub sub sub command' do
          root      = new_command('foo')
          sub       = new_command('bar')
          subsub    = new_command('baz')
          subsubsub = new_command('biz')
          subsub << subsubsub
          sub    << subsub
          root   << sub
          expect(root.search(['bar', 'baz', 'biz'])).to eq(subsubsub)
        end

        it 'returns NotFound cmd when the sub sub sub command is not found' do
          root      = new_command('foo')
          sub       = new_command('bar')
          subsub    = new_command('baz')
          subsubsub = new_command('biz')
          subsub << subsubsub
          sub    << subsub
          root   << sub

          list = ['bar', 'baz', 'fiz']
          expect(root.search(list)).to be_an_instance_of(NotFound)
        end
      end
    end

    context '#command' do
      it 'appends a subcommand to the command' do
        cmd = new_command('foo')
        cmd.command('bar') {}
        expect(cmd['bar']).to be_an_instance_of(Command)
      end

      it 'can assign the subcommand command usage' do
        cmd  = new_command('foo')
        text = 'this is some usage text'
        cmd.command('bar') do
          usage text
        end
        expect(cmd['bar'].usage).to eq(text)
      end

      it 'can assign the sub command desc' do
        cmd  = new_command('foo')
        text = 'some other text'
        cmd.command('bar') { desc text }
        expect(cmd['bar'].desc).to eq(text)
      end

      it 'can assign the sub command callable' do
        cmd  = new_command('foo')
        fun  = ->() {}
        cmd.command('bar') { callable fun }
        expect(cmd['bar'].callable).to eq(fun)
      end

      it 'can assign sub sub commands ' do
        cmd  = new_command('foo')
        fun = ->() {}
        cmd.command('bar') {
          command 'baz' do
            run fun
          end
        }

        expect(cmd['bar baz'].callable).to eq fun
      end
    end

    context '#add_global_options' do
      it 'will not add global options to itself' do
        root = new_command('foo')
        root.opt 'myopt', global: true

        expect {
          root.add_global_options(root)
        }.not_to raise_error
      end

      it 'returns itself' do
        root = new_command('foo')
        root.opt 'myopt', global: true
        expect(root.add_global_options(root)).to eq root
      end

      it 'will not add any new options' do
        root = new_command('foo')
        root.opt 'myopt', global: true
        root.add_global_options(root)
        expect(root.opts.options.size).to eq 1
      end
    end

    def new_command(name)
      Command.new(name)
    end
  end
end
