cartodb/spec/requests/carto/api/layers_controller_spec.rb
2020-06-15 10:58:47 +08:00

806 lines
29 KiB
Ruby

require_relative '../../../spec_helper'
require_relative '../../../../app/controllers/carto/api/layers_controller'
require 'helpers/unique_names_helper'
describe Carto::Api::LayersController do
include UniqueNamesHelper
describe '#refactored tests' do
include Rack::Test::Methods
include Warden::Test::Helpers
include CacheHelper
include Carto::Factories::Visualizations
include_context 'users helper'
describe '#operations' do
after(:each) do
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
@layer.destroy if @layer
@layer2.destroy if @layer2
end
let(:kind) { 'carto' }
let(:create_layer_url) do
api_v1_users_layers_create_url(user_domain: @user1.username, user_id: @user1.id, api_key: @user1.api_key)
end
def create_map_layer_url(map_id)
api_v1_maps_layers_create_url(user_domain: @user1.username, map_id: map_id, api_key: @user1.api_key)
end
def update_map_layer_url(map_id, layer_id = nil)
api_v1_maps_layers_update_url(
user_domain: @user1.username,
map_id: map_id,
id: layer_id,
api_key: @user1.api_key
)
end
def delete_map_layer_url(map_id, layer_id)
api_v1_maps_layers_destroy_url(
user_domain: @user1.username,
map_id: map_id,
id: layer_id,
api_key: @user1.api_key
)
end
let(:layer_json) do
{ kind: kind, options: { table_name: nil, user_name: nil }, order: 1, infowindow: {}, tooltip: {} }
end
it 'creates layers' do
post_json create_layer_url, layer_json do |response|
response.status.should eq 200
layer_response = response.body
layer_response.delete(:id).should_not be_nil
layer_response.should eq layer_json
end
end
it 'creates layers on maps' do
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1)
# Let's make room for another layer of the same kind
destroyed_layer = @map.layers.where(kind: layer_json[:kind]).first
destroyed_layer.destroy if destroyed_layer
post_json create_map_layer_url(@map.id), layer_json.merge(options: { table_name: @table.name }) do |response|
response.status.should eq 200
layer_response = response.body
layer_id = layer_response.delete(:id)
layer_id.should_not be_nil
layer_response.delete(:options).should eq ({ table_name: @table.name })
layer_response.should eq layer_json.except(:options)
@layer = Carto::Layer.find(layer_id)
@layer.maps.map(&:id).first.should eq @map.id
end
end
it 'registers table dependencies when creating a layer for a map' do
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1)
# Let's make room for another layer of the same kind
destroyed_layer = @map.layers.where(kind: layer_json[:kind]).first
destroyed_layer.destroy if destroyed_layer
post_json create_map_layer_url(@map.id), layer_json.merge(options: { table_name: @table.name }) do |response|
response.status.should eq 200
layer_response = response.body
@layer = Carto::Layer.find(layer_response[:id])
@layer.user_tables.should eq [@table]
end
end
it 'does not allow to exceed max_layers' do
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1)
@user1.max_layers = 1
@user1.save
post_json create_map_layer_url(@map.id), layer_json.merge(kind: 'carto', order: 10) do |response|
response.status.to_s.should match /4../ # 422 in new, 403 in old
end
end
it 'updates one layer' do
map = FactoryGirl.create(:carto_map_with_layers, user_id: @user1.id)
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1, map: map)
@layer = map.layers.first
new_order = 2
new_layer_json = layer_json.merge(
options: { random: '1' },
order: new_order
)
put_json update_map_layer_url(map.id, @layer.id), new_layer_json do |response|
response.status.should eq 200
layer_response = response.body
layer_response[:id].should eq @layer.id
layer_response[:options].should eq new_layer_json[:options]
layer_response[:order].should eq new_order
end
end
it 'register table dependencies when updating layers' do
map = FactoryGirl.create(:carto_map_with_layers, user_id: @user1.id)
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1, map: map)
@layer = map.layers.first
new_order = 2
new_layer_json = layer_json.merge(
options: { random: '1' },
order: new_order
)
Carto::Layer.any_instance.expects(:register_table_dependencies).once
put_json update_map_layer_url(map.id, @layer.id), new_layer_json do |response|
response.status.should eq 200
end
end
it 'updates several layers at once' do
map = FactoryGirl.create(:carto_map_with_layers, user_id: @user1.id)
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1, map: map)
@layer = map.layers.first
@layer2 = map.layers[1]
new_order = 2
new_layer_json = layer_json.merge(
options: { random: '1' },
order: new_order
)
new_layers_json = {
layers: [
new_layer_json.merge(id: @layer.id),
new_layer_json.merge(id: @layer2.id)
]
}
put_json update_map_layer_url(map.id), new_layers_json do |response|
response.status.should eq 200
layer_response = response.body
layer_response[:layers].map { |l| l[:id] }.should eq [@layer.id, @layer2.id]
layer_response[:layers].each do |layer|
layer[:options].reject { |k| k == :table_name }.should eq new_layer_json[:options]
layer[:order].should eq new_order
end
end
end
it 'does not update table_name or users_name options' do
map = FactoryGirl.create(:carto_map_with_layers, user_id: @user1.id)
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1, map: map)
@layer = map.data_layers.first
original_options = @layer.options.dup
new_layer_json = layer_json.merge(
options: { table_name: 'other_table_name', user_name: 'other_username' }
)
put_json update_map_layer_url(map.id, @layer.id), new_layer_json do |response|
response.status.should eq 200
layer_response = response.body
layer_response[:options].should eq original_options.slice(:table_name, :user_name).symbolize_keys
end
end
it 'does not remove table_name or users_name options' do
map = FactoryGirl.create(:carto_map_with_layers, user_id: @user1.id)
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1, map: map)
@layer = map.data_layers.first
original_options = @layer.options.dup
new_layer_json = layer_json.merge(
options: {}
)
put_json update_map_layer_url(map.id, @layer.id), new_layer_json do |response|
response.status.should eq 200
layer_response = response.body
layer_response[:options].should eq original_options.slice(:table_name, :user_name).symbolize_keys
end
end
it 'destroys layers' do
map = FactoryGirl.create(:carto_map_with_layers, user_id: @user1.id)
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1, map: map)
@layer = map.layers.first
delete_json delete_map_layer_url(map.id, @layer.id), {} do |response|
response.status.should eq 204
Carto::Layer.exists?(@layer.id).should be_false
end
end
end
describe 'creating a layer from an analysis node moves the style history' do
def create_layer(new_source, new_letter, from_letter)
url = api_v1_maps_layers_create_url(user_domain: @user2.username, map_id: @map.id, api_key: @user2.api_key)
payload = {
kind: 'carto',
options: {
source: new_source,
letter: new_letter,
table_name: @table.name,
user_name: @user2.username
},
infowindow: {},
tooltip: {},
from_layer_id: @original_layer.id,
from_letter: from_letter
}
post_json url, payload do |response|
response.status.should eq 200
layer_response = response.body
Carto::Layer.find(layer_response[:id])
end
end
before(:each) do
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user2)
@original_layer = @map.data_layers.first
@original_layer.options[:source] = 'a2'
@original_layer.save
@original_layer.layer_node_styles.each(&:destroy)
['a2', 'a1', 'a0'].each do |node_id|
LayerNodeStyle.create(
layer_id: @original_layer.id,
source_id: node_id,
options: { original_id: node_id },
infowindow: {},
tooltip: {}
)
end
end
after(:each) do
@layer.destroy if @layer
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
end
def verify_layer_node_styles(layer, styles_map)
# Map original_source_id -> new_source_id
layer.layer_node_styles.reload
actual_styles_map = layer.layer_node_styles.map { |lns| [lns.options[:original_id], lns.source_id] }.to_h
actual_styles_map.should eq styles_map
end
it 'when dragging an intermediate node' do
# A new layer B is created (copy A1 -> B1, A0 -> B0) and the old one starts using it as a source (rename A1 -> B1)
#
# _______ _______ ______
# | A | | A | | B |
# | | | | | |
# | [A2] | | [A2] | | |
# | {A1} | => | {B1} | | {B1} |
# | [A0] | | | | [B0] |
# |______| |______| |______|
@new_layer = create_layer('b1', 'b', 'a')
verify_layer_node_styles(@new_layer, nil => 'b1', 'a0' => 'b0')
verify_layer_node_styles(@original_layer, 'a2' => 'a2', 'a1' => 'b1')
end
describe 'when dragging a header node' do
# The original layer is renamed to B (rename A2 -> B1, A1 -> B1) and the new layer is named A (copy A1 and A0)
# The rename and the layer creation are independent requests, so we have to handle
# both possible orders of requests gracefully.
# _______ _______ ______
# | A | | A | | B |
# | | | | | |
# | {A2} | => | | | {B1} |
# | [A1] | | [A1] | | [A1] |
# | [A0] | | [A0] | | |
# |______| |______| |______|
it 'and the original layer has been previously renamed' do
old_model_layer = ::Layer[@original_layer.id]
old_model_layer.options['letter'] = 'b'
old_model_layer.options['source'] = 'b1'
old_model_layer.save
@new_layer = create_layer('a1', 'a', 'a')
verify_layer_node_styles(@new_layer, nil => 'a1', 'a0' => 'a0')
verify_layer_node_styles(@original_layer, nil => 'b1', 'a1' => 'a1')
end
it 'and the original layer has not yet been renamed' do
@new_layer = create_layer('a1', 'a', 'a')
verify_layer_node_styles(@new_layer, nil => 'a1', 'a0' => 'a0')
verify_layer_node_styles(@original_layer, 'a1' => 'a1')
end
end
end
describe "API 1.0 map layers management" do
before(:all) do
Capybara.current_driver = :rack_test
@user = create_user
end
before(:each) do
bypass_named_maps
delete_user_data @user
host! "#{@user.username}.localhost.lan"
@table = create_table(user_id: @user.id)
@map = create_map(user_id: @user.id, table_id: @table.id)
@table.reload
end
after(:all) do
bypass_named_maps
@user.destroy
end
let(:params) { { api_key: @user.api_key } }
it "Create a new layer associated to a map" do
opts = { type: "GMapsBase", base_type: "roadmap", style: "null", order: "0", query_history: nil }
infowindow = { fields: ['column1', 'column2', 'column3'] }
data = { kind: 'gmapsbase', infowindow: infowindow, options: opts }
post_json api_v1_maps_layers_create_url(params.merge(map_id: @map.id)), data do |response|
response.status.should be_success
@map.layers.size.should == 1
response.body[:id].should == @map.layers.first.id
response.body[:options].should == opts
response.body[:infowindow].should == infowindow
response.body[:order].should == 0
response.body[:kind].should == 'gmapsbase'
end
end
it "Get layer information" do
layer = Layer.create(
kind: 'carto',
order: 1,
options: { opt1: 'value' },
infowindow: { fields: ['column1', 'column2'] },
tooltip: { fields: ['column1', 'column3'] }
)
@map.add_layer layer
get_json api_v1_maps_layers_show_url(params.merge(id: layer.id, map_id: @map.id)) do |response|
response.status.should be_success
response.body[:id].should eq layer.id
response.body[:kind].should eq 'carto'
response.body[:order].should eq 1
response.body[:infowindow].should eq fields: ["column1", "column2"]
response.body[:tooltip].should eq fields: ["column1", "column3"]
end
end
it "Get all map layers" do
layer = Layer.create kind: 'carto', order: 3
layer2 = Layer.create kind: 'tiled', order: 2
layer3 = Layer.create kind: 'tiled', order: 1
@map.add_layer layer
@map.add_layer layer2
@map.add_layer layer3
get_json api_v1_maps_layers_index_url(params.merge(map_id: @map.id)) do |response|
response.status.should be_success
response.body[:total_entries].should == 3
response.body[:layers].size.should == 3
response.body[:layers][0][:id].should == layer3.id
response.body[:layers][1][:id].should == layer2.id
response.body[:layers][2][:id].should == layer.id
end
end
# see https://cartodb.atlassian.net/browse/CDB-3350
it "Update a layer" do
layer = Layer.create kind: 'carto', order: 0
@map.add_layer layer
data = { options: { opt1: 'value' }, infowindow: { fields: ['column1', 'column2'] }, order: 3, kind: 'carto' }
put_json api_v1_maps_layers_update_url(params.merge(id: layer.id, map_id: @map.id)), data do |response|
response.status.should be_success
response.body[:id].should == layer.id
response.body[:options].should == { opt1: 'value' }
response.body[:infowindow].should == { fields: ['column1', 'column2'] }
response.body[:kind].should == 'carto'
response.body[:order].should == 3
end
end
it "Update several layers at once" do
layer1 = Layer.create kind: 'carto', order: 0
layer2 = Layer.create kind: 'carto', order: 1
@map.add_layer layer1
@map.add_layer layer2
data = { layers: [
{ id: layer1.id, options: { opt1: 'value' }, infowindow: { fields: ['column1'] }, order: 2, kind: 'carto' },
{ id: layer2.id, options: { opt1: 'value' }, infowindow: { fields: ['column1'] }, order: 3, kind: 'carto' }
] }
put_json api_v1_maps_layers_update_url(params.merge(map_id: @map.id)), data do |response|
response.status.should be_success
response_layers = response.body[:layers]
response_layers.count.should == 2
response_layers.find { |l| l[:id] == layer1.id }[:order].should == 2
response_layers.find { |l| l[:id] == layer2.id }[:order].should == 3
layer1.reload.order.should == 2
layer2.reload.order.should == 3
end
end
it "Update a layer does not change table_name neither user_name" do
layer = Layer.create kind: 'carto', order: 0, options: { table_name: 'table1', user_name: @user.username }
@map.add_layer layer
data = { options: { table_name: 't1', user_name: 'u1' }, order: 3, kind: 'carto' }
put_json api_v1_maps_layers_update_url(params.merge(id: layer.id, map_id: @map.id)), data do |response|
response.status.should be_success
layer.options[:table_name].should == 'table1'
layer.options[:user_name].should == @user.username
response.body[:options].should == { table_name: 'table1', user_name: @user.username }
end
end
# see https://cartodb.atlassian.net/browse/CDB-3350
it "Update a layer > tiler error" do
layer = Layer.create kind: 'carto', order: 0
@map.add_layer layer
Layer.any_instance.stubs(:after_save).raises(RuntimeError)
Carto::Layer.any_instance.stubs(:invalidate_maps).raises(RuntimeError)
data = { options: { opt1: 'value' }, infowindow: { fields: ['column1', 'column2'] }, order: 999, kind: 'carto' }
put_json api_v1_maps_layers_update_url(params.merge(id: layer.id, map_id: @map.id)), data do |response|
response.status.should eq 400
layer.reload.order.should_not eq 999
end
end
it "Drop a layer" do
layer = Layer.create kind: 'carto'
@map.add_layer layer
delete_json api_v1_maps_layers_destroy_url(params.merge(id: layer.id, map_id: @map.id)) do |response|
response.status.should eq 204
expect { layer.refresh }.to raise_error
end
end
end
describe "API 1.0 user layers management" do
before(:all) do
Capybara.current_driver = :rack_test
@user = create_user
end
before(:each) do
bypass_named_maps
delete_user_data @user
host! "#{@user.username}.localhost.lan"
@table = create_table(user_id: @user.id)
end
after(:all) do
bypass_named_maps
@user.destroy
end
let(:params) { { api_key: @user.api_key } }
it "Create a new layer associated to the current user" do
opts = { kind: 'carto' }
post_json api_v1_users_layers_create_url(params.merge(user_id: @user.id)), opts do |response|
response.status.should be_success
@user.layers.size.should eq 1
response.body[:id].should eq @user.layers.first.id
end
end
# see https://cartodb.atlassian.net/browse/CDB-3350
it "Update a layer" do
layer = Layer.create kind: 'carto'
@user.add_layer layer
opts = { options: { opt1: 'value' }, infowindow: { fields: ['column1', 'column2'] }, kind: 'carto' }
put_json api_v1_users_layers_update_url(params.merge(id: layer.id, user_id: @user.id)), opts do |response|
response.status.should be_success
response.body[:id].should eq layer.id
response.body[:options].should eq opt1: 'value'
response.body[:infowindow].should == { fields: ['column1', 'column2'] }
response.body[:kind].should eq 'carto'
end
end
it "Drop a layer" do
layer = Layer.create kind: 'carto'
@user.add_layer layer
delete_json api_v1_users_layers_destroy_url(params.merge(id: layer.id, user_id: @user.id)) do |response|
response.status.should eq 204
expect { layer.refresh }.to raise_error
end
end
end
end
before(:each) do
bypass_named_maps
end
describe 'attribution changes' do
include Rack::Test::Methods
include Warden::Test::Helpers
before(:all) do
CartoDB::Visualization::Member.any_instance.stubs(:invalidate_cache).returns(nil)
@headers = { 'CONTENT_TYPE' => 'application/json' }
@user = FactoryGirl.create(:valid_user)
end
after(:each) do
@user.destroy
end
it 'attribution changes in a visualization propagate to associated layers' do
table_1_attribution = 'attribution 1'
table_2_attribution = 'attribution 2'
modified_table_2_attribution = 'modified attribution 2'
table1 = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @user.id)
table2 = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @user.id)
payload = {
name: 'new visualization',
tables: [
table1.name,
table2.name
],
privacy: 'public'
}
login_as(@user, scope: @user.username)
host! "#{@user.username}.localhost.lan"
post api_v1_visualizations_create_url(api_key: @api_key), payload.to_json, @headers do |response|
response.status.should eq 200
@visualization_data = JSON.parse(response.body)
end
visualization = Carto::Visualization.find(@visualization_data.fetch('id'))
table1_visualization = CartoDB::Visualization::Member.new(id: table1.table_visualization.id).fetch
table1_visualization.attributions = table_1_attribution
table1_visualization.store
table2_visualization = CartoDB::Visualization::Member.new(id: table2.table_visualization.id).fetch
table2_visualization.attributions = table_2_attribution
table2_visualization.store
get_json api_v1_maps_layers_index_url(map_id: visualization.map.id, api_key: @api_key) do |response|
response.status.should be_success
@layers_data = response.body.with_indifferent_access
end
# Done this way to preserve the order
data_layers = @layers_data['layers']
data_layers.delete_if { |layer| layer['kind'] != 'carto' }
data_layers.count.should eq 2
data_layers.map { |l| l['options']['attribution'] }.sort
.should eq [table_1_attribution, table_2_attribution]
table2_visualization.attributions = modified_table_2_attribution
table2_visualization.store
get_json api_v1_maps_layers_index_url(map_id: visualization.map.id, api_key: @api_key) do |response|
response.status.should be_success
@layers_data = response.body.with_indifferent_access
end
data_layers = @layers_data['layers'].select { |layer| layer['kind'] == 'carto' }
data_layers.count.should eq 2
data_layers.map { |l| l['options']['attribution'] }.sort
.should eq [table_1_attribution, modified_table_2_attribution]
end
end
describe 'index' do
include Rack::Test::Methods
include Warden::Test::Helpers
include CacheHelper
include_context 'visualization creation helpers'
include_context 'users helper'
it 'fetches layers from shared visualizations' do
CartoDB::Visualization::Member.any_instance.stubs(:invalidate_cache).returns(nil)
@headers = { 'CONTENT_TYPE' => 'application/json' }
def factory(user, attributes = {})
{
name: attributes.fetch(:name, unique_name('viz')),
tags: attributes.fetch(:tags, ['foo', 'bar']),
map_id: attributes.fetch(:map_id, ::Map.create(user_id: user.id).id),
description: attributes.fetch(:description, 'bogus'),
type: attributes.fetch(:type, 'derived'),
privacy: attributes.fetch(:privacy, 'public'),
source_visualization_id: attributes.fetch(:source_visualization_id, nil),
parent_id: attributes.fetch(:parent_id, nil),
locked: attributes.fetch(:locked, false),
prev_id: attributes.fetch(:prev_id, nil),
next_id: attributes.fetch(:next_id, nil)
}
end
create_account_type_fg('ORGANIZATION USER')
user_1 = create_user(
username: unique_name('user'),
email: unique_email,
password: 'clientex',
private_tables_enabled: false
)
user_2 = create_user(
username: unique_name('user'),
email: unique_email,
password: 'clientex',
private_tables_enabled: false
)
user_3 = create_user(
username: unique_name('user'),
email: unique_email,
password: 'clientex',
private_tables_enabled: false
)
organization = Organization.new
organization.name = unique_name('org')
organization.quota_in_bytes = 1234567890
organization.seats = 5
organization.save
organization.valid?.should eq true
user_org = CartoDB::UserOrganization.new(organization.id, user_1.id)
user_org.promote_user_to_admin
organization.reload
user_1.reload
user_2.organization_id = organization.id
user_2.save.reload
organization.reload
user_3.organization_id = organization.id
user_3.save.reload
organization.reload
default_url_options[:host] = "#{user_2.subdomain}.localhost.lan"
table = create_table(privacy: UserTable::PRIVACY_PRIVATE, name: unique_name('table'), user_id: user_1.id)
u1_t_1_perm_id = table.table_visualization.permission.id
put api_v1_permissions_update_url(user_domain: user_1.username, api_key: user_1.api_key, id: u1_t_1_perm_id),
{ acl: [{
type: CartoDB::Permission::TYPE_USER,
entity: {
id: user_2.id
},
access: CartoDB::Permission::ACCESS_READONLY
}] }.to_json, @headers
layer = Carto::Layer.create(
kind: 'carto',
tooltip: {},
options: {},
infowindow: {}
)
table.map.layers << layer
login_as(user_2, scope: user_2.username)
get_json api_v1_maps_layers_index_url(user_domain: user_2.username, map_id: table.map.id) do |response|
response.status.should be_success
body = JSON.parse(last_response.body)
body['layers'].count { |l| l['kind'] != 'tiled' }.should == 2
end
login_as(user_3, scope: user_3.username)
host! "#{user_3.username}.localhost.lan"
get_json api_v1_maps_layers_index_url(user_domain: user_3.username, map_id: table.map.id) do |response|
response.status.should == 404
end
end
end
describe '#show legacy tests' do
before(:all) do
@user = create_user
host! "#{@user.username}.localhost.lan"
end
before(:each) do
bypass_named_maps
delete_user_data @user
@table = create_table user_id: @user.id
end
after(:all) do
bypass_named_maps
@user.destroy
end
let(:params) { { api_key: @user.api_key } }
it "Get all user layers" do
layer = Layer.create kind: 'carto'
layer2 = Layer.create kind: 'tiled'
@user.add_layer layer
@user.add_layer layer2
default_url_options[:host] = "#{@user.subdomain}.localhost.lan"
get_json api_v1_users_layers_index_url(params.merge(user_id: @user.id)) do |response|
response.status.should be_success
response_body = response.body.with_indifferent_access
response_body['total_entries'].should eq 2
response_body['layers'].count { |l| l['kind'] != 'tiled' }.should eq 1
response_body['layers'].map { |l| l['id'] }.sort.should eq [layer.id, layer2.id].sort
end
end
it "Gets layers by map id" do
layer = Carto::Layer.create(
kind: 'carto',
tooltip: {},
options: {},
infowindow: {}
)
layer2 = Carto::Layer.create(
kind: 'tiled',
tooltip: {},
options: {},
infowindow: {}
)
expected_layers_ids = [layer.id, layer2.id]
existing_layers_ids = @table.map.layers.map(&:id)
existing_layers_count = @table.map.layers.count
@table.map.layers << layer
@table.map.layers << layer2
default_url_options[:host] = "#{@user.subdomain}.localhost.lan"
get_json api_v1_maps_layers_index_url(params.merge(map_id: @table.map.id)) do |response|
response.status.should be_success
response_body = response.body.with_indifferent_access
response_body['total_entries'].should eq 2 + existing_layers_count
response_body['layers'].count { |l| l['kind'] != 'tiled' }.should eq 2
new_layers_ids = response_body['layers'].map { |l| l['id'] }
(new_layers_ids - existing_layers_ids).should == expected_layers_ids
end
get_json api_v1_maps_layers_show_url(
params.merge(
map_id: @table.map.id,
id: layer.id
)) do |response|
response.status.should be_success
response_body = response.body.with_indifferent_access
response_body['id'].should eq layer.id
response_body['kind'].should eq layer.kind
end
end
end
end