# -*- encoding : utf-8 -*-
require_relative 'test_helper'

class DaoTest < ::Dao::TestCase
## api
#
  test 'that an api class for your application can be built using a simple dsl' do
    assert{
      Dao.api do
        ### dsl
      end
    }
  end

  test 'that apis can have callable endpoints added to them which accept params and return results' do
    captured = []

    api_class =
      assert{
        Dao.api do
          endpoint(:foo) do |params, result|
            captured.push(params, result)
          end
        end
      }
    api = assert{ api_class.new }
    result = assert{ api.call(:foo, {}) }
    assert{ result.is_a?(Hash) }
  end

  test 'that endpoints are automatically called according to arity' do
    api = assert{ Class.new(Dao.api) }
    assert{ api.class_eval{ endpoint(:zero){|| result.update :args => [] } } }
    assert{ api.class_eval{ endpoint(:one){|a| result.update :args => [a]} } }
    assert{ api.class_eval{ endpoint(:two){|a,b| result.update :args => [a,b]} } }

    assert{ api.new.call(:zero).args.size == 0 }
    assert{ api.new.call(:one).args.size == 1 }
    assert{ api.new.call(:two).args.size == 2 }
  end

  test 'that endpoints have an auto-vivifying params/result' do
    api = assert{ Class.new(Dao.api) }
    assert{ api.class_eval{ endpoint(:foo){ params; result; } } }
    result = assert{ api.new.call(:foo) }
    assert{ result.path.to_s =~ /foo/ }
  end

  test 'that an api can be called with different modes' do
    Dao::Mode.list.each do |mode|
      api_class =
        assert{
          Dao.api do
            call(:foo) do
              send(mode){ data.update(:mode => mode) }
            end
          end
        }
      api = assert{ api_class.new }

      assert{ api.mode(mode).call(:foo).data[:mode] == mode }
    end
  end

  test 'that read==get' do
    api_class =
      assert{
        Dao.api do
          call(:foo) do
            read { data.update :answer => 42 }
          end

          call(:bar) do
            get { data.update :answer => 42.0 }
          end
        end
      }
    api = assert{ api_class.new }

    assert{ api.read.call(:foo).data.answer == 42 }
    assert{ api.get.call(:foo).data.answer == 42 }

    assert{ api.read.call(:bar).data.answer == 42.0 }
    assert{ api.get.call(:bar).data.answer == 42.0 }
  end

  test 'that write==post' do
    api_class =
      assert{
        Dao.api do
          call(:foo) do
            write { data.update :answer => 42 }
          end

          call(:bar) do
            post { data.update :answer => 42.0 }
          end
        end
      }
    api = assert{ api_class.new }

    assert{ api.write.call(:foo).data.answer == 42 }
    assert{ api.post.call(:foo).data.answer == 42 }

    assert{ api.write.call(:bar).data.answer == 42.0 }
    assert{ api.post.call(:bar).data.answer == 42.0 }
  end

  test 'that aliases are re-defined in scope' do
    api_class =
      assert{
        Dao.api do
          call(:foo) do
            data.update :a => mode
            read { data.update :b => mode }
            write { data.update :b => mode }
          end

          call(:bar) do
            data.update :a => mode
            get { data.update :b => mode }
            post { data.update :b => mode }
          end
        end
      }
    api = assert{ api_class.new }


    [:foo, :bar].each do |call|
      result = assert{ api.read.call(call) }
      assert{ result.data[:a] = Dao::Mode.read }
      assert{ result.data[:b] = Dao::Mode.read }

      result = assert{ api.get.call(call) }
      assert{ result.data[:a] = Dao::Mode.read }
      assert{ result.data[:b] = Dao::Mode.get }


      result = assert{ api.write.call(call) }
      assert{ result.data[:a] = Dao::Mode.write }
      assert{ result.data[:b] = Dao::Mode.write }

      result = assert{ api.post.call(call) }
      assert{ result.data[:a] = Dao::Mode.write }
      assert{ result.data[:b] = Dao::Mode.post }
    end
  end

## context
#
  test 'that calls have a shortcut to status' do
    api_class =
      assert{
        Dao.api do
          call(:foo){ status! 420 }
        end
      }
    api = assert{ api_class.new }
    result = assert{ api.call(:foo) }
    assert{ result.status =~ 420 }
  end

## results
#
  test 'that results can be created' do
    result = assert{ Dao::Result.new }
    assert{ result.path }
    assert{ result.status }
    assert{ result.errors }
    assert{ result.params }
    assert{ result.data }
  end

  test 'that results can be created with a path' do
    result = assert{ Dao::Result.new('/api/foo/bar') }
    assert{ result.path == '/api/foo/bar' }
  end

## paths
#
  test 'that simple paths can be contstructed/compiled' do
    path = assert{ Dao::Path.for('./api/../foo/bar')  }
    assert{ path =~ %r|^/| }
    assert{ path !~ %r|[.]| }
    assert{ path.params.is_a?(Hash) }
    assert{ path.keys.is_a?(Array) }
    assert{ path.pattern.is_a?(Regexp) }
  end

## routes
#
  test 'that an api has a list of routes' do
    api_class =
      assert{
        Dao.api do
        end
      }
    assert{ api_class.routes.is_a?(Array) }
  end

  test 'that routed endpoints call be declared' do
    api_class =
      assert{
        Dao.api do
          call('/users/:user_id/comments/:comment_id') do
            data.update(params)
          end
        end
      }
    api_class.new
  end

  test 'that routed methods can be called with embedded params' do
    api_class =
      assert{
        Dao.api do
          call('/users/:user_id/comments/:comment_id') do 
            data.update(params)
          end
        end
      }
    api = api_class.new

    {
      '/users/4/comments/2' => {},
      '/users/:user_id/comments/:comment_id' => {:user_id => 4, :comment_id => 2},
    }.each do |path, params|
      result = assert{ api.call(path, params) }
      assert{ result.data.user_id.to_s =~ /4/ }
      assert{ result.data.comment_id.to_s =~ /2/ }
      assert{ result.path == '/users/4/comments/2' }
      assert{ result.route == '/users/:user_id/comments/:comment_id' }
    end
  end

## doc
#
  test 'that apis can be documented via the api' do
    api_class =
      assert {
        Dao.api {
          description 'foobar'
          doc 'signature' => {'read' => '...', 'write' => '...'} 
          endpoint('/barfoo'){}
        }
      }
    api_class_index = assert{ api_class.index.is_a?(Hash) }
    api = assert{ api_class.new }
    api_index = assert{ api.index.is_a?(Hash) }
    assert{ api_class_index==api_index }
  end

# aliases
#
  test 'that apis can alias methods' do
    api_class =
      assert {
        Dao.api {
          call('/barfoo'){ data.update(:k => :v) }
          call('/foobar', :alias => '/barfoo')
        }
      }
    api = assert{ api_class.new }
    assert{ api.call('/barfoo').data.k == :v }
    assert{ api.call('/foobar').data.k == :v }
  end

protected
  def hash_equal(a, b)
    array = lambda{|h| h.to_a.map{|k,v| [k.to_s, v]}.sort}
    array[a] == array[b]
  end

  def api(&block)
    api_class = assert{ Dao.api(&block) }
    assert{ api_class.new }
  end
end

