test/controllers/controller_test.rb in jsonapi-resources-0.3.3 vs test/controllers/controller_test.rb in jsonapi-resources-0.4.0

- old
+ new

@@ -2,14 +2,10 @@ def set_content_type_header! @request.headers['Content-Type'] = JSONAPI::MEDIA_TYPE end -class ConfigControllerTest < ActionController::TestCase - -end - class PostsControllerTest < ActionController::TestCase def test_index get :index assert_response :success assert json_response['data'].is_a?(Array) @@ -55,33 +51,49 @@ assert_response :success assert_equal 2, json_response['data'].size assert_equal 1, json_response['included'].size end + def test_index_include_one_level_query_count + query_count = count_queries do + get :index, {include: 'author'} + end + assert_response :success + assert_equal 2, query_count + end + + def test_index_include_two_levels_query_count + query_count = count_queries do + get :index, {include: 'author,author.comments'} + end + assert_response :success + assert_equal 3, query_count + end + def test_index_filter_by_ids_and_fields get :index, {filter: {id: '1,2'}, fields: {posts: 'id,title,author'}} assert_response :success assert_equal 2, json_response['data'].size - # type, id, title, links - assert_equal 4, json_response['data'][0].size + # type, id, links, attributes, relationships + assert_equal 5, json_response['data'][0].size assert json_response['data'][0].has_key?('type') assert json_response['data'][0].has_key?('id') - assert json_response['data'][0].has_key?('title') + assert json_response['data'][0]['attributes'].has_key?('title') assert json_response['data'][0].has_key?('links') end def test_index_filter_by_ids_and_fields_specify_type get :index, {filter: {id: '1,2'}, 'fields' => {'posts' => 'id,title,author'}} assert_response :success assert_equal 2, json_response['data'].size - # type, id, title, links - assert_equal 4, json_response['data'][0].size + # type, id, links, attributes, relationships + assert_equal 5, json_response['data'][0].size assert json_response['data'][0].has_key?('type') assert json_response['data'][0].has_key?('id') - assert json_response['data'][0].has_key?('title') + assert json_response['data'][0]['attributes'].has_key?('title') assert json_response['data'][0].has_key?('links') end def test_index_filter_by_ids_and_fields_specify_unrelated_type get :index, {filter: {id: '1,2'}, 'fields' => {'currencies' => 'code'}} @@ -92,15 +104,15 @@ def test_index_filter_by_ids_and_fields_2 get :index, {filter: {id: '1,2'}, fields: {posts: 'author'}} assert_response :success assert_equal 2, json_response['data'].size - # links, id, type - assert_equal 3, json_response['data'][0].size + # type, id, links, relationships + assert_equal 4, json_response['data'][0].size assert json_response['data'][0].has_key?('type') assert json_response['data'][0].has_key?('id') - assert json_response['data'][0]['links'].has_key?('author') + assert json_response['data'][0]['relationships'].has_key?('author') end def test_filter_association_single get :index, {filter: {tags: '5,1'}} assert_response :success @@ -170,95 +182,71 @@ assert_response :success assert_equal 3, json_response['data'].size end def test_sorting_asc - get :index, {sort: '+title'} + get :index, {sort: 'title'} assert_response :success - assert_equal "A First Post", json_response['data'][0]['title'] + assert_equal "A First Post", json_response['data'][0]['attributes']['title'] end - # Plus symbol may be replaced by a space - def test_sorting_asc_with_space - get :index, {sort: ' title'} - - assert_response :success - assert_equal "A First Post", json_response['data'][0]['title'] - end - - # Plus symbol may be sent uriencoded ('%2b') - def test_sorting_asc_with_encoded_plus - get :index, {sort: '%2btitle'} - - assert_response :success - assert_equal "A First Post", json_response['data'][0]['title'] - end - def test_sorting_desc get :index, {sort: '-title'} assert_response :success - assert_equal "Update This Later - Multiple", json_response['data'][0]['title'] + assert_equal "Update This Later - Multiple", json_response['data'][0]['attributes']['title'] end def test_sorting_by_multiple_fields - get :index, {sort: '+title,+body'} + get :index, {sort: 'title,body'} assert_response :success assert_equal '14', json_response['data'][0]['id'] end def test_invalid_sort_param - get :index, {sort: '+asdfg'} + get :index, {sort: 'asdfg'} assert_response :bad_request assert_match /asdfg is not a valid sort criteria for post/, response.body end - def test_invalid_sort_param_missing_direction - get :index, {sort: 'title'} - - assert_response :bad_request - assert_match /title must start with a direction/, response.body - end - def test_excluded_sort_param - get :index, {sort: '+id'} + get :index, {sort: 'id'} assert_response :bad_request assert_match /id is not a valid sort criteria for post/, response.body end def test_show_single get :show, {id: '1'} assert_response :success assert json_response['data'].is_a?(Hash) - assert_equal 'New post', json_response['data']['title'] - assert_equal 'A body!!!', json_response['data']['body'] + assert_equal 'New post', json_response['data']['attributes']['title'] + assert_equal 'A body!!!', json_response['data']['attributes']['body'] assert_nil json_response['included'] end def test_show_single_with_includes get :show, {id: '1', include: 'comments'} assert_response :success assert json_response['data'].is_a?(Hash) - assert_equal 'New post', json_response['data']['title'] - assert_equal 'A body!!!', json_response['data']['body'] - assert_nil json_response['data']['links']['tags']['linkage'] + assert_equal 'New post', json_response['data']['attributes']['title'] + assert_equal 'A body!!!', json_response['data']['attributes']['body'] + assert_nil json_response['data']['relationships']['tags']['data'] assert matches_array?([{'type' => 'comments', 'id' => '1'}, {'type' => 'comments', 'id' => '2'}], - json_response['data']['links']['comments']['linkage']) + json_response['data']['relationships']['comments']['data']) assert_equal 2, json_response['included'].size end def test_show_single_with_fields get :show, {id: '1', fields: {posts: 'author'}} assert_response :success assert json_response['data'].is_a?(Hash) - assert_nil json_response['data']['title'] - assert_nil json_response['data']['body'] - assert_equal '1', json_response['data']['links']['author']['linkage']['id'] + assert_nil json_response['data']['attributes'] + assert_equal '1', json_response['data']['relationships']['author']['data']['id'] end def test_show_single_with_fields_string get :show, {id: '1', fields: 'author'} assert_response :bad_request @@ -293,35 +281,39 @@ set_content_type_header! post :create, { data: { type: 'posts', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['author']['linkage']['id'] - assert_equal 'JR is Great', json_response['data']['title'] - assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['body'] + assert_equal '3', json_response['data']['relationships']['author']['data']['id'] + assert_equal 'JR is Great', json_response['data']['attributes']['title'] + assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['attributes']['body'] end def test_create_link_to_missing_object set_content_type_header! post :create, { data: { type: 'posts', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '304567'}} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '304567'}} } } } assert_response :unprocessable_entity @@ -333,15 +325,17 @@ set_content_type_header! post :create, { data: { type: 'posts', - asdfg: 'aaaa', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + asdfg: 'aaaa', + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } } assert_response :bad_request @@ -352,13 +346,15 @@ set_content_type_header! post :create, { data: { type: 'posts', - title: 'JSONAPIResources is the greatest thing...', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { + attributes: { + title: 'JSONAPIResources is the greatest thing...', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { author: nil } } } @@ -378,31 +374,35 @@ post :create, { data: [ { type: 'posts', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } }, { type: 'posts', - title: 'Ember is Great', - body: 'Ember is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'Ember is Great', + body: 'Ember is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } ] } assert_response :created assert json_response['data'].is_a?(Array) assert_equal json_response['data'].size, 2 - assert_equal json_response['data'][0]['links']['author']['linkage']['id'], '3' + assert_equal json_response['data'][0]['relationships']['author']['data']['id'], '3' assert_match /JR is Great/, response.body assert_match /Ember is Great/, response.body end def test_create_multiple_wrong_case @@ -410,22 +410,26 @@ post :create, { data: [ { type: 'posts', - Title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + Title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } }, { type: 'posts', - title: 'Ember is Great', - BODY: 'Ember is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'Ember is Great', + BODY: 'Ember is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } ] } @@ -437,14 +441,16 @@ set_content_type_header! post :create, { data_spelled_wrong: { type: 'posts', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } } assert_response :bad_request @@ -455,14 +461,16 @@ set_content_type_header! post :create, { data: { type: 'posts_spelled_wrong', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } } assert_response :bad_request @@ -472,14 +480,16 @@ def test_create_simple_missing_type set_content_type_header! post :create, { data: { - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } } assert_response :bad_request @@ -490,14 +500,16 @@ set_content_type_header! post :create, { data: { type: 'posts', - subject: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}} + attributes: { + subject: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}} } } } assert_response :bad_request @@ -508,69 +520,75 @@ set_content_type_header! post :create, { data: { type: 'posts', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } } } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['author']['linkage']['id'] - assert_equal 'JR is Great', json_response['data']['title'] - assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['body'] + assert_equal '3', json_response['data']['relationships']['author']['data']['id'] + assert_equal 'JR is Great', json_response['data']['attributes']['title'] + assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['attributes']['body'] end def test_create_with_links_has_many_array set_content_type_header! post :create, { data: { type: 'posts', - title: 'JR is Great', - body: 'JSONAPIResources is the greatest thing since unsliced bread.', - links: { - author: {linkage: {type: 'people', id: '3'}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'JR is Great', + body: 'JSONAPIResources is the greatest thing since unsliced bread.' + }, + relationships: { + author: {data: {type: 'people', id: '3'}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } } } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['author']['linkage']['id'] - assert_equal 'JR is Great', json_response['data']['title'] - assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['body'] + assert_equal '3', json_response['data']['relationships']['author']['data']['id'] + assert_equal 'JR is Great', json_response['data']['attributes']['title'] + assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['attributes']['body'] end def test_create_with_links_include_and_fields set_content_type_header! post :create, { data: { type: 'posts', - title: 'JR is Great!', - body: 'JSONAPIResources is the greatest thing since unsliced bread!', - links: { - author: {linkage: {type: 'people', id: '3'}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'JR is Great!', + body: 'JSONAPIResources is the greatest thing since unsliced bread!' + }, + relationships: { + author: {data: {type: 'people', id: '3'}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } }, include: 'author,author.posts', fields: {posts: 'id,title,author'} } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['author']['linkage']['id'] - assert_equal 'JR is Great!', json_response['data']['title'] + assert_equal '3', json_response['data']['relationships']['author']['data']['id'] + assert_equal 'JR is Great!', json_response['data']['attributes']['title'] assert_not_nil json_response['included'].size end def test_update_with_links set_content_type_header! @@ -580,41 +598,45 @@ { id: 3, data: { id: '3', type: 'posts', - title: 'A great new Post', - links: { - section: {linkage: {type: 'sections', id: "#{javascript.id}"}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'A great new Post' + }, + relationships: { + section: {data: {type: 'sections', id: "#{javascript.id}"}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } }, include: 'tags' } assert_response :success assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['author']['linkage']['id'] - assert_equal javascript.id.to_s, json_response['data']['links']['section']['linkage']['id'] - assert_equal 'A great new Post', json_response['data']['title'] - assert_equal 'AAAA', json_response['data']['body'] + assert_equal '3', json_response['data']['relationships']['author']['data']['id'] + assert_equal javascript.id.to_s, json_response['data']['relationships']['section']['data']['id'] + assert_equal 'A great new Post', json_response['data']['attributes']['title'] + assert_equal 'AAAA', json_response['data']['attributes']['body'] assert matches_array?([{'type' => 'tags', 'id' => '3'}, {'type' => 'tags', 'id' => '4'}], - json_response['data']['links']['tags']['linkage']) + json_response['data']['relationships']['tags']['data']) end def test_update_remove_links set_content_type_header! put :update, { id: 3, data: { id: '3', type: 'posts', - title: 'A great new Post', - links: { - section: {linkage: {type: 'sections', id: 1}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'A great new Post' + }, + relationships: { + section: {data: {type: 'sections', id: 1}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } }, include: 'tags' } @@ -624,27 +646,29 @@ { id: 3, data: { type: 'posts', id: 3, - title: 'A great new Post', - links: { + attributes: { + title: 'A great new Post' + }, + relationships: { section: nil, tags: [] } }, include: 'tags' } assert_response :success assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['author']['linkage']['id'] - assert_equal nil, json_response['data']['links']['section']['linkage'] - assert_equal 'A great new Post', json_response['data']['title'] - assert_equal 'AAAA', json_response['data']['body'] + assert_equal '3', json_response['data']['relationships']['author']['data']['id'] + assert_equal nil, json_response['data']['relationships']['section']['data'] + assert_equal 'A great new Post', json_response['data']['attributes']['title'] + assert_equal 'AAAA', json_response['data']['attributes']['body'] assert matches_array?([], - json_response['data']['links']['tags']['linkage']) + json_response['data']['relationships']['tags']['data']) end def test_update_relationship_has_one set_content_type_header! ruby = Section.find_by(name: 'ruby') @@ -696,11 +720,11 @@ { id: 3, data: { type: 'posts', id: 3, - links: { + relationships: { tags: nil } } } @@ -714,12 +738,12 @@ { id: 3, data: { type: 'posts', id: 3, - links: { - tags: {linkage: {typ: 'bad link', idd: 'as'}} + relationships: { + tags: {data: {typ: 'bad link', idd: 'as'}} } } } assert_response :bad_request @@ -732,30 +756,30 @@ { id: 3, data: { type: 'posts', id: 3, - links: { + relationships: { tags: 'bad link' } } } assert_response :bad_request assert_match /Invalid Links Object/, response.body end - def test_update_other_has_many_links_linkage_nil + def test_update_other_has_many_links_data_nil set_content_type_header! put :update, { id: 3, data: { type: 'posts', id: 3, - links: { - tags: {linkage: nil} + relationships: { + tags: {data: nil} } } } assert_response :bad_request @@ -977,12 +1001,14 @@ { id: 3, data: { type: 'posts', id: 2, - title: 'A great new Post', - links: { + attributes: { + title: 'A great new Post' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } } @@ -999,13 +1025,15 @@ { id: 3, data: { type: 'posts', id: '3', - asdfg: 'aaaa', - title: 'A great new Post', - links: { + attributes: { + asdfg: 'aaaa', + title: 'A great new Post' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } } @@ -1022,12 +1050,14 @@ { id: 3, data: { type: 'posts', id: '3', - title: 'A great new Post', - links: { + attributes: { + title: 'A great new Post' + }, + relationships: { asdfg: 'aaaa', section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } @@ -1044,12 +1074,14 @@ put :update, { id: 3, data_spelled_wrong: { type: 'posts', - title: 'A great new Post', - links: { + attributes: { + title: 'A great new Post' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } } @@ -1064,11 +1096,13 @@ put :update, { id: 3, data: { type: 'posts', - title: 'A great new Post' + attributes: { + title: 'A great new Post' + } } } assert_response :bad_request assert_match /The resource object does not contain a key/, response.body @@ -1082,22 +1116,49 @@ { id: 3, data: { id: '3', type_spelled_wrong: 'posts', - title: 'A great new Post', - links: { + attributes: { + title: 'A great new Post' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } } assert_response :bad_request assert_match /The required parameter, type, is missing./, response.body end + def test_update_unknown_key + set_content_type_header! + javascript = Section.find_by(name: 'javascript') + + put :update, + { + id: 3, + data: { + id: '3', + type: 'posts', + body: 'asdfg', + attributes: { + title: 'A great new Post' + }, + relationships: { + section: {type: 'sections', id: "#{javascript.id}"}, + tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] + } + } + } + + assert_response :bad_request + assert_match /body is not allowed/, response.body + end + def test_update_multiple set_content_type_header! javascript = Section.find_by(name: 'javascript') put :update, @@ -1105,44 +1166,48 @@ id: [3, 16], data: [ { type: 'posts', id: 3, - title: 'A great new Post QWERTY', - links: { - section: {linkage: {type: 'sections', id: "#{javascript.id}"}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'A great new Post QWERTY' + }, + relationships: { + section: {data: {type: 'sections', id: "#{javascript.id}"}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } }, { type: 'posts', id: 16, - title: 'A great new Post ASDFG', - links: { - section: {linkage: {type: 'sections', id: "#{javascript.id}"}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'A great new Post ASDFG' + }, + relationships: { + section: {data: {type: 'sections', id: "#{javascript.id}"}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } } ], include: 'tags' } assert_response :success assert_equal json_response['data'].size, 2 - assert_equal json_response['data'][0]['links']['author']['linkage']['id'], '3' - assert_equal json_response['data'][0]['links']['section']['linkage']['id'], javascript.id.to_s - assert_equal json_response['data'][0]['title'], 'A great new Post QWERTY' - assert_equal json_response['data'][0]['body'], 'AAAA' + assert_equal json_response['data'][0]['relationships']['author']['data']['id'], '3' + assert_equal json_response['data'][0]['relationships']['section']['data']['id'], javascript.id.to_s + assert_equal json_response['data'][0]['attributes']['title'], 'A great new Post QWERTY' + assert_equal json_response['data'][0]['attributes']['body'], 'AAAA' assert matches_array?([{'type' => 'tags', 'id' => '3'}, {'type' => 'tags', 'id' => '4'}], - json_response['data'][0]['links']['tags']['linkage']) + json_response['data'][0]['relationships']['tags']['data']) - assert_equal json_response['data'][1]['links']['author']['linkage']['id'], '3' - assert_equal json_response['data'][1]['links']['section']['linkage']['id'], javascript.id.to_s - assert_equal json_response['data'][1]['title'], 'A great new Post ASDFG' - assert_equal json_response['data'][1]['body'], 'Not First!!!!' + assert_equal json_response['data'][1]['relationships']['author']['data']['id'], '3' + assert_equal json_response['data'][1]['relationships']['section']['data']['id'], javascript.id.to_s + assert_equal json_response['data'][1]['attributes']['title'], 'A great new Post ASDFG' + assert_equal json_response['data'][1]['attributes']['body'], 'Not First!!!!' assert matches_array?([{'type' => 'tags', 'id' => '3'}, {'type' => 'tags', 'id' => '4'}], - json_response['data'][1]['links']['tags']['linkage']) + json_response['data'][1]['relationships']['tags']['data']) end def test_update_multiple_missing_keys set_content_type_header! javascript = Section.find_by(name: 'javascript') @@ -1151,20 +1216,24 @@ { id: [3, 9], data: [ { type: 'posts', - title: 'A great new Post ASDFG', - links: { + attributes: { + title: 'A great new Post ASDFG' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } }, { type: 'posts', - title: 'A great new Post QWERTY', - links: { + attributes: { + title: 'A great new Post QWERTY' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } ]} @@ -1182,23 +1251,27 @@ id: [3, 9], data: [ { type: 'posts', id: 3, - title: 'A great new Post ASDFG', - links: { - section: {linkage: {type: 'sections', id: "#{javascript.id}"}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'A great new Post ASDFG' + }, + relationships: { + section: {data: {type: 'sections', id: "#{javascript.id}"}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } }, { type: 'posts', id: 8, - title: 'A great new Post QWERTY', - links: { - section: {linkage: {type: 'sections', id: "#{javascript.id}"}}, - tags: {linkage: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} + attributes: { + title: 'A great new Post QWERTY' + }, + relationships: { + section: {data: {type: 'sections', id: "#{javascript.id}"}}, + tags: {data: [{type: 'tags', id: 3}, {type: 'tags', id: 4}]} } } ]} assert_response :bad_request @@ -1214,21 +1287,25 @@ id: [3, 9, 2], data: [ { type: 'posts', id: 3, - title: 'A great new Post QWERTY', - links: { + attributes: { + title: 'A great new Post QWERTY' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } }, { type: 'posts', id: 9, - title: 'A great new Post ASDFG', - links: { + attributes: { + title: 'A great new Post ASDFG' + }, + relationships: { section: {type: 'sections', id: "#{javascript.id}"}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } ]} @@ -1243,12 +1320,14 @@ { id: 3, data: { type: 'posts', id: '3', - subject: 'A great new Post', - links: { + attributes: { + subject: 'A great new Post' + }, + relationships: { author: {type: 'people', id: '1'}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } } @@ -1263,11 +1342,13 @@ put :update, { id: 3, data: { type: 'posts', - subject: 'A great new Post', + attributes: { + subject: 'A great new Post' + }, linked_objects: { author: {type: 'people', id: '1'}, tags: [{type: 'tags', id: 3}, {type: 'tags', id: 4}] } } @@ -1311,11 +1392,11 @@ {data: { type: 'people', id: '1' }, links: { - self: 'http://test.host/posts/1/links/author', + self: 'http://test.host/posts/1/relationships/author', related: 'http://test.host/posts/1/author' } } end @@ -1326,11 +1407,11 @@ { data: [ {type: 'tags', id: '5'} ], links: { - self: 'http://test.host/posts/2/links/tags', + self: 'http://test.host/posts/2/relationships/tags', related: 'http://test.host/posts/2/tags' } } end @@ -1345,33 +1426,33 @@ assert_response :success assert_hash_equals json_response, { data: nil, links: { - self: 'http://test.host/posts/17/links/author', + self: 'http://test.host/posts/17/relationships/author', related: 'http://test.host/posts/17/author' } } end end class TagsControllerTest < ActionController::TestCase def test_tags_index - get :index, {filter: {id: '6,7,8,9'}, include: 'posts,posts.tags,posts.author.posts'} + get :index, {filter: {id: '6,7,8,9'}, include: 'posts.tags,posts.author.posts'} assert_response :success assert_equal 4, json_response['data'].size - assert_equal 2, json_response['included'].size + assert_equal 3, json_response['included'].size end def test_tags_show_multiple get :show, {id: '6,7,8,9'} assert_response :bad_request assert_match /6,7,8,9 is not a valid value for id/, response.body end def test_tags_show_multiple_with_include - get :show, {id: '6,7,8,9', include: 'posts,posts.tags,posts.author.posts'} + get :show, {id: '6,7,8,9', include: 'posts.tags,posts.author.posts'} assert_response :bad_request assert_match /6,7,8,9 is not a valid value for id/, response.body end def test_tags_show_multiple_with_nonexistent_ids @@ -1394,11 +1475,11 @@ def test_text_error JSONAPI.configuration.use_text_errors = true get :index, {sort: 'not_in_record'} assert_response 400 - assert_equal 'INVALID_SORT_FORMAT', json_response['errors'][0]['code'] + assert_equal 'INVALID_SORT_CRITERIA', json_response['errors'][0]['code'] JSONAPI.configuration.use_text_errors = false end def test_expense_entries_index get :index @@ -1435,20 +1516,20 @@ def test_expense_entries_show_fields get :show, {id: 1, include: 'isoCurrency,employee', 'fields' => {'expenseEntries' => 'transactionDate'}} assert_response :success assert json_response['data'].is_a?(Hash) - assert json_response['data'].has_key?('transactionDate') + assert json_response['data']['attributes'].has_key?('transactionDate') assert_equal 2, json_response['included'].size end def test_expense_entries_show_fields_type_many get :show, {id: 1, include: 'isoCurrency,employee', 'fields' => {'expenseEntries' => 'transactionDate', 'isoCurrencies' => 'id,name'}} assert_response :success assert json_response['data'].is_a?(Hash) - assert json_response['data'].has_key?('transactionDate') + assert json_response['data']['attributes'].has_key?('transactionDate') assert_equal 2, json_response['included'].size end def test_create_expense_entries_underscored set_content_type_header! @@ -1456,26 +1537,28 @@ post :create, { data: { type: 'expense_entries', - transaction_date: '2014/04/15', - cost: 50.58, - links: { - employee: {linkage: {type: 'people', id: '3'}}, - iso_currency: {linkage: {type: 'iso_currencies', id: 'USD'}} + attributes: { + transaction_date: '2014/04/15', + cost: 50.58 + }, + relationships: { + employee: {data: {type: 'people', id: '3'}}, + iso_currency: {data: {type: 'iso_currencies', id: 'USD'}} } }, include: 'iso_currency', fields: {expense_entries: 'id,transaction_date,iso_currency,cost,employee'} } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['employee']['linkage']['id'] - assert_equal 'USD', json_response['data']['links']['iso_currency']['linkage']['id'] - assert_equal '50.58', json_response['data']['cost'] + assert_equal '3', json_response['data']['relationships']['employee']['data']['id'] + assert_equal 'USD', json_response['data']['relationships']['iso_currency']['data']['id'] + assert_equal '50.58', json_response['data']['attributes']['cost'] delete :destroy, {id: json_response['data']['id']} assert_response :no_content end @@ -1485,26 +1568,28 @@ post :create, { data: { type: 'expense_entries', - transactionDate: '2014/04/15', - cost: 50.58, - links: { - employee: {linkage: {type: 'people', id: '3'}}, - isoCurrency: {linkage: {type: 'iso_currencies', id: 'USD'}} + attributes: { + transactionDate: '2014/04/15', + cost: 50.58 + }, + relationships: { + employee: {data: {type: 'people', id: '3'}}, + isoCurrency: {data: {type: 'iso_currencies', id: 'USD'}} } }, include: 'isoCurrency', fields: {expenseEntries: 'id,transactionDate,isoCurrency,cost,employee'} } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['employee']['linkage']['id'] - assert_equal 'USD', json_response['data']['links']['isoCurrency']['linkage']['id'] - assert_equal '50.58', json_response['data']['cost'] + assert_equal '3', json_response['data']['relationships']['employee']['data']['id'] + assert_equal 'USD', json_response['data']['relationships']['isoCurrency']['data']['id'] + assert_equal '50.58', json_response['data']['attributes']['cost'] delete :destroy, {id: json_response['data']['id']} assert_response :no_content end @@ -1514,26 +1599,28 @@ post :create, { data: { type: 'expense_entries', - 'transaction-date' => '2014/04/15', - cost: 50.58, - links: { - employee: {linkage: {type: 'people', id: '3'}}, - 'iso-currency' => {linkage: {type: 'iso_currencies', id: 'USD'}} + attributes: { + 'transaction-date' => '2014/04/15', + cost: 50.58 + }, + relationships: { + employee: {data: {type: 'people', id: '3'}}, + 'iso-currency' => {data: {type: 'iso_currencies', id: 'USD'}} } }, include: 'iso-currency', fields: {'expense-entries' => 'id,transaction-date,iso-currency,cost,employee'} } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['employee']['linkage']['id'] - assert_equal 'USD', json_response['data']['links']['iso-currency']['linkage']['id'] - assert_equal '50.58', json_response['data']['cost'] + assert_equal '3', json_response['data']['relationships']['employee']['data']['id'] + assert_equal 'USD', json_response['data']['relationships']['iso-currency']['data']['id'] + assert_equal '50.58', json_response['data']['attributes']['cost'] delete :destroy, {id: json_response['data']['id']} assert_response :no_content end end @@ -1542,91 +1629,132 @@ def after_teardown JSONAPI.configuration.json_key_format = :camelized_key end def test_currencies_show - get :show, {code: 'USD'} + get :show, {id: 'USD'} assert_response :success assert json_response['data'].is_a?(Hash) end + def test_create_currencies_client_generated_id + set_content_type_header! + JSONAPI.configuration.json_key_format = :underscored_route + + post :create, + { + data: { + type: 'iso_currencies', + id: 'BTC', + attributes: { + name: 'Bit Coin', + 'country_name' => 'global', + 'minor_unit' => 'satoshi' + } + } + } + + assert_response :created + assert_equal 'BTC', json_response['data']['id'] + assert_equal 'Bit Coin', json_response['data']['attributes']['name'] + assert_equal 'global', json_response['data']['attributes']['country_name'] + assert_equal 'satoshi', json_response['data']['attributes']['minor_unit'] + + delete :destroy, {id: json_response['data']['id']} + assert_response :no_content + end + + def test_currencies_primary_key_sort + get :index, {sort: 'id'} + assert_response :success + assert_equal 3, json_response['data'].size + assert_equal 'CAD', json_response['data'][0]['id'] + assert_equal 'EUR', json_response['data'][1]['id'] + assert_equal 'USD', json_response['data'][2]['id'] + end + + def test_currencies_code_sort + get :index, {sort: 'code'} + assert_response :bad_request + end + def test_currencies_json_key_underscored_sort JSONAPI.configuration.json_key_format = :underscored_key - get :index, {sort: '+country_name'} + get :index, {sort: 'country_name'} assert_response :success assert_equal 3, json_response['data'].size - assert_equal 'Canada', json_response['data'][0]['country_name'] - assert_equal 'Euro Member Countries', json_response['data'][1]['country_name'] - assert_equal 'United States', json_response['data'][2]['country_name'] + assert_equal 'Canada', json_response['data'][0]['attributes']['country_name'] + assert_equal 'Euro Member Countries', json_response['data'][1]['attributes']['country_name'] + assert_equal 'United States', json_response['data'][2]['attributes']['country_name'] # reverse sort get :index, {sort: '-country_name'} assert_response :success assert_equal 3, json_response['data'].size - assert_equal 'United States', json_response['data'][0]['country_name'] - assert_equal 'Euro Member Countries', json_response['data'][1]['country_name'] - assert_equal 'Canada', json_response['data'][2]['country_name'] + assert_equal 'United States', json_response['data'][0]['attributes']['country_name'] + assert_equal 'Euro Member Countries', json_response['data'][1]['attributes']['country_name'] + assert_equal 'Canada', json_response['data'][2]['attributes']['country_name'] end def test_currencies_json_key_dasherized_sort JSONAPI.configuration.json_key_format = :dasherized_key - get :index, {sort: '+country-name'} + get :index, {sort: 'country-name'} assert_response :success assert_equal 3, json_response['data'].size - assert_equal 'Canada', json_response['data'][0]['country-name'] - assert_equal 'Euro Member Countries', json_response['data'][1]['country-name'] - assert_equal 'United States', json_response['data'][2]['country-name'] + assert_equal 'Canada', json_response['data'][0]['attributes']['country-name'] + assert_equal 'Euro Member Countries', json_response['data'][1]['attributes']['country-name'] + assert_equal 'United States', json_response['data'][2]['attributes']['country-name'] # reverse sort get :index, {sort: '-country-name'} assert_response :success assert_equal 3, json_response['data'].size - assert_equal 'United States', json_response['data'][0]['country-name'] - assert_equal 'Euro Member Countries', json_response['data'][1]['country-name'] - assert_equal 'Canada', json_response['data'][2]['country-name'] + assert_equal 'United States', json_response['data'][0]['attributes']['country-name'] + assert_equal 'Euro Member Countries', json_response['data'][1]['attributes']['country-name'] + assert_equal 'Canada', json_response['data'][2]['attributes']['country-name'] end def test_currencies_json_key_custom_json_key_sort JSONAPI.configuration.json_key_format = :upper_camelized_key - get :index, {sort: '+CountryName'} + get :index, {sort: 'CountryName'} assert_response :success assert_equal 3, json_response['data'].size - assert_equal 'Canada', json_response['data'][0]['CountryName'] - assert_equal 'Euro Member Countries', json_response['data'][1]['CountryName'] - assert_equal 'United States', json_response['data'][2]['CountryName'] + assert_equal 'Canada', json_response['data'][0]['attributes']['CountryName'] + assert_equal 'Euro Member Countries', json_response['data'][1]['attributes']['CountryName'] + assert_equal 'United States', json_response['data'][2]['attributes']['CountryName'] # reverse sort get :index, {sort: '-CountryName'} assert_response :success assert_equal 3, json_response['data'].size - assert_equal 'United States', json_response['data'][0]['CountryName'] - assert_equal 'Euro Member Countries', json_response['data'][1]['CountryName'] - assert_equal 'Canada', json_response['data'][2]['CountryName'] + assert_equal 'United States', json_response['data'][0]['attributes']['CountryName'] + assert_equal 'Euro Member Countries', json_response['data'][1]['attributes']['CountryName'] + assert_equal 'Canada', json_response['data'][2]['attributes']['CountryName'] end def test_currencies_json_key_underscored_filter JSONAPI.configuration.json_key_format = :underscored_key get :index, {filter: {country_name: 'Canada'}} assert_response :success assert_equal 1, json_response['data'].size - assert_equal 'Canada', json_response['data'][0]['country_name'] + assert_equal 'Canada', json_response['data'][0]['attributes']['country_name'] end def test_currencies_json_key_camelized_key_filter JSONAPI.configuration.json_key_format = :camelized_key get :index, {filter: {'countryName' => 'Canada'}} assert_response :success assert_equal 1, json_response['data'].size - assert_equal 'Canada', json_response['data'][0]['countryName'] + assert_equal 'Canada', json_response['data'][0]['attributes']['countryName'] end def test_currencies_json_key_custom_json_key_filter JSONAPI.configuration.json_key_format = :upper_camelized_key get :index, {filter: {'CountryName' => 'Canada'}} assert_response :success assert_equal 1, json_response['data'].size - assert_equal 'Canada', json_response['data'][0]['CountryName'] + assert_equal 'Canada', json_response['data'][0]['attributes']['CountryName'] end end class PeopleControllerTest < ActionController::TestCase def setup @@ -1637,13 +1765,15 @@ set_content_type_header! post :create, { data: { type: 'people', - name: 'Steve Jobs', - email: 'sj@email.zzz', - dateJoined: DateTime.parse('2014-1-30 4:20:00 UTC +00:00') + attributes: { + name: 'Steve Jobs', + email: 'sj@email.zzz', + dateJoined: DateTime.parse('2014-1-30 4:20:00 UTC +00:00') + } } } assert_response :success end @@ -1655,13 +1785,13 @@ { id: 3, data: { id: '3', type: 'people', - links: { + relationships: { 'hair-cut' => { - linkage: { + data: { type: 'hair-cuts', id: '1' } } } @@ -1674,11 +1804,13 @@ set_content_type_header! post :create, { data: { type: 'people', - email: 'sj@email.zzz' + attributes: { + email: 'sj@email.zzz' + } } } assert_response :unprocessable_entity assert_equal 2, json_response['errors'].size @@ -1694,11 +1826,13 @@ { id: 3, data: { id: '3', type: 'people', - name: '' + attributes: { + name: '' + } } } assert_response :unprocessable_entity assert_equal 1, json_response['errors'].size @@ -1721,11 +1855,11 @@ def test_valid_filter_value get :index, {filter: {name: 'Joe Author'}} assert_response :success assert_equal json_response['data'].size, 1 assert_equal json_response['data'][0]['id'], '1' - assert_equal json_response['data'][0]['name'], 'Joe Author' + assert_equal json_response['data'][0]['attributes']['name'], 'Joe Author' end def test_get_related_resource JSONAPI.configuration.json_key_format = :dasherized_key JSONAPI.configuration.route_format = :underscored_key @@ -1734,35 +1868,47 @@ assert_hash_equals( { data: { id: '1', type: 'people', - name: 'Joe Author', - email: 'joe@xyz.fake', - "date-joined" => '2013-08-07 16:25:00 -0400', + attributes: { + name: 'Joe Author', + email: 'joe@xyz.fake', + "date-joined" => '2013-08-07 16:25:00 -0400' + }, links: { - self: 'http://test.host/people/1', + self: 'http://test.host/people/1' + }, + relationships: { comments: { - self: 'http://test.host/people/1/links/comments', - related: 'http://test.host/people/1/comments' + links: { + self: 'http://test.host/people/1/relationships/comments', + related: 'http://test.host/people/1/comments' + } }, posts: { - self: 'http://test.host/people/1/links/posts', - related: 'http://test.host/people/1/posts' + links: { + self: 'http://test.host/people/1/relationships/posts', + related: 'http://test.host/people/1/posts' + } }, preferences: { - self: 'http://test.host/people/1/links/preferences', - related: 'http://test.host/people/1/preferences', - linkage: { + links: { + self: 'http://test.host/people/1/relationships/preferences', + related: 'http://test.host/people/1/preferences' + }, + data: { type: 'preferences', id: '1' } }, "hair-cut" => { - "self" => "http://test.host/people/1/links/hair_cut", - "related" => "http://test.host/people/1/hair_cut", - "linkage" => nil + "links" => { + "self" => "http://test.host/people/1/relationships/hair_cut", + "related" => "http://test.host/people/1/hair_cut" + }, + "data" => nil } } } }, json_response @@ -1785,39 +1931,39 @@ get :index, {filter: {id: '1'}} assert_response :success assert_equal 1, json_response['data'].size assert_equal '1', json_response['data'][0]['id'] assert_equal 'authors', json_response['data'][0]['type'] - assert_equal 'Joe Author', json_response['data'][0]['name'] - assert_equal nil, json_response['data'][0]['email'] + assert_equal 'Joe Author', json_response['data'][0]['attributes']['name'] + assert_equal nil, json_response['data'][0]['attributes']['email'] end def test_get_person_as_author_by_name_filter get :index, {filter: {name: 'thor'}} assert_response :success assert_equal 3, json_response['data'].size assert_equal '1', json_response['data'][0]['id'] - assert_equal 'Joe Author', json_response['data'][0]['name'] + assert_equal 'Joe Author', json_response['data'][0]['attributes']['name'] end end class BreedsControllerTest < ActionController::TestCase # Note: Breed names go through the TitleValueFormatter def test_poro_index get :index assert_response :success assert_equal '0', json_response['data'][0]['id'] - assert_equal 'Persian', json_response['data'][0]['name'] + assert_equal 'Persian', json_response['data'][0]['attributes']['name'] end def test_poro_show get :show, {id: '0'} assert_response :success assert json_response['data'].is_a?(Hash) assert_equal '0', json_response['data']['id'] - assert_equal 'Persian', json_response['data']['name'] + assert_equal 'Persian', json_response['data']['attributes']['name'] end def test_poro_show_multiple get :show, {id: '0,2'} @@ -1829,45 +1975,68 @@ set_content_type_header! post :create, { data: { type: 'breeds', - name: 'tabby' + attributes: { + name: 'tabby' + } } } - assert_response :created + assert_response :accepted assert json_response['data'].is_a?(Hash) - assert_equal 'Tabby', json_response['data']['name'] + assert_equal 'Tabby', json_response['data']['attributes']['name'] end + def test_poro_create_validation_error + set_content_type_header! + post :create, + { + data: { + type: 'breeds', + attributes: { + name: '' + } + } + } + + assert_equal 1, json_response['errors'].size + assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][0]['code'] + assert_match /name - can't be blank/, response.body + end + def test_poro_create_update set_content_type_header! post :create, { data: { type: 'breeds', - name: 'CALIC' + attributes: { + name: 'CALIC' + } } } - assert_response :created + assert_response :accepted assert json_response['data'].is_a?(Hash) - assert_equal 'Calic', json_response['data']['name'] + assert_equal 'Calic', json_response['data']['attributes']['name'] put :update, { id: json_response['data']['id'], data: { id: json_response['data']['id'], type: 'breeds', - name: 'calico' + attributes: { + name: 'calico' + } } } assert_response :success assert json_response['data'].is_a?(Hash) - assert_equal 'Calico', json_response['data']['name'] + assert_equal 'Calico', json_response['data']['attributes']['name'] end def test_poro_delete initial_count = $breed_data.breeds.keys.count delete :destroy, {id: '3'} @@ -1886,21 +2055,21 @@ class Api::V1::PostsControllerTest < ActionController::TestCase def test_show_post_namespaced get :show, {id: '1'} assert_response :success - assert_equal 'http://test.host/api/v1/posts/1/links/writer', json_response['data']['links']['writer']['self'] + assert_equal 'http://test.host/api/v1/posts/1/relationships/writer', json_response['data']['relationships']['writer']['links']['self'] end def test_show_post_namespaced_include get :show, {id: '1', include: 'writer'} assert_response :success - assert_equal '1', json_response['data']['links']['writer']['linkage']['id'] - assert_nil json_response['data']['links']['tags'] + assert_equal '1', json_response['data']['relationships']['writer']['data']['id'] + assert_nil json_response['data']['relationships']['tags'] assert_equal '1', json_response['included'][0]['id'] assert_equal 'writers', json_response['included'][0]['type'] - assert_equal 'joe@xyz.fake', json_response['included'][0]['email'] + assert_equal 'joe@xyz.fake', json_response['included'][0]['attributes']['email'] end def test_index_filter_on_association_namespaced get :index, {filter: {writer: '1'}} assert_response :success @@ -1909,33 +2078,35 @@ def test_sorting_desc_namespaced get :index, {sort: '-title'} assert_response :success - assert_equal "Update This Later - Multiple", json_response['data'][0]['title'] + assert_equal "Update This Later - Multiple", json_response['data'][0]['attributes']['title'] end def test_create_simple_namespaced set_content_type_header! post :create, { data: { type: 'posts', - title: 'JR - now with Namespacing', - body: 'JSONAPIResources is the greatest thing since unsliced bread now that it has namespaced resources.', - links: { - writer: { linkage: {type: 'writers', id: '3'}} + attributes: { + title: 'JR - now with Namespacing', + body: 'JSONAPIResources is the greatest thing since unsliced bread now that it has namespaced resources.' + }, + relationships: { + writer: { data: {type: 'writers', id: '3'}} } } } assert_response :created assert json_response['data'].is_a?(Hash) - assert_equal '3', json_response['data']['links']['writer']['linkage']['id'] - assert_equal 'JR - now with Namespacing', json_response['data']['title'] + assert_equal '3', json_response['data']['relationships']['writer']['data']['id'] + assert_equal 'JR - now with Namespacing', json_response['data']['attributes']['title'] assert_equal 'JSONAPIResources is the greatest thing since unsliced bread now that it has namespaced resources.', - json_response['data']['body'] + json_response['data']['attributes']['body'] end end class FactsControllerTest < ActionController::TestCase @@ -1945,43 +2116,71 @@ def test_type_formatting get :show, {id: '1'} assert_response :success assert json_response['data'].is_a?(Hash) - assert_equal 'Jane Author', json_response['data']['spouseName'] - assert_equal 'First man to run across Antartica.', json_response['data']['bio'] - assert_equal 23.89/45.6, json_response['data']['qualityRating'] - assert_equal '47000.56', json_response['data']['salary'] - assert_equal '2013-08-07T20:25:00Z', json_response['data']['dateTimeJoined'] - assert_equal '1965-06-30', json_response['data']['birthday'] - assert_equal '2000-01-01T20:00:00Z', json_response['data']['bedtime'] - assert_equal 'abc', json_response['data']['photo'] - assert_equal false, json_response['data']['cool'] + assert_equal 'Jane Author', json_response['data']['attributes']['spouseName'] + assert_equal 'First man to run across Antartica.', json_response['data']['attributes']['bio'] + assert_equal 23.89/45.6, json_response['data']['attributes']['qualityRating'] + assert_equal '47000.56', json_response['data']['attributes']['salary'] + assert_equal '2013-08-07T20:25:00Z', json_response['data']['attributes']['dateTimeJoined'] + assert_equal '1965-06-30', json_response['data']['attributes']['birthday'] + assert_equal '2000-01-01T20:00:00Z', json_response['data']['attributes']['bedtime'] + assert_equal 'abc', json_response['data']['attributes']['photo'] + assert_equal false, json_response['data']['attributes']['cool'] end end class Api::V2::BooksControllerTest < ActionController::TestCase + def setup + JSONAPI.configuration.json_key_format = :dasherized_key + end + def after_teardown Api::V2::BookResource.paginator :offset end def test_books_offset_pagination_no_params Api::V2::BookResource.paginator :offset get :index assert_response :success assert_equal 10, json_response['data'].size - assert_equal 'Book 0', json_response['data'][0]['title'] + assert_equal 'Book 0', json_response['data'][0]['attributes']['title'] end + def test_books_offset_pagination_no_params_includes_query_count_one_level + Api::V2::BookResource.paginator :offset + + query_count = count_queries do + get :index, {include: 'book-comments'} + end + assert_response :success + assert_equal 10, json_response['data'].size + assert_equal 'Book 0', json_response['data'][0]['attributes']['title'] + assert_equal 2, query_count + end + + def test_books_offset_pagination_no_params_includes_query_count_two_levels + Api::V2::BookResource.paginator :offset + + query_count = count_queries do + get :index, {include: 'book-comments,book-comments.author'} + end + assert_response :success + assert_equal 10, json_response['data'].size + assert_equal 'Book 0', json_response['data'][0]['attributes']['title'] + assert_equal 3, query_count + end + def test_books_offset_pagination Api::V2::BookResource.paginator :offset get :index, {page: {offset: 50, limit: 12}} assert_response :success assert_equal 12, json_response['data'].size - assert_equal 'Book 50', json_response['data'][0]['title'] + assert_equal 'Book 50', json_response['data'][0]['attributes']['title'] end def test_books_offset_pagination_bad_page_param Api::V2::BookResource.paginator :offset @@ -2004,10 +2203,18 @@ get :index, {page: {offset: 50, limit: -1}} assert_response :bad_request assert_match /-1 is not a valid value for limit page parameter./, json_response['errors'][0]['detail'] end + def test_books_offset_pagination_bad_param_offset_less_than_zero + Api::V2::BookResource.paginator :offset + + get :index, {page: {offset: -1, limit: 20}} + assert_response :bad_request + assert_match /-1 is not a valid value for offset page parameter./, json_response['errors'][0]['detail'] + end + def test_books_offset_pagination_invalid_page_format Api::V2::BookResource.paginator :offset get :index, {page: 50} assert_response :bad_request @@ -2018,29 +2225,29 @@ Api::V2::BookResource.paginator :paged get :index assert_response :success assert_equal 10, json_response['data'].size - assert_equal 'Book 0', json_response['data'][0]['title'] + assert_equal 'Book 0', json_response['data'][0]['attributes']['title'] end def test_books_paged_pagination_no_page Api::V2::BookResource.paginator :paged get :index, {page: {size: 12}} assert_response :success assert_equal 12, json_response['data'].size - assert_equal 'Book 0', json_response['data'][0]['title'] + assert_equal 'Book 0', json_response['data'][0]['attributes']['title'] end def test_books_paged_pagination Api::V2::BookResource.paginator :paged get :index, {page: {number: 3, size: 12}} assert_response :success assert_equal 12, json_response['data'].size - assert_equal 'Book 24', json_response['data'][0]['title'] + assert_equal 'Book 24', json_response['data'][0]['attributes']['title'] end def test_books_paged_pagination_bad_page_param Api::V2::BookResource.paginator :paged @@ -2063,23 +2270,92 @@ get :index, {page: {number: 50, size: -1}} assert_response :bad_request assert_match /-1 is not a valid value for size page parameter./, json_response['errors'][0]['detail'] end - def test_books_paged_pagination_invalid_page_format_interpret_int_text + def test_books_paged_pagination_invalid_page_format_incorrect Api::V2::BookResource.paginator :paged get :index, {page: 'qwerty'} - assert_response :success - assert_equal 10, json_response['data'].size - assert_equal 'Book 0', json_response['data'][0]['title'] + assert_response :bad_request + assert_match /0 is not a valid value for number page parameter./, json_response['errors'][0]['detail'] end def test_books_paged_pagination_invalid_page_format_interpret_int Api::V2::BookResource.paginator :paged get :index, {page: 3} assert_response :success assert_equal 10, json_response['data'].size - assert_equal 'Book 20', json_response['data'][0]['title'] + assert_equal 'Book 20', json_response['data'][0]['attributes']['title'] end end + +class Api::V4::BooksControllerTest < ActionController::TestCase + def setup + JSONAPI.configuration.json_key_format = :camelized_key + end + + def test_books_offset_pagination_meta + JSONAPI.configuration.operations_processor = :counting_active_record + Api::V4::BookResource.paginator :offset + get :index, {page: {offset: 50, limit: 12}} + assert_response :success + assert_equal 12, json_response['data'].size + assert_equal 'Book 50', json_response['data'][0]['attributes']['title'] + assert_equal 1000, json_response['meta']['totalRecords'] + JSONAPI.configuration.operations_processor = :active_record + end +end + +class CategoriesControllerTest < ActionController::TestCase + def test_index_default_filter + get :index + assert_response :success + assert json_response['data'].is_a?(Array) + assert_equal 3, json_response['data'].size + end + + def test_index_default_filter_override + get :index, { filter: { status: 'inactive' } } + assert_response :success + assert json_response['data'].is_a?(Array) + assert_equal 4, json_response['data'].size + end +end + +class Api::V1::PlanetsControllerTest < ActionController::TestCase + def test_save_model_callbacks + set_content_type_header! + post :create, + { + data: { + type: 'planets', + attributes: { + name: 'Zeus', + description: 'The largest planet in the solar system. Discovered in 2015.' + } + } + } + + assert_response :created + assert json_response['data'].is_a?(Hash) + assert_equal 'Zeus', json_response['data']['attributes']['name'] + end + + def test_save_model_callbacks_fail + set_content_type_header! + post :create, + { + data: { + type: 'planets', + attributes: { + name: 'Pluto', + description: 'Yes, it is a planet.' + } + } + } + + assert_response :unprocessable_entity + assert_match /Save failed or was cancelled/, json_response['errors'][0]['detail'] + end +end \ No newline at end of file