Initial commit
This commit is contained in:
@@ -0,0 +1,478 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../services/data-repository/backend/sequel'
|
||||
require_relative '../../../services/data-repository/repository'
|
||||
require_relative '../../../app/models/visualization/collection'
|
||||
require_relative '../../../app/models/visualization/member'
|
||||
require_relative '../../doubles/support_tables.rb'
|
||||
require 'helpers/unique_names_helper'
|
||||
|
||||
include UniqueNamesHelper
|
||||
include CartoDB
|
||||
describe Visualization::Collection do
|
||||
include Carto::Factories::Visualizations
|
||||
|
||||
before(:all) do
|
||||
@user_1 = FactoryGirl.create(:valid_user, quota_in_bytes: 524288000, table_quota: 500, private_tables_enabled: true)
|
||||
@user_2 = FactoryGirl.create(:valid_user, private_tables_enabled: true)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
bypass_named_maps
|
||||
delete_user_data @user_1
|
||||
delete_user_data @user_2
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@user_1.destroy
|
||||
@user_2.destroy
|
||||
end
|
||||
|
||||
describe '#fetch' do
|
||||
it 'filters by tag if the backend supports array columns' do
|
||||
attributes1 = random_attributes_for_vis_member(tags: ['tag 1', 'tag 11'], user_id: @user_1.id)
|
||||
attributes2 = random_attributes_for_vis_member(tags: ['tag 2', 'tag 22'], user_id: @user_1.id)
|
||||
Visualization::Member.new(attributes1).store
|
||||
Visualization::Member.new(attributes2).store
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
collection.fetch(user_id: @user_1.id, tags: 'tag 1').count.should == 1
|
||||
end
|
||||
|
||||
it 'filters by partial name / description match' do
|
||||
attributes1 =
|
||||
random_attributes_for_vis_member(name: 'viz_1', description: 'description_11', user_id: @user_1.id)
|
||||
attributes2 =
|
||||
random_attributes_for_vis_member(name: 'viz_2', description: 'description_22', user_id: @user_1.id)
|
||||
Visualization::Member.new(attributes1).store
|
||||
Visualization::Member.new(attributes2).store
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
collection.fetch(user_id: @user_1.id, q: 'viz').count.should == 2
|
||||
collection.fetch(user_id: @user_1.id, q: 'viz_1').count.should == 1
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
collection.fetch(user_id: @user_1.id, q: 'description').count.should == 2
|
||||
collection.fetch(user_id: @user_1.id, q: 'ion_11').count.should == 1
|
||||
collection.fetch(user_id: @user_1.id, q: 'ion_22').count.should == 1
|
||||
end
|
||||
|
||||
it 'orders the collection by the passed criteria' do
|
||||
Visualization::Member.new(random_attributes_for_vis_member(user_id: @user_1.id, name: 'viz_1')).store
|
||||
Visualization::Member.new(random_attributes_for_vis_member(user_id: @user_1.id, name: 'viz_2')).store
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
records = collection.fetch(user_id: @user_1.id, order: :name)
|
||||
records.first.name.should == 'viz_2'
|
||||
end
|
||||
|
||||
it 'checks fetching with, without and only shared entities' do
|
||||
vis_1_name = unique_name('viz')
|
||||
vis_2_name = unique_name('viz')
|
||||
vis_3_name = unique_name('viz')
|
||||
Visualization::Member.new(random_attributes_for_vis_member(name: vis_1_name, user_id: @user_1.id)).store
|
||||
vis2 = Visualization::Member.new(random_attributes_for_vis_member(name: vis_2_name, user_id: @user_2.id)).store
|
||||
vis3 = Visualization::Member.new(random_attributes_for_vis_member(name: vis_3_name, user_id: @user_2.id)).store
|
||||
|
||||
shared_entity = CartoDB::SharedEntity.new(
|
||||
recipient_id: @user_1.id,
|
||||
recipient_type: CartoDB::SharedEntity::RECIPIENT_TYPE_USER,
|
||||
entity_id: vis2.id,
|
||||
entity_type: CartoDB::SharedEntity::ENTITY_TYPE_VISUALIZATION
|
||||
)
|
||||
shared_entity.save
|
||||
shared_entity.reload
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
collection.stubs(:user_shared_vis).with(@user_1.id).returns([vis2.id])
|
||||
|
||||
# Filter by user_id and non-owned id (excluding shared)
|
||||
records = collection.fetch(user_id: @user_1.id, id: vis3.id, exclude_shared: true)
|
||||
records.count.should eq 0
|
||||
|
||||
# Filter by user_id and non-owned id (not excluding shared)
|
||||
records = collection.fetch(user_id: @user_1.id, id: vis3.id)
|
||||
records.count.should eq 0
|
||||
|
||||
# Filter by name (not present user_id)
|
||||
records = collection.fetch(name: vis_1_name)
|
||||
records.count.should eq 1
|
||||
records.first.name.should eq vis_1_name
|
||||
|
||||
# Filter by user_id (includes shared)
|
||||
records = collection.fetch(user_id: @user_1.id).map { |item| item }
|
||||
records.count.should eq 2
|
||||
((records[0].name == vis_1_name || records[0].name == vis_2_name) &&
|
||||
(records[1].name == vis_1_name || records[1].name == vis_2_name)).should eq true
|
||||
|
||||
# Filter by user_id excluding shared
|
||||
records = collection.fetch(user_id: @user_1.id, exclude_shared: true)
|
||||
records.count.should eq 1
|
||||
records.first.name.should eq vis_1_name
|
||||
|
||||
# Filter by user_id and only shared
|
||||
records = collection.fetch(user_id: @user_1.id, only_shared: true)
|
||||
records.count.should eq 1
|
||||
records.first.name.should eq vis_2_name
|
||||
end
|
||||
|
||||
it 'checks that filtering collection by locked works' do
|
||||
collection = Visualization::Collection.new
|
||||
|
||||
vis1 = Visualization::Member.new(random_attributes_for_vis_member(user_id: @user_1.id,
|
||||
name: 'viz_1',
|
||||
locked: true)).store
|
||||
vis2 = Visualization::Member.new(random_attributes_for_vis_member(user_id: @user_1.id,
|
||||
name: 'viz_2',
|
||||
locked: false)).store
|
||||
|
||||
records = collection.fetch(user_id: @user_1.id, locked: false)
|
||||
records.count.should eq 1
|
||||
records.first.name.should eq vis2.name
|
||||
|
||||
records = collection.fetch(user_id: @user_1.id, locked: true)
|
||||
records.count.should eq 1
|
||||
records.first.name.should eq vis1.name
|
||||
end
|
||||
|
||||
it "checks that shared entities appear no matter if they're locked or not" do
|
||||
vis_1_name = 'viz_1'
|
||||
vis_2_name = 'viz_2'
|
||||
vis_3_name = 'viz_3'
|
||||
vis_4_name = 'viz_4'
|
||||
|
||||
Visualization::Member.new(random_attributes_for_vis_member(name: vis_1_name,
|
||||
user_id: @user_1.id,
|
||||
locked: true)).store
|
||||
vis2 = Visualization::Member.new(random_attributes_for_vis_member(name: vis_2_name,
|
||||
user_id: @user_2.id,
|
||||
locked: true)).store
|
||||
vis3 = Visualization::Member.new(random_attributes_for_vis_member(name: vis_3_name,
|
||||
user_id: @user_2.id,
|
||||
locked: false)).store
|
||||
Visualization::Member.new(random_attributes_for_vis_member(name: vis_4_name,
|
||||
user_id: @user_1.id,
|
||||
locked: false)).store
|
||||
|
||||
shared_entity = CartoDB::SharedEntity.new(
|
||||
recipient_id: @user_1.id,
|
||||
recipient_type: CartoDB::SharedEntity::RECIPIENT_TYPE_USER,
|
||||
entity_id: vis2.id,
|
||||
entity_type: CartoDB::SharedEntity::ENTITY_TYPE_VISUALIZATION
|
||||
)
|
||||
shared_entity.save
|
||||
shared_entity = CartoDB::SharedEntity.new(
|
||||
recipient_id: @user_1.id,
|
||||
recipient_type: CartoDB::SharedEntity::RECIPIENT_TYPE_USER,
|
||||
entity_id: vis3.id,
|
||||
entity_type: CartoDB::SharedEntity::ENTITY_TYPE_VISUALIZATION
|
||||
)
|
||||
shared_entity.save
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
collection.stubs(:user_shared_vis).with(@user_1.id).returns([vis2.id, vis3.id])
|
||||
|
||||
# Non-locked vis, all shared vis
|
||||
records = collection.fetch(user_id: @user_1.id)
|
||||
records.count.should eq 4
|
||||
|
||||
# Same behaviour, non-locked, all shared
|
||||
records = collection.fetch(user_id: @user_1.id, locked: false)
|
||||
records.count.should eq 3
|
||||
|
||||
# Only user vis, no shared vis at all
|
||||
records = collection.fetch(user_id: @user_1.id, locked: true)
|
||||
records.count.should eq 1
|
||||
records.map(&:name).first.should eq vis_1_name
|
||||
end
|
||||
|
||||
it 'Checks all supported sorting methods work' do
|
||||
# Supported: updated_at, likes, mapviews, row_count, size
|
||||
# TODO: Add mapviews test. As it uses redis requires more work
|
||||
|
||||
vis1 = full_visualization_table(@user_1, nil).visualization
|
||||
vis2 = full_visualization_table(@user_1, nil).visualization
|
||||
vis3 = full_visualization_table(@user_1, nil).visualization
|
||||
|
||||
# Biggest row count
|
||||
vis3.table.add_column!(name: "test_col", type: "text")
|
||||
vis3.table.insert_row!(test_col: "333")
|
||||
vis3.table.insert_row!(test_col: "333")
|
||||
vis3.table.insert_row!(test_col: "333")
|
||||
vis3.table.insert_row!(test_col: "333")
|
||||
vis3.table.insert_row!(test_col: "333")
|
||||
vis3.table.insert_row!(test_col: "333")
|
||||
# pg_class.reltuples only get updated after VACUUMs, etc.
|
||||
@user_1.in_database.run("VACUUM #{vis3.table.name}")
|
||||
vis3.updated_at = Time.now - 3.minute
|
||||
vis3.save
|
||||
|
||||
# Biggest in size and likes
|
||||
long_string = ""
|
||||
2000.times do
|
||||
long_string << rand(999).to_s
|
||||
end
|
||||
vis2.table.add_column!(name: "test_col", type: "text")
|
||||
vis2.table.add_column!(name: "test_col2", type: "text")
|
||||
vis2.table.add_column!(name: "test_col3", type: "text")
|
||||
vis2.table.insert_row!(test_col: long_string, test_col2: long_string, test_col3: long_string)
|
||||
vis2.table.insert_row!(test_col: long_string, test_col2: long_string, test_col3: long_string)
|
||||
vis2.table.insert_row!(test_col: long_string, test_col2: long_string, test_col3: long_string)
|
||||
vis2.table.insert_row!(test_col: long_string, test_col2: long_string, test_col3: long_string)
|
||||
@user_1.in_database.run("VACUUM #{vis2.table.name}")
|
||||
vis2.updated_at = Time.now - 2.minute
|
||||
vis2.save
|
||||
|
||||
# Latest edited
|
||||
vis1.table.add_column!(name: "test_col", type: "text")
|
||||
@user_1.in_database.run("VACUUM #{vis1.table.name}")
|
||||
vis1.updated_at = Time.now - 1.minute
|
||||
vis1.save
|
||||
|
||||
# Actual tests start here
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
order: 'updated_at',
|
||||
exclude_shared: true)
|
||||
collection.count.should eq 3
|
||||
ids = collection.map(&:id)
|
||||
expected_updated_ats = [vis1.id, vis2.id, vis3.id]
|
||||
ids.should eq expected_updated_ats
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
order: :row_count,
|
||||
exclude_shared: true)
|
||||
collection.count.should eq 3
|
||||
ids = collection.map(&:id)
|
||||
expected_row_count = [vis3.id, vis2.id, vis1.id]
|
||||
ids.should eq expected_row_count
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
order: :size,
|
||||
exclude_shared: true)
|
||||
collection.count.should eq 3
|
||||
ids = collection.map(&:id)
|
||||
expected_size = [vis2.id, vis3.id, vis1.id]
|
||||
ids.should eq expected_size
|
||||
|
||||
# Cleanup
|
||||
vis1.delete
|
||||
vis2.delete
|
||||
vis3.delete
|
||||
end
|
||||
|
||||
def liked(user)
|
||||
Visualization::Collection.new.fetch(user_id: user.id, only_liked: true)
|
||||
end
|
||||
|
||||
it "checks filtering by 'liked' " do
|
||||
user3 = create_user(quota_in_bytes: 524288000, table_quota: 500, private_tables_enabled: true)
|
||||
|
||||
full_visualization_table(@user_1, nil).visualization
|
||||
vis2 = full_visualization_table(@user_1, nil).visualization
|
||||
vis2.privacy = Visualization::Member::PRIVACY_PUBLIC
|
||||
vis2.save
|
||||
vis3 = full_visualization_table(@user_1, nil).visualization
|
||||
vis3.privacy = Visualization::Member::PRIVACY_PUBLIC
|
||||
vis3.save
|
||||
vis4 = full_visualization_table(@user_1, nil).visualization
|
||||
vis4.privacy.should eq Visualization::Member::PRIVACY_PRIVATE
|
||||
vis_link = full_visualization_table(@user_1, nil).visualization
|
||||
vis_link.privacy = Visualization::Member::PRIVACY_LINK
|
||||
vis_link.save
|
||||
vis_private = full_visualization_table(@user_1, nil).visualization
|
||||
vis_private.privacy = Visualization::Member::PRIVACY_PRIVATE
|
||||
vis_private.save
|
||||
|
||||
# vis1 0 likes
|
||||
|
||||
vis2.add_like_from(@user_1)
|
||||
expect {
|
||||
vis2.add_like_from(@user_2)
|
||||
}.to raise_exception Carto::Visualization::UnauthorizedLikeError
|
||||
|
||||
vis3.add_like_from(@user_1)
|
||||
|
||||
# since vis4 is not public it won't count for users 2 and 3
|
||||
vis4.add_like_from(@user_1)
|
||||
|
||||
vis_link.add_like_from(@user_1)
|
||||
vis_private.add_like_from(@user_1)
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id)
|
||||
collection.count.should eq 6
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id, only_liked: true)
|
||||
collection.count.should eq 5
|
||||
ids = collection.map(&:id)
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_CANONICAL,
|
||||
only_liked: true)
|
||||
collection.count.should eq 5
|
||||
ids = collection.map(&:id)
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
only_liked: true,
|
||||
unauthenticated: true)
|
||||
collection.count.should eq 2
|
||||
ids = collection.map(&:id)
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
only_liked: true,
|
||||
privacy: Visualization::Member::PRIVACY_PRIVATE)
|
||||
collection.count.should eq 2
|
||||
ids = collection.map(&:id)
|
||||
|
||||
collection = Visualization::Collection.new.fetch(only_liked: true)
|
||||
collection.count.should eq 0
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_2.id, only_liked: true)
|
||||
collection.count.should eq 0
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: user3.id, only_liked: true)
|
||||
collection.count.should eq 0 # Liked link privacy one
|
||||
|
||||
user3.destroy
|
||||
end
|
||||
end
|
||||
|
||||
describe 'single methods' do
|
||||
it 'checks count_total method' do
|
||||
vis_1_name = 'viz_1'
|
||||
vis_2_name = 'viz_2'
|
||||
vis_3_name = 'viz_3'
|
||||
vis_4_name = 'viz_4'
|
||||
|
||||
Visualization::Member.new(random_attributes(name: vis_1_name, user_id: @user_1.id)).store
|
||||
Visualization::Member.new(random_attributes(name: vis_2_name, privacy: 'private', user_id: @user_1.id)).store
|
||||
vis_user2 = Visualization::Member.new(random_attributes(name: vis_3_name, user_id: @user_2.id)).store
|
||||
vis2_user2 = Visualization::Member.new(random_attributes(name: vis_4_name, user_id: @user_2.id)).store
|
||||
|
||||
shared_entity = CartoDB::SharedEntity.new(
|
||||
recipient_id: @user_1.id,
|
||||
recipient_type: CartoDB::SharedEntity::RECIPIENT_TYPE_USER,
|
||||
entity_id: vis_user2.id,
|
||||
entity_type: CartoDB::SharedEntity::ENTITY_TYPE_VISUALIZATION
|
||||
)
|
||||
shared_entity.save
|
||||
shared_entity.reload
|
||||
|
||||
collection = Visualization::Collection.new
|
||||
collection.stubs(:user_shared_vis).with(@user_1.id).returns([vis_user2.id])
|
||||
|
||||
records = collection.fetch(user_id: @user_1.id)
|
||||
records.count.should eq 3
|
||||
|
||||
collection.count_total(user_id: @user_1.id).should eq 2
|
||||
|
||||
# Other filters should be skipped
|
||||
collection.count_total(user_id: @user_1.id, id: vis_user2.id).should eq 2
|
||||
collection.count_total(user_id: @user_1.id, id: vis2_user2.id).should eq 2
|
||||
collection.count_total(user_id: @user_1.id, name: 'test').should eq 2
|
||||
collection.count_total(user_id: @user_1.id, description: 'test').should eq 2
|
||||
collection.count_total(user_id: @user_1.id, privacy: CartoDB::Visualization::Member::PRIVACY_PRIVATE).should eq 2
|
||||
collection.count_total(user_id: @user_1.id, locked: true).should eq 2
|
||||
collection.count_total(user_id: @user_1.id, exclude_shared: false).should eq 2
|
||||
collection.count_total(user_id: @user_1.id, only_shared: false).should eq 2
|
||||
|
||||
# If unauthenticated remove private ones
|
||||
collection.count_total(user_id: @user_1.id, unauthenticated: true).should eq 1
|
||||
|
||||
# Type filters are allowed
|
||||
collection.count_total(user_id: @user_1.id, type: CartoDB::Visualization::Member::TYPE_CANONICAL).should eq 2
|
||||
collection.count_total(user_id: @user_1.id, type: CartoDB::Visualization::Member::TYPE_DERIVED).should eq 0
|
||||
|
||||
# And filtering by user_id
|
||||
collection.count_total(user_id: @user_2.id).should eq 2
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Slide visualization types specs
|
||||
|
||||
# This should be a member_spec test, but as those specs have no collection support...
|
||||
it 'checks the .children method' do
|
||||
parent = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_DERIVED)).store
|
||||
|
||||
parent.children.count.should eq 0
|
||||
|
||||
child1 = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id)).store.fetch
|
||||
|
||||
parent.children.count.should eq 1
|
||||
|
||||
child2 = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id)).store.fetch
|
||||
|
||||
child2.set_prev_list_item!(child1)
|
||||
parent.fetch
|
||||
|
||||
parent.children.count.should eq 2
|
||||
|
||||
canonical = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_CANONICAL)).store
|
||||
|
||||
parent.children.count.should eq 2
|
||||
|
||||
canonical.children.count.should eq 0
|
||||
|
||||
child_2_1 = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_DERIVED)).store
|
||||
|
||||
Visualization::Member.new(random_attributes_for_vis_member(
|
||||
user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: child_2_1.id)).store
|
||||
|
||||
child_2_1.children.count.should eq 1
|
||||
end
|
||||
|
||||
it 'checks retrieving slide type items' do
|
||||
member = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
type: Visualization::Member::TYPE_DERIVED,
|
||||
user_id: @user_1.id)).store
|
||||
|
||||
c1 = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: member.id,
|
||||
user_id: @user_1.id)).store
|
||||
c2 = Visualization::Member.new(random_attributes_for_vis_member(
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: member.id,
|
||||
user_id: @user_1.id)).store
|
||||
|
||||
collection = Visualization::Collection.new.fetch(user_id: @user_1.id,
|
||||
type: Visualization::Member::TYPE_SLIDE)
|
||||
collection.count.should eq 2
|
||||
|
||||
c1.delete
|
||||
c2.delete
|
||||
member.delete
|
||||
end
|
||||
|
||||
def random_attributes(attributes = {})
|
||||
random = unique_name('viz')
|
||||
{
|
||||
name: attributes.fetch(:name, random),
|
||||
description: attributes.fetch(:description, "description #{random}"),
|
||||
privacy: attributes.fetch(:privacy, 'public'),
|
||||
tags: attributes.fetch(:tags, ['tag 1']),
|
||||
type: attributes.fetch(:type, CartoDB::Visualization::Member::TYPE_CANONICAL),
|
||||
user_id: attributes.fetch(:user_id, UUIDTools::UUID.timestamp_create.to_s),
|
||||
locked: attributes.fetch(:locked, false),
|
||||
title: attributes.fetch(:title, ''),
|
||||
source: attributes.fetch(:source, ''),
|
||||
license: attributes.fetch(:license, ''),
|
||||
attributions: attributes.fetch(:attributions, ''),
|
||||
kind: attributes.fetch(:kind, CartoDB::Visualization::Member::KIND_GEOM),
|
||||
map_id: attributes.fetch(:map_id, nil)
|
||||
}
|
||||
end # random_attributes
|
||||
end # Visualization::Collection
|
||||
@@ -0,0 +1,117 @@
|
||||
require 'rspec/core'
|
||||
require 'rspec/expectations'
|
||||
require 'rspec/mocks'
|
||||
require 'sequel'
|
||||
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/models/visualization/locator'
|
||||
require_relative '../../../app/models/visualization'
|
||||
require_relative '../../../app/models/visualization/member'
|
||||
require_relative '../../doubles/support_tables.rb'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::Locator do
|
||||
|
||||
UUID = 'db0dfb0c-a944-11e3-a51e-30f9edfe5da6'
|
||||
|
||||
before(:each) do
|
||||
support_tables_mock = Doubles::Visualization::SupportTables.new
|
||||
Visualization::Relator.any_instance.stubs(:support_tables).returns(support_tables_mock)
|
||||
end
|
||||
|
||||
before do
|
||||
bypass_named_maps
|
||||
|
||||
@db = SequelRails.connection
|
||||
Sequel.extension(:pagination)
|
||||
|
||||
Visualization.repository = DataRepository::Backend::Sequel.new(@db, :visualizations)
|
||||
|
||||
@user_id = UUIDTools::UUID.timestamp_create.to_s
|
||||
|
||||
@map_id = UUIDTools::UUID.timestamp_create.to_s
|
||||
|
||||
# For relator->permission
|
||||
@user_mock = create_mocked_user
|
||||
CartoDB::Visualization::Relator.any_instance.stubs(:user).returns(@user_mock)
|
||||
|
||||
@visualization = Visualization::Member.new(
|
||||
{
|
||||
name: 'Visualization 1',
|
||||
description: 'A sample visualization',
|
||||
privacy: 'public',
|
||||
type: 'derived',
|
||||
map_id: UUID,
|
||||
id: @map_id,
|
||||
user_id: @user_id
|
||||
}
|
||||
).store
|
||||
|
||||
user_fake = model_fake(@map_id, @user_id)
|
||||
|
||||
@subdomain = 'bogus'
|
||||
@locator = Visualization::Locator.new(user_fake)
|
||||
end
|
||||
|
||||
describe '#get' do
|
||||
it 'fetches a Visualization::Member if passed an UUID' do
|
||||
rehydrated = @locator.get(@visualization.id, @subdomain).first
|
||||
|
||||
rehydrated.name.should == @visualization.name
|
||||
rehydrated.description.should_not be_nil
|
||||
end
|
||||
|
||||
it 'fetches a Visualization::Member if passed a visualization name' do
|
||||
Visualization::Collection.any_instance.stubs(:user_shared_vis).returns([])
|
||||
rehydrated = @locator.get(@visualization.name, @subdomain).first
|
||||
|
||||
rehydrated.id.should == @visualization.id
|
||||
rehydrated.description.should_not be_nil
|
||||
end
|
||||
|
||||
it 'fetches a Table if passed a table id' do
|
||||
user = create_user(quota_in_bytes: 1234567890, table_quota: 10)
|
||||
user.stubs(:invalidate_varnish_cache).returns(nil)
|
||||
table = Table.new
|
||||
table.user_id = user.id
|
||||
table.save
|
||||
table.reload
|
||||
|
||||
table_vis = table.table_visualization
|
||||
|
||||
table = Visualization::Locator.new.get(table.id, user.username)
|
||||
table[1].nil?.should eq false
|
||||
table[1].id.should eq table_vis.table.id
|
||||
|
||||
user.destroy
|
||||
end
|
||||
|
||||
it 'returns nil if no visualization or table found' do
|
||||
Visualization::Collection.any_instance.stubs(:user_shared_vis).returns([])
|
||||
@locator.get('220d2f46-b371-11e4-93f7-080027880ca6', @subdomain).should == [nil, nil]
|
||||
end
|
||||
end #get
|
||||
|
||||
def model_fake(map_id=nil, user_id=UUID)
|
||||
model_klass = Object.new
|
||||
|
||||
class << model_klass
|
||||
attr_accessor :id
|
||||
end
|
||||
model_klass.id = user_id
|
||||
|
||||
def model_klass.where(filter)
|
||||
@called_filter = filter
|
||||
[OpenStruct.new(
|
||||
maps: [OpenStruct.new(id: UUID)],
|
||||
id: id
|
||||
)]
|
||||
end
|
||||
|
||||
def model_klass.called_filter
|
||||
@called_filter
|
||||
end
|
||||
model_klass
|
||||
end #model_fake
|
||||
end # Visualization::Locator
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
require 'rspec/core'
|
||||
require 'rspec/expectations'
|
||||
require 'rspec/mocks'
|
||||
require 'sequel'
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/models/visualization/name_checker'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::NameChecker do
|
||||
before :all do
|
||||
bypass_named_maps
|
||||
@user = FactoryGirl.create(:valid_user)
|
||||
@user2 = FactoryGirl.create(:valid_user)
|
||||
|
||||
@vis1 = FactoryGirl.build(:derived_visualization, name: 'Visualization 1', user_id: @user.id).store
|
||||
@vis2 = FactoryGirl.build(:derived_visualization, name: 'Visualization 2', user_id: @user.id).store
|
||||
@vis3 = FactoryGirl.build(:derived_visualization, name: 'Visualization 4', user_id: @user2.id).store
|
||||
|
||||
@shared_entity = CartoDB::SharedEntity.new(
|
||||
recipient_id: @user.id,
|
||||
recipient_type: CartoDB::SharedEntity::RECIPIENT_TYPE_USER,
|
||||
entity_id: @vis3.id,
|
||||
entity_type: CartoDB::SharedEntity::ENTITY_TYPE_VISUALIZATION
|
||||
).save
|
||||
end
|
||||
|
||||
after :all do
|
||||
@shared_entity.destroy
|
||||
@vis3.destroy
|
||||
@vis2.destroy
|
||||
@vis1.destroy
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
describe '#available?' do
|
||||
it 'returns true if passed visualization name is available for the user' do
|
||||
checker = Visualization::NameChecker.new(@user)
|
||||
checker.available?('Visualization 3').should == true
|
||||
end
|
||||
|
||||
it 'returns false if passed visualization name is in use by the user' do
|
||||
checker = Visualization::NameChecker.new(@user)
|
||||
checker.available?('Visualization 1').should == false
|
||||
checker.available?('Visualization 2').should == false
|
||||
end
|
||||
|
||||
it 'returns true if name is available but used in shared visualizations' do
|
||||
checker = Visualization::NameChecker.new(@user)
|
||||
checker.available?('Visualization 4').should == true
|
||||
end
|
||||
end # available?
|
||||
end # Visualization::NameChecker
|
||||
@@ -0,0 +1,83 @@
|
||||
require 'ostruct'
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/models/visualization/name_generator'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::NameGenerator do
|
||||
describe '#generate' do
|
||||
it 'returns the candidate name if available' do
|
||||
user = Object.new
|
||||
candidate = 'Non existing name'
|
||||
checker = positive_checker
|
||||
generator = Visualization::NameGenerator.new(user, checker)
|
||||
generator.name(candidate).should == candidate
|
||||
end
|
||||
|
||||
it 'returns an altered name if no available candidate' do
|
||||
user = Object.new
|
||||
existing = 'visualization 1'
|
||||
checker = negative_checker
|
||||
generator = Visualization::NameGenerator.new(user, checker)
|
||||
|
||||
generator.name(existing).should_not == existing
|
||||
end
|
||||
|
||||
it 'returns a generated name if no candidate' do
|
||||
user = Object.new
|
||||
checker = negative_checker
|
||||
generator = Visualization::NameGenerator.new(user, checker)
|
||||
|
||||
generator.name.should =~ /#{Visualization::NameGenerator::PATTERN}/
|
||||
end
|
||||
|
||||
it 'accepts names with non-ASCII characters' do
|
||||
user = Object.new
|
||||
checker = positive_checker
|
||||
candidate = 'Me gustan los ñúes'
|
||||
generator = Visualization::NameGenerator.new(user, checker)
|
||||
generator.name(candidate).should == candidate
|
||||
end
|
||||
|
||||
it 'strips leading and trailing whitespace from candidates' do
|
||||
user = Object.new
|
||||
checker = positive_checker
|
||||
candidate = ' viva la pepa '
|
||||
generator = Visualization::NameGenerator.new(user, checker)
|
||||
generator.name(candidate).should == 'viva la pepa'
|
||||
end
|
||||
|
||||
it 'assigns a valid name if a explicit nil candidate is passed' do
|
||||
user = Object.new
|
||||
checker = positive_checker
|
||||
candidate = ' viva la pepa '
|
||||
generator = Visualization::NameGenerator.new(user, checker)
|
||||
generator.name(nil).should == Visualization::NameGenerator::PATTERN
|
||||
end
|
||||
|
||||
it 'assigns an incremental number' do
|
||||
user = Object.new
|
||||
candidate = 'Untitled map'
|
||||
Visualization::NameGenerator.new(user, negative_checker([])).name(candidate).should == candidate
|
||||
Visualization::NameGenerator.new(user, negative_checker([candidate])).name(candidate).should == "#{candidate} 1"
|
||||
Visualization::NameGenerator.new(user, negative_checker([candidate, "#{candidate} 1"])).name(candidate).should == "#{candidate} 2"
|
||||
Visualization::NameGenerator.new(user, negative_checker([candidate, "#{candidate} 1", "#{candidate} 2"])).name(candidate).should == "#{candidate} 3"
|
||||
end
|
||||
end
|
||||
|
||||
def positive_checker
|
||||
checker = Object.new
|
||||
def checker.available?(*args); true; end
|
||||
checker
|
||||
end
|
||||
|
||||
def negative_checker(existing = [ 'visualization 1', 'Untitled visualization', 'Untitled visualization 0', 'Untitled visualization 1'])
|
||||
checker = OpenStruct.new
|
||||
checker.existing = existing
|
||||
def checker.available?(candidate)
|
||||
!existing.include?(candidate)
|
||||
end
|
||||
checker
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../services/data-repository/backend/sequel'
|
||||
require_relative '../../../app/models/visualization/member'
|
||||
require_relative '../../../services/data-repository/repository'
|
||||
require_relative '../../factories/organizations_contexts'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::Member do
|
||||
include_context 'organization with users helper'
|
||||
|
||||
before(:each) do
|
||||
bypass_named_maps
|
||||
end
|
||||
|
||||
describe 'sharing tables and visualizations' do
|
||||
|
||||
it 'should give read permission to table aka canonical visualization' do
|
||||
owner_table = create_table(@org_user_owner)
|
||||
carto_canonical_vis = owner_table.table_visualization
|
||||
canonical_vis = CartoDB::Visualization::Member.new(id: carto_canonical_vis.id).fetch
|
||||
|
||||
canonical_vis.has_permission?(@org_user_owner, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
canonical_vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq false
|
||||
|
||||
give_permission(canonical_vis, @org_user_1, CartoDB::Permission::ACCESS_READONLY)
|
||||
|
||||
canonical_vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
end
|
||||
|
||||
it 'other user should not have permission until given' do
|
||||
owner_table = create_table(@org_user_owner)
|
||||
carto_vis = create_vis_from_table(owner_table.table_visualization.user, owner_table)
|
||||
vis = CartoDB::Visualization::Member.new(id: carto_vis.id).fetch
|
||||
vis.has_permission?(@org_user_owner, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq false
|
||||
|
||||
give_permission(vis, @org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY)
|
||||
|
||||
vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
end
|
||||
|
||||
it 'other user will not get permission in private table when the table owners adds it to the visualization' do
|
||||
owner_table = create_table(@org_user_owner)
|
||||
carto_vis = create_vis_from_table(owner_table.table_visualization.user, owner_table)
|
||||
vis = CartoDB::Visualization::Member.new(id: carto_vis.id).fetch
|
||||
give_permission(vis, @org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY)
|
||||
vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
|
||||
other_table = create_table(@org_user_owner)
|
||||
other_vis = CartoDB::Visualization::Member.new(id: other_table.table_visualization.id).fetch
|
||||
add_layer_from_table(vis, other_table)
|
||||
|
||||
vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
other_vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq false
|
||||
end
|
||||
|
||||
it 'should not remove access to visualization if table privacy is changed to private' do
|
||||
owner_table = create_table(@org_user_owner)
|
||||
carto_canonical_vis = owner_table.table_visualization
|
||||
canonical_vis = CartoDB::Visualization::Member.new(id: carto_canonical_vis.id).fetch
|
||||
carto_vis = create_vis_from_table(owner_table.table_visualization.user, owner_table)
|
||||
vis = CartoDB::Visualization::Member.new(id: carto_vis.id).fetch
|
||||
give_permission(vis, @org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY)
|
||||
|
||||
# removes access to table
|
||||
canonical_vis.permission.clear
|
||||
|
||||
canonical_vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq false
|
||||
vis.has_permission?(@org_user_1, CartoDB::Visualization::Member::PERMISSION_READONLY).should eq true
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_layer_from_table(vis, table)
|
||||
layer = Layer.create(layer_params(table).slice(:kind, :options, :infowindow, :tooltip, :order))
|
||||
vis.map.add_layer(layer)
|
||||
layer.register_table_dependencies
|
||||
vis.map.process_privacy_in(layer)
|
||||
end
|
||||
|
||||
def create_vis_from_table(user, table)
|
||||
blender = Visualization::TableBlender.new(user, [table])
|
||||
map = blender.blend
|
||||
vis = Visualization::Member.new(
|
||||
name: 'wadus_vis',
|
||||
map_id: map.id,
|
||||
type: Visualization::Member::TYPE_DERIVED,
|
||||
privacy: blender.blended_privacy,
|
||||
user_id: user.id
|
||||
)
|
||||
vis.store
|
||||
|
||||
vis
|
||||
end
|
||||
|
||||
def give_permission(vis, user, access)
|
||||
per = vis.permission
|
||||
per.set_user_permission(user, access)
|
||||
per.save
|
||||
per.reload
|
||||
end
|
||||
|
||||
# @return [CartoDB::Visualization::Member]
|
||||
def create_table(user)
|
||||
table = Table.new
|
||||
table.user_id = user.id
|
||||
table.name = 'wadus_table_' + UUIDTools::UUID.timestamp_create.to_s
|
||||
table.save
|
||||
table.reload
|
||||
|
||||
# table = create_table(user_id: user.id, name: 'wadus_table')
|
||||
table.table_visualization.type.should eq Visualization::Member::TYPE_CANONICAL
|
||||
|
||||
table
|
||||
end
|
||||
|
||||
def layer_params(table)
|
||||
{
|
||||
:kind => "carto",
|
||||
:options => {
|
||||
:attribution => 'CartoDB',
|
||||
:type => 'CartoDB',
|
||||
:active => true,
|
||||
:query => '',
|
||||
:opacity => 0.99,
|
||||
:interactivity => 'cartodb_id',
|
||||
:interaction => true,
|
||||
:debug => false,
|
||||
:tiler_domain => 'localhost.lan',
|
||||
:tiler_port => '8181',
|
||||
:tiler_protocol => 'http',
|
||||
:sql_api_domain => 'localhost.lan',
|
||||
:sql_api_port => 8080,
|
||||
:sql_api_protocol => 'http',
|
||||
:extra_params => {
|
||||
:cache_policy => 'persist',
|
||||
:cache_buster => 1404930437358
|
||||
},
|
||||
:maxZoom => 28,
|
||||
:auto_bound => false,
|
||||
:visible => true,
|
||||
:sql_domain => 'localhost.lan',
|
||||
:sql_port => '80',
|
||||
:sql_protocol => 'http',
|
||||
:tile_style_history => ["##{table.name}{ line-color: #FF6600; line-width: 2; line-opacity: 0.7; }"],
|
||||
:style_version => '2.1.1',
|
||||
:table_name => table.name,
|
||||
:user_name => 'foo',
|
||||
:tile_style => "##{table.name}{ line-color: #FF6600; line-width: 2; line-opacity: 0.7; }",
|
||||
:use_server_style => true,
|
||||
:query_history => [],
|
||||
:wizard_properties => {
|
||||
:type => 'polygon',
|
||||
:properties => {
|
||||
'line-width' => 2,
|
||||
'line-color' => '#FF6600',
|
||||
'line-opacity' => 0.7,
|
||||
'line-comp-op' => 'none',
|
||||
'text-name' => 'None',
|
||||
'text-face-name' => 'DejaVu Sans Book',
|
||||
'text-size' => 10,
|
||||
'text-fill' => '#000',
|
||||
'text-halo-fill' => '#FFF',
|
||||
'text-halo-radius' => 1,
|
||||
'text-dy' => -10,
|
||||
'text-allow-overlap' => true,
|
||||
'text-placement-type' => 'dummy',
|
||||
'text-label-position-tolerance' => 0,
|
||||
'text-placement' => 'point',
|
||||
'geometry_type' => 'line'
|
||||
}
|
||||
},
|
||||
:tile_style_custom => false,
|
||||
:query_wrapper => nil,
|
||||
:query_generated => false,
|
||||
:order => 2,
|
||||
:stat_tag => table.id,
|
||||
:sql_api_endpoint => '/api/v1/sql',
|
||||
:no_cdn => true,
|
||||
:force_cors => true
|
||||
},
|
||||
:infowindow => {
|
||||
:template_name => 'table/views/infowindow_light',
|
||||
:fields => []
|
||||
},
|
||||
:tooltip => {
|
||||
:fields => []
|
||||
},
|
||||
:order => 2
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
require 'ostruct'
|
||||
require 'sequel'
|
||||
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../services/data-repository/backend/sequel'
|
||||
require_relative '../../../app/models/visualization/member'
|
||||
require_relative '../../../app/models/visualization/overlays'
|
||||
require_relative '../../../services/data-repository/repository'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::Overlays do
|
||||
|
||||
before(:each) do
|
||||
@db = SequelRails.connection
|
||||
Visualization.repository = DataRepository::Backend::Sequel.new(@db, :visualizations)
|
||||
|
||||
member = Visualization::Member.new
|
||||
map_mock = mock
|
||||
map_mock.stubs(:scrollwheel=)
|
||||
map_mock.stubs(:legends=)
|
||||
map_mock.stubs(:legends)
|
||||
map_mock.stubs(:scrollwheel)
|
||||
map_mock.stubs(:id)
|
||||
map_mock.stubs(:save)
|
||||
Visualization::Member.any_instance.stubs(:map).returns(map_mock)
|
||||
@visualization = member
|
||||
|
||||
Visualization.repository = DataRepository::Backend::Sequel.new(@db, :visualizations)
|
||||
end
|
||||
|
||||
describe 'default' do
|
||||
it 'should create all overlays' do
|
||||
Visualization::Overlays.new(@visualization).create_default_overlays
|
||||
|
||||
@visualization.overlays.count.should eq 5
|
||||
@visualization.overlays.select { |o| o.options['display'] }.count.should eq 5
|
||||
@visualization.overlays.select { |o| o.type == 'logo' }.count.should eq 1
|
||||
end
|
||||
|
||||
it 'should not create logo if user has disabled_cartodb_logo feature_flag' do
|
||||
user_mock = mock
|
||||
user_mock.stubs(:has_feature_flag?).with('disabled_cartodb_logo').returns(true)
|
||||
@visualization.stubs(:user).returns(user_mock)
|
||||
|
||||
Visualization::Overlays.new(@visualization).create_default_overlays
|
||||
|
||||
@visualization.overlays.select { |o| o.type == 'logo' }.count.should eq 0
|
||||
@visualization.overlays.count.should eq 4
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,198 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/models/visualization/presenter'
|
||||
require_relative '../../doubles/support_tables.rb'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::Member do
|
||||
before do
|
||||
@db = SequelRails.connection
|
||||
Sequel.extension(:pagination)
|
||||
|
||||
Visualization.repository = DataRepository::Backend::Sequel.new(@db, :visualizations)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
bypass_named_maps
|
||||
|
||||
user_name = 'whatever'
|
||||
@user_mock = FactoryGirl.build(:user, username: user_name)
|
||||
CartoDB::Visualization::Relator.any_instance.stubs(:user).returns(@user_mock)
|
||||
|
||||
support_tables_mock = Doubles::Visualization::SupportTables.new
|
||||
Visualization::Relator.any_instance.stubs(:support_tables).returns(support_tables_mock)
|
||||
end
|
||||
|
||||
describe '#privacy_for_vizjson' do
|
||||
it 'checks expected privacy values for the vizjson' do
|
||||
visualization = Visualization::Member.new(
|
||||
privacy: Visualization::Member::PRIVACY_PUBLIC,
|
||||
name: 'test',
|
||||
type: Visualization::Member::TYPE_CANONICAL
|
||||
)
|
||||
|
||||
# Careful, do a user mock after touching user_data as it does some checks about user too
|
||||
user_mock = mock
|
||||
user_mock.stubs(:private_tables_enabled).returns(true)
|
||||
user_mock.stubs(:id).returns(@user_mock.id)
|
||||
Visualization::Member.any_instance.stubs(:user).returns(user_mock)
|
||||
|
||||
presenter = Visualization::Presenter.new(visualization)
|
||||
presenter.send(:privacy_for_vizjson).should eq Visualization::Member::PRIVACY_PUBLIC
|
||||
|
||||
visualization.privacy = Visualization::Member::PRIVACY_PRIVATE
|
||||
presenter.send(:privacy_for_vizjson).should eq Visualization::Member::PRIVACY_PRIVATE
|
||||
|
||||
visualization.privacy = Visualization::Member::PRIVACY_PROTECTED
|
||||
presenter.send(:privacy_for_vizjson).should eq Visualization::Member::PRIVACY_PROTECTED
|
||||
|
||||
visualization.privacy = Visualization::Member::PRIVACY_LINK
|
||||
presenter.send(:privacy_for_vizjson).should eq Visualization::Member::PRIVACY_PUBLIC
|
||||
end
|
||||
end
|
||||
|
||||
describe 'to_poro fields' do
|
||||
it 'basic fields expected at the to_poro method' do
|
||||
perm_mock = FactoryGirl.build(:carto_permission)
|
||||
|
||||
vis_mock = mock
|
||||
vis_mock.stubs(:id).returns(UUIDTools::UUID.timestamp_create.to_s)
|
||||
vis_mock.stubs(:name).returns('vis1')
|
||||
vis_mock.stubs(:display_name).returns('vis1')
|
||||
vis_mock.stubs(:map_id).returns(UUIDTools::UUID.timestamp_create.to_s)
|
||||
vis_mock.stubs(:active_layer_id).returns(1)
|
||||
vis_mock.stubs(:type).returns(Visualization::Member::TYPE_CANONICAL)
|
||||
vis_mock.stubs(:tags).returns(['tag1'])
|
||||
vis_mock.stubs(:description).returns('desc')
|
||||
vis_mock.stubs(:privacy).returns(Visualization::Member::PRIVACY_PUBLIC)
|
||||
vis_mock.stubs(:stats).returns('123')
|
||||
vis_mock.stubs(:created_at).returns(Time.now)
|
||||
vis_mock.stubs(:updated_at).returns(Time.now)
|
||||
vis_mock.stubs(:permission).returns(perm_mock)
|
||||
vis_mock.stubs(:locked).returns(true)
|
||||
vis_mock.stubs(:source).returns('')
|
||||
vis_mock.stubs(:license).returns('')
|
||||
vis_mock.stubs(:attributions).returns('')
|
||||
vis_mock.stubs(:title).returns('')
|
||||
vis_mock.stubs(:parent_id).returns(nil)
|
||||
vis_mock.stubs(:children).returns([])
|
||||
vis_mock.stubs(:kind).returns(Visualization::Member::KIND_GEOM)
|
||||
vis_mock.stubs(:table).returns(nil)
|
||||
vis_mock.stubs(:related_tables).returns([])
|
||||
vis_mock.stubs(:prev_id).returns(nil)
|
||||
vis_mock.stubs(:next_id).returns(nil)
|
||||
vis_mock.stubs(:transition_options).returns({})
|
||||
vis_mock.stubs(:active_child).returns(nil)
|
||||
vis_mock.stubs(:likes).returns([])
|
||||
|
||||
vis_mock.stubs(:synchronization).returns(nil)
|
||||
|
||||
presenter = Visualization::Presenter.new(vis_mock)
|
||||
data = presenter.to_poro
|
||||
|
||||
data[:id].present?.should eq true
|
||||
data[:name].present?.should eq true
|
||||
data[:display_name].present?.should eq true
|
||||
data[:map_id].present?.should eq true
|
||||
data[:active_layer_id].present?.should eq true
|
||||
data[:type].present?.should eq true
|
||||
data[:tags].present?.should eq true
|
||||
data[:description].present?.should eq true
|
||||
data[:privacy].present?.should eq true
|
||||
data[:stats].present?.should eq true
|
||||
data[:created_at].present?.should eq true
|
||||
data[:updated_at].present?.should eq true
|
||||
data[:permission].present?.should eq true
|
||||
data[:locked].present?.should eq true
|
||||
data[:related_tables].should eq Array.new
|
||||
data[:table].should eq Hash.new
|
||||
data[:parent_id].should eq nil
|
||||
data[:children].should eq Array.new
|
||||
data[:kind].should eq Visualization::Member::KIND_GEOM
|
||||
data[:prev_id].should eq nil
|
||||
data[:next_id].should eq nil
|
||||
data[:transition_options].should eq Hash.new
|
||||
data[:active_child].should eq nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#children' do
|
||||
it 'tests .children and its sorting' do
|
||||
Visualization::Member.any_instance.stubs(:supports_private_maps?).returns(true)
|
||||
|
||||
::Permission.any_instance.stubs(:owner).returns(@user_mock)
|
||||
|
||||
parent = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'PARENT',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_DERIVED
|
||||
}))
|
||||
parent = parent.store.fetch
|
||||
|
||||
# Create unsorted on purpose
|
||||
member_d = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'D',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id
|
||||
}))
|
||||
member_d = member_d.store.fetch
|
||||
member_c = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'C',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id
|
||||
}))
|
||||
member_c = member_c.store.fetch
|
||||
member_b = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'B',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id
|
||||
}))
|
||||
member_b = member_b.store.fetch
|
||||
member_e = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'E',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id
|
||||
}))
|
||||
member_e = member_e.store.fetch
|
||||
member_a = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'A',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_SLIDE,
|
||||
parent_id: parent.id
|
||||
}))
|
||||
member_a = member_a.store.fetch
|
||||
|
||||
# A -> B -> C -> D -> E
|
||||
member_a.set_next_list_item! member_b
|
||||
member_b.set_next_list_item! member_c
|
||||
member_c.set_next_list_item! member_d
|
||||
member_d.set_next_list_item! member_e
|
||||
member_a.fetch
|
||||
member_b.fetch
|
||||
member_c.fetch
|
||||
member_d.fetch
|
||||
member_e.fetch
|
||||
|
||||
parent.fetch
|
||||
|
||||
@request_mock = mock
|
||||
@request_mock.stubs(:host).returns("#{@user_mock.username}#{CartoDB.session_domain}")
|
||||
@request_mock.stubs(:fullpath).returns('')
|
||||
|
||||
data = Visualization::Presenter.new(parent,{request:@request_mock}).to_poro
|
||||
|
||||
data[:children].length.should eq 5
|
||||
|
||||
data[:children][0][:id].should eq member_a.id
|
||||
data[:children][1][:id].should eq member_b.id
|
||||
data[:children][2][:id].should eq member_c.id
|
||||
data[:children][3][:id].should eq member_d.id
|
||||
data[:children][4][:id].should eq member_e.id
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,149 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/models/visualization/relator'
|
||||
require_relative '../../../app/models/visualization/member'
|
||||
require_relative '../../../app/models/visualization/collection'
|
||||
require_relative '../../doubles/support_tables.rb'
|
||||
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::Relator do
|
||||
before do
|
||||
@db = SequelRails.connection
|
||||
Sequel.extension(:pagination)
|
||||
|
||||
Visualization.repository = DataRepository::Backend::Sequel.new(@db, :visualizations)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
@user = create_user({
|
||||
email: 'admin@cartotest.com',
|
||||
username: 'admin',
|
||||
password: '000123456'
|
||||
})
|
||||
@carto_user = Carto::User.find(@user.id)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
bypass_named_maps
|
||||
delete_user_data(@user)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
bypass_named_maps
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
describe '#estimated_row_count and #actual_row_count' do
|
||||
|
||||
it 'should query Table estimated an actual row count methods' do
|
||||
::Table.any_instance.stubs(:row_count_and_size).returns(row_count: 999)
|
||||
::Table.any_instance.stubs(:actual_row_count).returns(1000)
|
||||
table = create_table(name: 'table1', user_id: @user.id)
|
||||
vis = table.table_visualization
|
||||
vis.estimated_row_count.should == 999
|
||||
vis.actual_row_count.should == 1000
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
# For relator->permission
|
||||
@user_mock = create_mocked_user
|
||||
CartoDB::Visualization::Relator.any_instance.stubs(:user).returns(@user_mock)
|
||||
|
||||
support_tables_mock = Doubles::Visualization::SupportTables.new
|
||||
Visualization::Relator.any_instance.stubs(:support_tables).returns(support_tables_mock)
|
||||
end
|
||||
|
||||
describe '#related_canonical_visualizations' do
|
||||
|
||||
it 'should return the canonical visualizations associated to a derived visualization' do
|
||||
table1 = create_table({:name => 'table1', :user_id => @user.id})
|
||||
table2 = create_table({:name => 'table2', :user_id => @user.id})
|
||||
vis_table1 = create_vis_from_table(@carto_user, table1)
|
||||
create_vis_from_table(@carto_user, table2)
|
||||
|
||||
vis_table1.related_canonical_visualizations.map(&:id).should == [table1.table_visualization.id]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#children' do
|
||||
it 'tests .children and its sorting' do
|
||||
Visualization::Member.any_instance.stubs(:supports_private_maps?).returns(true)
|
||||
|
||||
parent = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'PARENT',
|
||||
user_id: @user_mock.id,
|
||||
type: Visualization::Member::TYPE_DERIVED }))
|
||||
parent = parent.store.fetch
|
||||
|
||||
# Create unsorted on purpose
|
||||
member_d = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'D', type: Visualization::Member::TYPE_SLIDE,
|
||||
user_id: @user_mock.id,
|
||||
parent_id: parent.id }))
|
||||
member_d = member_d.store.fetch
|
||||
member_c = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'C', type: Visualization::Member::TYPE_SLIDE,
|
||||
user_id: @user_mock.id,
|
||||
parent_id: parent.id }))
|
||||
member_c = member_c.store.fetch
|
||||
member_b = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'B', type: Visualization::Member::TYPE_SLIDE,
|
||||
user_id: @user_mock.id,
|
||||
parent_id: parent.id }))
|
||||
member_b = member_b.store.fetch
|
||||
member_e = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'E', type: Visualization::Member::TYPE_SLIDE,
|
||||
user_id: @user_mock.id,
|
||||
parent_id: parent.id }))
|
||||
member_e = member_e.store.fetch
|
||||
member_a = Visualization::Member.new(random_attributes_for_vis_member({
|
||||
name:'A', type: Visualization::Member::TYPE_SLIDE,
|
||||
user_id: @user_mock.id,
|
||||
parent_id: parent.id }))
|
||||
member_a = member_a.store.fetch
|
||||
|
||||
# A -> B -> C -> D -> E
|
||||
member_a.set_next_list_item! member_b
|
||||
member_b.set_next_list_item! member_c
|
||||
member_c.set_next_list_item! member_d
|
||||
member_d.set_next_list_item! member_e
|
||||
member_a.fetch
|
||||
member_b.fetch
|
||||
member_c.fetch
|
||||
member_d.fetch
|
||||
member_e.fetch
|
||||
|
||||
parent.fetch
|
||||
|
||||
children = parent.children
|
||||
|
||||
children.length.should eq 5
|
||||
|
||||
children[0].id.should eq member_a.id
|
||||
children[1].id.should eq member_b.id
|
||||
children[2].id.should eq member_c.id
|
||||
children[3].id.should eq member_d.id
|
||||
children[4].id.should eq member_e.id
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def create_vis_from_table(user, table)
|
||||
blender = Visualization::TableBlender.new(user, [table])
|
||||
map = blender.blend
|
||||
vis = Visualization::Member.new(
|
||||
name: 'wadus_vis',
|
||||
map_id: map.id,
|
||||
type: Visualization::Member::TYPE_DERIVED,
|
||||
privacy: blender.blended_privacy,
|
||||
user_id: user.id
|
||||
)
|
||||
vis.store
|
||||
vis
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,125 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/models/visualization/table_blender'
|
||||
|
||||
include CartoDB::Visualization
|
||||
|
||||
# TODO this file cannot be executed in isolation
|
||||
describe TableBlender do
|
||||
let(:user) do
|
||||
FactoryGirl.create(:valid_user, private_tables_enabled: true, viewer: true)
|
||||
end
|
||||
|
||||
let(:map_mock) do
|
||||
map = mock
|
||||
map.stubs(:to_hash).returns({})
|
||||
map.stubs(:user).returns(user)
|
||||
map.stubs(:user_layers).returns([])
|
||||
map.stubs(:data_layers).returns([])
|
||||
map
|
||||
end
|
||||
|
||||
describe '#blend' do
|
||||
include Carto::Factories::Visualizations
|
||||
include_context 'users helper'
|
||||
|
||||
it 'raises an error for viewer users' do
|
||||
tables = [fake_public_table, fake_private_table]
|
||||
expect {
|
||||
TableBlender.new(user, tables).blend
|
||||
}.to raise_error(/Viewer users can't blend tables/)
|
||||
user.destroy
|
||||
end
|
||||
|
||||
describe 'multiple tables' do
|
||||
it 'sets increasing order for data layers and keep tiled first and last' do
|
||||
map1 = FactoryGirl.create(:carto_map_with_2_tiled_layers, user_id: @carto_user1.id)
|
||||
map2 = FactoryGirl.create(:carto_map_with_2_tiled_layers, user_id: @carto_user1.id)
|
||||
map1, table1, table_visualization1, visualization1 = create_full_visualization(@carto_user1, canonical_map: map1)
|
||||
map2, table2, table_visualization2, visualization2 = create_full_visualization(@carto_user1, canonical_map: map2)
|
||||
|
||||
blender = CartoDB::Visualization::TableBlender.new(@carto_user1, [table1, table2])
|
||||
map = blender.blend
|
||||
|
||||
map.layers.count.should eq 4
|
||||
orders_and_kind = map.layers.map { |l| [l.order, l.kind] }.sort { |x, y| x[0] <=> y[0] }
|
||||
orders_and_kind.should eq [[0, 'tiled'], [1, 'carto'], [2, 'carto'], [3, 'tiled']]
|
||||
|
||||
destroy_full_visualization(map2, table2, table_visualization2, visualization2)
|
||||
destroy_full_visualization(map1, table1, table_visualization1, visualization1)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'default basemap' do
|
||||
before(:each) do
|
||||
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_user1)
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
|
||||
end
|
||||
|
||||
it 'is chosen from table map basemap for editor users' do
|
||||
@carto_user1.builder_enabled = false
|
||||
# Let's force a name change in order to be sure that it's taken from the table map and it's not a default one
|
||||
fake_name = 'fake_basemap_name'
|
||||
@table.map.user_layers.first.options['name'] = fake_name
|
||||
@table.save
|
||||
|
||||
blender = CartoDB::Visualization::TableBlender.new(@carto_user1, [@table])
|
||||
|
||||
map = blender.blend
|
||||
map.user_layers.first.options['name'].should eq fake_name
|
||||
end
|
||||
|
||||
it 'is chosen from the default basemap for builder users' do
|
||||
@carto_user1.builder_enabled = true
|
||||
# Let's force a name change in order to be sure that it's not taken from the table map
|
||||
fake_name = 'fake_basemap_name'
|
||||
@table.map.user_layers.first.options['name'] = fake_name
|
||||
@table.save
|
||||
|
||||
blender = CartoDB::Visualization::TableBlender.new(@carto_user1, [@table])
|
||||
|
||||
map = blender.blend
|
||||
map.user_layers.first.options['name'].should_not eq fake_name
|
||||
map.user_layers.first.options['name'].should eq Cartodb.default_basemap['name']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO test too coupled with implementation outside blender
|
||||
# refactor once Privacy is extracted
|
||||
describe '#blended_privacy' do
|
||||
it 'returns private if any of all tables is private' do
|
||||
user = Object.new
|
||||
tables = [fake_public_table, fake_private_table]
|
||||
TableBlender.new(user, tables).blended_privacy.should == 'private'
|
||||
|
||||
tables = [fake_private_table, fake_public_table]
|
||||
TableBlender.new(user, tables).blended_privacy.should == 'private'
|
||||
end
|
||||
|
||||
it 'returns public if all tables are public' do
|
||||
user = Object.new
|
||||
tables = [fake_public_table, fake_public_table]
|
||||
|
||||
TableBlender.new(user, tables).blended_privacy.should == 'public'
|
||||
end
|
||||
end
|
||||
|
||||
def fake_public_table
|
||||
table = mock
|
||||
table.stubs(:private?).returns(false)
|
||||
table.stubs(:public_with_link_only?).returns(false)
|
||||
table.stubs(:map).returns(map_mock)
|
||||
table
|
||||
end
|
||||
|
||||
def fake_private_table
|
||||
table = mock
|
||||
table.stubs(:private?).returns(true)
|
||||
table.stubs(:public_with_link_only?).returns(false)
|
||||
table.stubs(:map).returns(map_mock)
|
||||
table
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,97 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../services/data-repository/backend/sequel'
|
||||
require_relative '../../../services/data-repository/repository'
|
||||
require_relative '../../../app/models/visualization/tags'
|
||||
require_relative '../../../app/models/visualization/collection'
|
||||
require 'helpers/unique_names_helper'
|
||||
|
||||
include UniqueNamesHelper
|
||||
include CartoDB
|
||||
|
||||
describe Visualization::Tags do
|
||||
include CartoDB::Factories
|
||||
|
||||
before(:each) do
|
||||
Varnish.any_instance.stubs(:send_command).returns(true)
|
||||
@db = SequelRails.connection
|
||||
Visualization.repository = DataRepository::Backend::Sequel.new(@db, :visualizations)
|
||||
|
||||
bypass_named_maps
|
||||
|
||||
# For relator->permission
|
||||
user_id = UUIDTools::UUID.timestamp_create.to_s
|
||||
user_name = 'whatever'
|
||||
user_apikey = '123'
|
||||
@user_mock = create_mocked_user(user_id: user_id, user_name: user_name, user_apikey: user_apikey)
|
||||
Visualization::Relator.any_instance.stubs(:user).returns(@user_mock)
|
||||
end
|
||||
|
||||
describe '#filters' do
|
||||
it 'checks that filtering by locked works' do
|
||||
vis_1_tag = 'v1tag'
|
||||
vis_2_tag = 'v2tag'
|
||||
|
||||
Visualization::Member.new(random_attributes(user_id: @user_mock.id, name: 'viz_1', locked:true, \
|
||||
tags: [vis_1_tag])).store
|
||||
Visualization::Member.new(random_attributes(user_id: @user_mock.id, name: 'viz_2', locked:false, \
|
||||
tags: [vis_2_tag])).store
|
||||
|
||||
tags = Visualization::Tags.new(@user_mock)
|
||||
|
||||
records = tags.names()
|
||||
records.length.should eq 2
|
||||
|
||||
records = tags.names(locked: false)
|
||||
records.length.should eq 1
|
||||
records.first.should eq vis_2_tag
|
||||
|
||||
records = tags.names(locked: true)
|
||||
records.length.should eq 1
|
||||
records.first.should eq vis_1_tag
|
||||
end
|
||||
end
|
||||
|
||||
describe 'searching' do
|
||||
it 'should search for tags correctly' do
|
||||
manolo_tag = 'manolo'
|
||||
manoloescobar_tag = 'manoloescobar'
|
||||
manolo_escobar_tag = 'Manolo Escobar'
|
||||
|
||||
|
||||
v1 = Visualization::Member.new(random_attributes(user_id: @user_mock.id, name: 'v1', locked:true, tags: [manolo_tag, manoloescobar_tag, manolo_escobar_tag])).store
|
||||
v2 = Visualization::Member.new(random_attributes(user_id: @user_mock.id, name: 'v2', locked:false, tags: [manolo_tag])).store
|
||||
v3 = Visualization::Member.new(random_attributes(user_id: @user_mock.id, name: 'v3', locked:false, tags: [manolo_escobar_tag, manoloescobar_tag])).store
|
||||
|
||||
vqb1 = Carto::VisualizationQueryBuilder.new.with_tags([manolo_tag.upcase]).build.map(&:id)
|
||||
|
||||
vqb1.should include v1.id
|
||||
vqb1.should include v2.id # Searching for 'manolo' should bring up 'manoloescobar' as well
|
||||
vqb1.should include v3.id
|
||||
|
||||
vqb2 = Carto::VisualizationQueryBuilder.new.with_tags([manoloescobar_tag.upcase]).build.map(&:id)
|
||||
|
||||
vqb2.should include v1.id
|
||||
vqb2.should_not include v2.id # 'manoloescobar' is not a substring of 'manolo'
|
||||
vqb2.should include v3.id
|
||||
|
||||
vqb3 = Carto::VisualizationQueryBuilder.new.with_tags([manolo_escobar_tag.upcase]).build.map(&:id)
|
||||
|
||||
vqb3.should include v1.id
|
||||
vqb3.should_not include v2.id
|
||||
vqb3.should include v3.id
|
||||
end
|
||||
end
|
||||
|
||||
def random_attributes(attributes={})
|
||||
random = unique_name('viz')
|
||||
{
|
||||
name: attributes.fetch(:name, random),
|
||||
description: attributes.fetch(:description, "description #{random}"),
|
||||
privacy: attributes.fetch(:privacy, 'public'),
|
||||
tags: attributes.fetch(:tags, ['tag 1']),
|
||||
type: attributes.fetch(:type, CartoDB::Visualization::Member::TYPE_CANONICAL),
|
||||
user_id: attributes.fetch(:user_id, UUIDTools::UUID.timestamp_create.to_s),
|
||||
locked: attributes.fetch(:locked, false)
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user