aboutsummaryrefslogblamecommitdiffstats
path: root/activeresource/test/cases/base/schema_test.rb
blob: d9dc67994193028dbafe00861f77a64012d17393 (plain) (tree)
1
2
                       
                                                  




























































































                                                                                                                
                                                                    













































































                                                                                                                            
 




                                                                                                      
 

                                                       
                           

      

                                                                                      
 
                                                                       
                       

       
 

                                                                   



                               
         

                                                                                         

       
 
                                                           
                            



                                  
         

                                                                                   




                                                       



                                 
         


                                                                                                

       
 
                                                           
                            
                                                                      



                                    
           

                                                                                                          


         
 


                                                       





                                                                                                                     
           




                                                                                                                              




           
 
                                                                          




                                                                    
         

                                                                                                        

       
 

                                                                          







                                                                                                






                                                       
               




                                                                                  
 




                                                                                                                         
                                                


                                                                                                                                          
                                                                                                                                            






                                                                                  
 



                                                                                                                         
                                                



                                                                                                                                          
                                                                                                                                            










                                                                                                        
                                    

                                                                                                            
                                        


                                                  
                                              











                                                                         
                                   
                                                                     
 























































                                                                                                                                                     
require 'abstract_unit'
require 'active_support/core_ext/hash/conversions'
require "fixtures/person"
require "fixtures/street_address"

########################################################################
# Testing the schema of your Active Resource models
########################################################################
class SchemaTest < ActiveModel::TestCase
  def setup
    @matz  = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
    @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
    @greg  = { :id => 3, :name => 'Greg' }.to_xml(:root => 'person')
    @addy  = { :id => 1, :street => '12345 Street', :country => 'Australia' }.to_xml(:root => 'address')
    @default_request_headers = { 'Content-Type' => 'application/xml' }
    @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person")
    @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people')
    @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people')
    @addresses = [{ :id => 1, :street => '12345 Street', :country => 'Australia' }].to_xml(:root => 'addresses')

    ActiveResource::HttpMock.respond_to do |mock|
      mock.get    "/people/1.xml",                {}, @matz
      mock.get    "/people/2.xml",                {}, @david
      mock.get    "/people/Greg.xml",             {}, @greg
      mock.get    "/people/4.xml",                {'key' => 'value'}, nil, 404
      mock.get    "/people/5.xml",                {}, @rick
      mock.put    "/people/1.xml",                {}, nil, 204
      mock.delete "/people/1.xml",                {}, nil, 200
      mock.delete "/people/2.xml",                {}, nil, 400
      mock.get    "/people/99.xml",               {}, nil, 404
      mock.post   "/people.xml",                  {}, @rick, 201, 'Location' => '/people/5.xml'
      mock.get    "/people.xml",                  {}, @people
      mock.get    "/people/1/addresses.xml",      {}, @addresses
      mock.get    "/people/1/addresses/1.xml",    {}, @addy
      mock.get    "/people/1/addresses/2.xml",    {}, nil, 404
      mock.get    "/people/2/addresses/1.xml",    {}, nil, 404
      mock.get    "/people/Greg/addresses/1.xml", {}, @addy
      mock.put    "/people/1/addresses/1.xml",    {}, nil, 204
      mock.delete "/people/1/addresses/1.xml",    {}, nil, 200
      mock.post   "/people/1/addresses.xml",      {}, nil, 201, 'Location' => '/people/1/addresses/5'
      mock.get    "/people//addresses.xml",       {}, nil, 404
      mock.get    "/people//addresses/1.xml",     {}, nil, 404
      mock.put    "/people//addressaddresseses/1.xml",     {}, nil, 404
      mock.delete "/people//addresses/1.xml",     {}, nil, 404
      mock.post   "/people//addresses.xml",       {}, nil, 404
      mock.head   "/people/1.xml",                {}, nil, 200
      mock.head   "/people/Greg.xml",             {}, nil, 200
      mock.head   "/people/99.xml",               {}, nil, 404
      mock.head   "/people/1/addresses/1.xml",    {}, nil, 200
      mock.head   "/people/1/addresses/2.xml",    {}, nil, 404
      mock.head   "/people/2/addresses/1.xml",    {}, nil, 404
      mock.head   "/people/Greg/addresses/1.xml", {}, nil, 200
    end

    Person.user = nil
    Person.password = nil
  end
  def teardown
    Person.schema = nil # hack to stop test bleedthrough...
  end


  #####################################################
  # Passing in a schema directly and returning it
  ####

  test "schema on a new model should be empty" do
    assert Person.schema.blank?, "should have a blank class schema"
    assert Person.new.schema.blank?, "should have a blank instance schema"
  end

  test "schema should only accept a hash" do
    ["blahblah", ['one','two'],  [:age, :name], Person.new].each do |bad_schema|
      assert_raises(ArgumentError,"should only accept a hash (or nil), but accepted: #{bad_schema.inspect}") do
        Person.schema = bad_schema
      end
    end
  end

  test "schema should accept a simple hash" do
    new_schema = {'age' => 'integer', 'name' => 'string'}

    assert_nothing_raised { Person.schema = new_schema }
    assert_equal new_schema, Person.schema
  end

  test "schema should accept a hash with simple values" do
    new_schema = {'age' => 'integer', 'name' => 'string', 'height' => 'float', 'mydatetime' => 'string'}

    assert_nothing_raised { Person.schema = new_schema }
    assert_equal new_schema, Person.schema
  end

  test "schema should accept all known attribute types as values" do
    # I'd prefer to use  here...
    ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type|
      assert_nothing_raised("should have accepted #{the_type.inspect}"){ Person.schema = {'my_key' => the_type }}
    end
  end

  test "schema should not accept unknown values" do
    bad_values = [ :oogle, :blob, 'thing']

    bad_values.each do |bad_value|
      assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do
        Person.schema = {'key' => bad_value}
      end
    end
  end

  test "schema should accept nil and remove the schema" do
    new_schema = {'age' => 'integer', 'name' => 'string'}
    assert_nothing_raised { Person.schema = new_schema }
    assert_equal new_schema, Person.schema # sanity check


    assert_nothing_raised { Person.schema = nil }
    assert_nil Person.schema, "should have nulled out the schema, but still had: #{Person.schema.inspect}"
  end


  test "schema should be with indifferent access" do
    new_schema = {:age => :integer, 'name' => 'string'}
    new_schema_syms = new_schema.keys

    assert_nothing_raised { Person.schema = new_schema }
    new_schema_syms.each do |col|
      assert Person.new.respond_to?(col.to_s), "should respond to the schema's string key, but failed on: #{col.to_s}"
      assert Person.new.respond_to?(col.to_sym), "should respond to the schema's symbol key, but failed on: #{col.to_sym}"
    end
  end


  test "schema on a fetched resource should return all the attributes of that model instance" do
    p = Person.find(1)
    s = p.schema

    assert s.present?, "should have found a non-empty schema!"

    p.attributes.each do |the_attr, val|
      assert s.has_key?(the_attr), "should have found attr: #{the_attr} in schema, but only had: #{s.inspect}"
    end
  end

  test "with two instances, default schema should match the attributes of the individual instances - even if they differ" do
    matz = Person.find(1)
    rick = Person.find(5)

    m_attrs = matz.attributes.keys.sort
    r_attrs = rick.attributes.keys.sort

    assert_not_equal m_attrs, r_attrs, "should have different attributes on each model"

    assert_not_equal matz.schema, rick.schema, "should have had different schemas too"
  end

  test "defining a schema should return it when asked" do
    assert Person.schema.blank?, "should have a blank class schema"
    new_schema = {'name' => 'string', 'age' => 'integer', 'height' => 'float', 'weight' => 'float'}
    assert_nothing_raised {
      Person.schema = new_schema
      assert_equal new_schema, Person.schema, "should have saved the schema on the class"
      assert_equal new_schema, Person.new.schema, "should have mde the schema available to every instance"
    }
  end

  test "defining a schema, then fetching a model should still match the defined schema" do
    # sanity checks
    assert Person.schema.blank?, "should have a blank class schema"
    new_schema = {'name' => 'string', 'age' => 'integer', 'height' => 'float', 'weight' => 'float'}

    matz = Person.find(1)
    assert !matz.schema.blank?, "should have some sort of schema on an instance variable"
    assert_not_equal new_schema, matz.schema, "should not have the class-level schema until it's been added to the class!"

    assert_nothing_raised {
      Person.schema = new_schema
      assert_equal new_schema, matz.schema, "class-level schema should override instance-level schema"
    }
  end


  #####################################################
  # Using the schema syntax
  ####

  test "should be able to use schema" do
    assert Person.respond_to?(:schema), "should at least respond to the schema method"

    assert_nothing_raised("Should allow the schema to take a block") do
      Person.schema { }
    end
  end

  test "schema definition should store and return attribute set" do
    assert_nothing_raised do
      s = nil
      Person.schema do
        s = self
        attribute :foo, :string
      end
      assert s.respond_to?(:attrs), "should return attributes in theory"
      assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice")
    end
  end

  test "should be able to add attributes through schema" do
    assert_nothing_raised do
      s = nil
      Person.schema do
        s = self
        attribute('foo', 'string')
      end
      assert s.attrs.has_key?('foo'), "should have saved the attribute name"
      assert_equal 'string', s.attrs['foo'], "should have saved the attribute type"
    end
  end

  test "should convert symbol attributes to strings" do
    assert_nothing_raised do
      s = nil
      Person.schema do
        attribute(:foo, :integer)
        s = self
      end

      assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string"
      assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string"
    end
  end

  test "should be able to add all known attribute types" do
    assert_nothing_raised do
      ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type|
        s = nil
        Person.schema do
          s = self
          attribute('foo', the_type)
        end
        assert s.attrs.has_key?('foo'), "should have saved the attribute name"
        assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}"
      end
    end
  end

  test "attributes should not accept unknown values" do
    bad_values = [ :oogle, :blob, 'thing']

    bad_values.each do |bad_value|
      s = nil
      assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do
        Person.schema do
          s = self
          attribute 'key', bad_value
        end
      end
      assert !self.respond_to?(bad_value), "should only respond to a known attribute type, but accepted: #{bad_value.inspect}"
      assert_raises(NoMethodError,"should only have methods for known attribute types, but accepted: #{bad_value.inspect}") do
        Person.schema do
          send bad_value, 'key'
        end
      end
    end
  end


  test "should accept attribute types as the type's name as the method" do
    ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type|
      s = nil
      Person.schema do
        s = self
        send(the_type,'foo')
      end
      assert s.attrs.has_key?('foo'), "should now have saved the attribute name"
      assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}"
    end
  end

  test "should accept multiple attribute names for an attribute method" do
    names = ['foo','bar','baz']
    s = nil
    Person.schema do
      s = self
      string(*names)
    end
    names.each do |the_name|
      assert s.attrs.has_key?(the_name), "should now have saved the attribute name: #{the_name}"
      assert_equal 'string', s.attrs[the_name], "should have saved the attribute as a string"
    end
  end

  #####################################################
  # What a schema does for us
  ####

  # respond_to?

  test "should respond positively to attributes that are only in the schema" do
    new_attr_name = :my_new_schema_attribute
    new_attr_name_two = :another_new_schema_attribute
    assert Person.schema.blank?, "sanity check - should have a blank class schema"

    assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet"
    assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet"

    assert_nothing_raised do
      Person.schema = {new_attr_name.to_s => 'string'}
      Person.schema { string new_attr_name_two }
    end

    assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}"
    assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the schema, but failed on: #{new_attr_name_two}"
  end

  test "should not care about ordering of schema definitions" do
    new_attr_name = :my_new_schema_attribute
    new_attr_name_two = :another_new_schema_attribute

    assert Person.schema.blank?, "sanity check - should have a blank class schema"

    assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet"
    assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet"

    assert_nothing_raised do
      Person.schema { string new_attr_name_two }
      Person.schema = {new_attr_name.to_s => 'string'}
    end

    assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}"
    assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the schema, but failed on: #{new_attr_name_two}"
  end

  # method_missing effects

  test "should not give method_missing for attribute only in schema" do
    new_attr_name = :another_new_schema_attribute
    new_attr_name_two = :another_new_schema_attribute

    assert Person.schema.blank?, "sanity check - should have a blank class schema"

    assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name} as a method") do
      Person.new.send(new_attr_name)
    end
    assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name_two} as a method") do
      Person.new.send(new_attr_name_two)
    end

    Person.schema = {new_attr_name.to_s => :float}
    Person.schema { string new_attr_name_two }

    assert_nothing_raised do
      Person.new.send(new_attr_name)
      Person.new.send(new_attr_name_two)
    end
  end


  ########
  # Known attributes
  #
  # Attributes can be known to be attributes even if they aren't actually
  # 'set' on a particular instance.
  # This will only differ from 'attributes' if a schema has been set.

  test "new model should have no known attributes" do
    assert Person.known_attributes.blank?, "should have no known attributes"
    assert Person.new.known_attributes.blank?, "should have no known attributes on a new instance"
  end

  test "setting schema should set known attributes on class and instance" do
    new_schema = {'age' => 'integer', 'name' => 'string'}

    assert_nothing_raised { Person.schema = new_schema }

    assert_equal new_schema.keys, Person.known_attributes
    assert_equal new_schema.keys, Person.new.known_attributes
  end

  test "known attributes on a fetched resource should return all the attributes of the instance" do
    p = Person.find(1)
    attrs = p.known_attributes

    assert attrs.present?, "should have found some attributes!"

    p.attributes.each do |the_attr, val|
      assert attrs.include?(the_attr), "should have found attr: #{the_attr} in known attributes, but only had: #{attrs.inspect}"
    end
  end

  test "with two instances, known attributes should match the attributes of the individual instances - even if they differ" do
    matz = Person.find(1)
    rick = Person.find(5)

    m_attrs = matz.attributes.keys.sort
    r_attrs = rick.attributes.keys.sort

    assert_not_equal m_attrs, r_attrs, "should have different attributes on each model"

    assert_not_equal matz.known_attributes, rick.known_attributes, "should have had different known attributes too"
  end

  test "setting schema then fetching should add schema attributes to the intance attributes" do
    # an attribute in common with fetched instance and one that isn't
    new_schema = {'name' => 'string', 'my_strange_attribute' => 'string'}

    assert_nothing_raised { Person.schema = new_schema }

    matz = Person.find(1)
    known_attrs = matz.known_attributes

    matz.attributes.keys.each do |the_attr|
      assert known_attrs.include?(the_attr), "should have found instance attr: #{the_attr} in known attributes, but only had: #{known_attrs.inspect}"
    end
    new_schema.keys.each do |the_attr|
      assert known_attrs.include?(the_attr), "should have found schema attr: #{the_attr} in known attributes, but only had: #{known_attrs.inspect}"
    end
  end


end