Proper tests. (#1)

* Add tests for Deserializable::Resource.

* Add codecov integration.

* Add tests for Deserializable::Relationship + fix bad arity.
This commit is contained in:
Lucas Hosseini 2016-11-19 22:13:33 +01:00 committed by GitHub
parent 2443af13cb
commit dca9420f7a
13 changed files with 514 additions and 184 deletions

View File

@ -16,4 +16,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'rake', '>=0.9'
spec.add_development_dependency 'rspec', '~>3.4'
spec.add_development_dependency 'codecov', '~> 0.1'
end

View File

@ -33,16 +33,26 @@ module JSONAPI
def deserialize!
@hash = {}
if @data.is_a?(Array)
ids = @data.map { |ri| ri['id'] }
types = @data.map { |ri| ri['type'] }
instance_exec(@document, ids, types, &self.class.has_many_block)
deserialize_has_many!
else
id = @data && @data['id']
type = @data && @data['type']
instance_exec(@document, id, type, &self.class.has_one_block)
deserialize_has_one!
end
end
def deserialize_has_one!
return unless self.class.has_one_block && @document.key?('data')
id = @data && @data['id']
type = @data && @data['type']
instance_exec(@document, id, type, &self.class.has_one_block)
end
def deserialize_has_many!
return unless self.class.has_many_block
ids = @data.map { |ri| ri['id'] }
types = @data.map { |ri| ri['type'] }
instance_exec(@document, ids, types, &self.class.has_many_block)
end
def field(hash)
@hash.merge!(hash)
end

View File

@ -7,12 +7,12 @@ module JSONAPI
module ClassMethods
def has_one(&block)
block ||= proc { |rel| field key.to_sym => rel }
block ||= proc { |rel| field relationship: rel }
self.has_one_block = block
end
def has_many(&block)
block ||= proc { |rel| field key.to_sym => rel }
block ||= proc { |rel| field relationship: rel }
self.has_many_block = block
end
end

View File

@ -1,44 +0,0 @@
require 'jsonapi/deserializable'
describe JSONAPI::Deserializable::Relationship, '#to_h' do
it 'deserializes has_one relationships' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Relationship) do
has_one do |rel|
field sponsor_id: (rel['data'] && rel['data']['id'])
end
end
payload = {
'data' => {
'type' => 'users',
'id' => '1'
}
}
actual = deserializable_klass.(payload)
expected = { sponsor_id: '1' }
expect(actual).to eq(expected)
end
it 'deserializes has_many relationships' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Relationship) do
has_many do |rel|
field post_ids: rel['data'].map { |ri| ri['id'] }
end
end
payload = {
'data' => [
{ 'type' => 'postd', 'id' => '1' },
{ 'type' => 'postd', 'id' => '2' },
{ 'type' => 'postd', 'id' => '3' }
]
}
actual = deserializable_klass.(payload)
expected = { post_ids: %w(1 2 3) }
expect(actual).to eq(expected)
end
end

View File

@ -1,132 +0,0 @@
require 'jsonapi/deserializable'
describe JSONAPI::Deserializable::Resource, '#to_h' do
before(:all) do
@payload = {
'data' => {
'id' => '1',
'type' => 'users',
'attributes' => {
'name' => 'Name',
'address' => 'Address'
},
'relationships' => {
'sponsor' => {
'data' => { 'type' => 'users', 'id' => '1337' }
},
'posts' => {
'data' => [
{ 'type' => 'posts', 'id' => '123' },
{ 'type' => 'posts', 'id' => '234' },
{ 'type' => 'posts', 'id' => '345' }
]
}
}
}
}
end
it 'deserializes primary type' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
type { |type| field type: type }
end
actual = deserializable_klass.(@payload)
expected = { type: 'users' }
expect(actual).to eq(expected)
end
it 'deserializes primary id when present' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |id| field id: id }
end
actual = deserializable_klass.(@payload)
expected = { id: '1' }
expect(actual).to eq(expected)
end
it 'does not deserialize primary id when absent' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |id| field id: id }
end
payload = {
'data' => { 'type' => 'users' }
}
actual = deserializable_klass.(payload)
expected = {}
expect(actual).to eq(expected)
end
it 'handles attributes' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:name) { |name| field username: name }
attribute(:address) { |address| field address: address }
end
actual = deserializable_klass.(@payload)
expected = {
username: 'Name',
address: 'Address'
}
expect(actual).to eq(expected)
end
it 'handles has_one relationships' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
has_one(:sponsor) { |rel| field sponsor_id: rel['data']['id'] }
end
actual = deserializable_klass.(@payload)
expected = {
sponsor_id: '1337'
}
expect(actual).to eq(expected)
end
it 'handles has_many relationships' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
has_many(:posts) do |rel|
field post_ids: rel['data'].map { |ri| ri['id'] }
end
end
actual = deserializable_klass.(@payload)
expected = {
post_ids: %w(123 234 345)
}
expect(actual).to eq(expected)
end
it 'works' do
deserializable_klass = Class.new(JSONAPI::Deserializable::Resource) do
id
attribute(:name) { |name| field username: name }
attribute :address
has_one :sponsor do |_, id|
field sponsor_id: id
end
has_many :posts do |_, ids|
field post_ids: ids
end
end
actual = deserializable_klass.(@payload)
expected = {
id: '1',
username: 'Name',
address: 'Address',
sponsor_id: '1337',
post_ids: %w(123 234 345)
}
expect(actual).to eq(expected)
end
end

View File

@ -0,0 +1,72 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Relationship, '.has_many' do
let(:deserializable_foo) do
Class.new(JSONAPI::Deserializable::Relationship) do
has_many do |rel, ids, types|
field foo_ids: ids
field foo_types: types
field foo_rel: rel
end
end
end
context 'relationship is not empty' do
let(:payload) do
{
'data' => [
{ 'type' => 'foo', 'id' => 'bar' },
{ 'type' => 'foo', 'id' => 'baz' }
]
}
end
it 'creates corresponding fields' do
actual = deserializable_foo.call(payload)
expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo),
foo_rel: payload }
expect(actual).to eq(expected)
end
it 'defaults to creating a relationship field' do
klass = Class.new(JSONAPI::Deserializable::Relationship) do
has_many
end
actual = klass.call(payload)
expected = { relationship: payload }
expect(actual).to eq(expected)
end
end
context 'relationship is empty' do
it 'creates corresponding fields' do
payload = { 'data' => [] }
actual = deserializable_foo.call(payload)
expected = { foo_ids: [], foo_types: [], foo_rel: payload }
expect(actual).to eq(expected)
end
end
context 'data is absent' do
it 'does not create corresponding fields' do
payload = {}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
context 'relationship is not to-many' do
it 'does not create corresponding fields' do
payload = { 'data' => nil }
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
end

View File

@ -0,0 +1,66 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Relationship, '.has_one' do
let(:deserializable_foo) do
Class.new(JSONAPI::Deserializable::Relationship) do
has_one do |rel, id, type|
field foo_id: id
field foo_type: type
field foo_rel: rel
end
end
end
context 'relationship is not nil' do
let(:payload) do
{ 'data' => { 'type' => 'foo', 'id' => 'bar' } }
end
it 'creates corresponding fields' do
actual = deserializable_foo.call(payload)
expected = { foo_id: 'bar', foo_type: 'foo', foo_rel: payload }
expect(actual).to eq(expected)
end
it 'defaults to creating a relationship field' do
klass = Class.new(JSONAPI::Deserializable::Relationship) do
has_one
end
actual = klass.call(payload)
expected = { relationship: payload }
expect(actual).to eq(expected)
end
end
context 'relationship is nil' do
it 'creates corresponding fields' do
payload = { 'data' => nil }
actual = deserializable_foo.call(payload)
expected = { foo_id: nil, foo_type: nil, foo_rel: payload }
expect(actual).to eq(expected)
end
end
context 'data is absent' do
it 'does not create corresponding fields' do
payload = {}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
context 'relationship is not to-one' do
it 'does not create corresponding fields' do
payload = { 'data' => [] }
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
end

View File

@ -0,0 +1,57 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.attribute' do
it 'creates corresponding field if attribute is present' do
payload = {
'data' => {
'type' => 'foo',
'attributes' => { 'foo' => 'bar' }
}
}
klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) { |foo| field foo: foo }
end
actual = klass.call(payload)
expected = { foo: 'bar' }
expect(actual).to eq(expected)
end
it 'does not create corresponding field if attribute is absent' do
payload = { 'data' => { 'type' => 'foo' }, 'attributes' => {} }
klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) { |foo| field foo: foo }
end
actual = klass.call(payload)
expected = {}
expect(actual).to eq(expected)
end
it 'does not create corresponding field if no attribute specified' do
payload = { 'data' => { 'type' => 'foo' } }
klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) { |foo| field foo: foo }
end
actual = klass.call(payload)
expected = {}
expect(actual).to eq(expected)
end
it 'defaults to creating a field with same name' do
payload = {
'data' => {
'type' => 'foo',
'attributes' => { 'foo' => 'bar' }
}
}
klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute :foo
end
actual = klass.call(payload)
expected = { foo: 'bar' }
expect(actual).to eq(expected)
end
end

View File

@ -0,0 +1,118 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.has_many' do
let(:deserializable_foo) do
Class.new(JSONAPI::Deserializable::Resource) do
has_many :foo do |rel, ids, types|
field foo_ids: ids
field foo_types: types
field foo_rel: rel
end
end
end
context 'relationship is not empty' do
let(:payload) do
{
'data' => {
'type' => 'foo',
'relationships' => {
'foo' => {
'data' => [
{ 'type' => 'foo', 'id' => 'bar' },
{ 'type' => 'foo', 'id' => 'baz' }
]
}
}
}
}
end
it 'creates corresponding fields' do
actual = deserializable_foo.call(payload)
expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo),
foo_rel: payload['data']['relationships']['foo'] }
expect(actual).to eq(expected)
end
it 'defaults to creating a field of the same name' do
klass = Class.new(JSONAPI::Deserializable::Resource) do
has_many :foo
end
actual = klass.call(payload)
expected = { foo: payload['data']['relationships']['foo'] }
expect(actual).to eq(expected)
end
end
context 'relationship is empty' do
it 'creates corresponding fields' do
payload = {
'data' => {
'type' => 'foo',
'relationships' => {
'foo' => {
'data' => []
}
}
}
}
actual = deserializable_foo.call(payload)
expected = { foo_ids: [], foo_types: [],
foo_rel: payload['data']['relationships']['foo'] }
expect(actual).to eq(expected)
end
end
context 'relationship is absent' do
it 'does not create corresponding fields' do
payload = {
'data' => {
'type' => 'foo',
'relationships' => {}
}
}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
context 'there is no relationships member' do
it 'does not create corresponding fields' do
payload = {
'data' => {
'type' => 'foo'
}
}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
context 'relationship is not to-many' do
it 'does not create corresponding fields' do
payload = {
'data' => {
'type' => 'foo',
'relationships' => {
'foo' => {
'data' => { 'type' => 'foo', 'id' => 'bar' }
}
}
}
}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
end

View File

@ -0,0 +1,114 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.has_one' do
let(:deserializable_foo) do
Class.new(JSONAPI::Deserializable::Resource) do
has_one :foo do |rel, id, type|
field foo_id: id
field foo_type: type
field foo_rel: rel
end
end
end
context 'relationship is not nil' do
let(:payload) do
{
'data' => {
'type' => 'foo',
'relationships' => {
'foo' => {
'data' => { 'type' => 'foo', 'id' => 'bar' }
}
}
}
}
end
it 'creates corresponding fields' do
actual = deserializable_foo.call(payload)
expected = { foo_id: 'bar', foo_type: 'foo',
foo_rel: payload['data']['relationships']['foo'] }
expect(actual).to eq(expected)
end
it 'defaults to creating a field of the same name' do
klass = Class.new(JSONAPI::Deserializable::Resource) do
has_one :foo
end
actual = klass.call(payload)
expected = { foo: payload['data']['relationships']['foo'] }
expect(actual).to eq(expected)
end
end
context 'relationship is nil' do
it 'creates corresponding fields' do
payload = {
'data' => {
'type' => 'foo',
'relationships' => {
'foo' => {
'data' => nil
}
}
}
}
actual = deserializable_foo.call(payload)
expected = { foo_id: nil, foo_type: nil,
foo_rel: payload['data']['relationships']['foo'] }
expect(actual).to eq(expected)
end
end
context 'relationship is absent' do
it 'does not create corresponding fields' do
payload = {
'data' => {
'type' => 'foo',
'relationships' => {}
}
}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
context 'there is no relationships member' do
it 'does not create corresponding fields' do
payload = {
'data' => {
'type' => 'foo'
}
}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
context 'relationship is not to-one' do
it 'does not create corresponding fields' do
payload = {
'data' => {
'type' => 'foo',
'relationships' => {
'foo' => {
'data' => []
}
}
}
}
actual = deserializable_foo.call(payload)
expected = {}
expect(actual).to eq(expected)
end
end
end

View File

@ -0,0 +1,36 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.id' do
it 'creates corresponding field if id is present' do
payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } }
klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |i| field id: i }
end
actual = klass.call(payload)
expected = { id: 'bar' }
expect(actual).to eq(expected)
end
it 'does not create corresponding field if id is absent' do
payload = { 'data' => { 'type' => 'foo' } }
klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |i| field id: i }
end
actual = klass.call(payload)
expected = {}
expect(actual).to eq(expected)
end
it 'defaults to creating an id field' do
payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } }
klass = Class.new(JSONAPI::Deserializable::Resource) do
id
end
actual = klass.call(payload)
expected = { id: 'bar' }
expect(actual).to eq(expected)
end
end

View File

@ -0,0 +1,25 @@
require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.type' do
it 'creates corresponding field' do
payload = { 'data' => { 'type' => 'foo' } }
klass = Class.new(JSONAPI::Deserializable::Resource) do
type { |t| field type: t }
end
actual = klass.call(payload)
expected = { type: 'foo' }
expect(actual).to eq(expected)
end
it 'defaults to creating a type field' do
payload = { 'data' => { 'type' => 'foo' } }
klass = Class.new(JSONAPI::Deserializable::Resource) do
type
end
actual = klass.call(payload)
expected = { type: 'foo' }
expect(actual).to eq(expected)
end
end

7
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,7 @@
require 'simplecov'
SimpleCov.start
require 'codecov'
SimpleCov.formatter = SimpleCov::Formatter::Codecov
require 'jsonapi/deserializable'