cartodb/spec/models/organization_spec.rb
2020-06-15 10:58:47 +08:00

694 lines
26 KiB
Ruby

require_relative '../spec_helper'
require_relative '../../app/models/visualization/collection'
require_relative '../../app/models/organization.rb'
require_relative 'organization_shared_examples'
require_relative '../factories/visualization_creation_helpers'
require 'helpers/account_types_helper'
require 'helpers/unique_names_helper'
require 'helpers/storage_helper'
require 'factories/organizations_contexts'
include CartoDB, StorageHelper, UniqueNamesHelper
describe 'refactored behaviour' do
it_behaves_like 'organization models' do
before(:each) do
@the_organization = ::Organization.where(id: @organization.id).first
end
def get_twitter_imports_count_by_organization_id(organization_id)
raise "id doesn't match" unless organization_id == @the_organization.id
@the_organization.get_twitter_imports_count
end
def get_geocoding_calls_by_organization_id(organization_id)
raise "id doesn't match" unless organization_id == @the_organization.id
@the_organization.get_geocoding_calls
end
def get_organization
@the_organization
end
end
end
describe Organization do
before(:all) do
@user = create_user(:quota_in_bytes => 524288000, :table_quota => 500)
end
after(:all) do
bypass_named_maps
begin
@user.destroy
rescue
# Silence error, can't do much more
end
end
before(:each) do
create_account_type_fg('ORGANIZATION USER')
end
describe '#destroy_cascade' do
include TableSharing
before(:each) do
@organization = FactoryGirl.create(:organization)
::User.any_instance.stubs(:create_in_central).returns(true)
::User.any_instance.stubs(:update_in_central).returns(true)
end
after(:each) do
@organization.delete if @organization.try(:persisted?)
end
it 'Destroys users and owner as well' do
organization = Organization.new(quota_in_bytes: 123456789000, name: 'wadus', seats: 5).save
owner = create_user(:quota_in_bytes => 524288000, :table_quota => 500)
owner_org = CartoDB::UserOrganization.new(organization.id, owner.id)
owner_org.promote_user_to_admin
owner.reload
organization.reload
user = create_user(quota_in_bytes: 524288000, table_quota: 500, organization_id: organization.id)
user.save
user.reload
organization.reload
organization.users.count.should eq 2
organization.destroy_cascade
Organization.where(id: organization.id).first.should be nil
::User.where(id: user.id).first.should be nil
::User.where(id: owner.id).first.should be nil
end
it 'Destroys viewer users with shared visualizations' do
organization = Organization.new(quota_in_bytes: 123456789000, name: 'wadus', seats: 3, viewer_seats: 2).save
owner = create_user(quota_in_bytes: 524288000, table_quota: 500)
owner_org = CartoDB::UserOrganization.new(organization.id, owner.id)
owner_org.promote_user_to_admin
user1 = create_user(organization_id: organization.id)
user2 = create_user(organization_id: organization.id)
table1 = create_table(user_id: user1.id)
table2 = create_table(user_id: user2.id)
share_table_with_user(table1, user2)
share_table_with_user(table2, user1)
user1.viewer = true
user1.save
user2.viewer = true
user2.save
organization.destroy_cascade
Organization.where(id: organization.id).first.should be nil
::User.where(id: user1.id).first.should be nil
::User.where(id: user2.id).first.should be nil
::User.where(id: owner.id).first.should be nil
Carto::UserTable.exists?(table1.id).should be_false
Carto::UserTable.exists?(table2.id).should be_false
end
it 'destroys users with unregistered tables' do
organization = Organization.new(quota_in_bytes: 123456789000, name: 'wadus', seats: 5).save
owner = create_user(quota_in_bytes: 524288000, table_quota: 500)
owner_org = CartoDB::UserOrganization.new(organization.id, owner.id)
owner_org.promote_user_to_admin
owner.reload
organization.reload
user = create_user(quota_in_bytes: 524288000, table_quota: 500, organization_id: organization.id)
user.save
user.reload
organization.reload
user.in_database.run('CREATE TABLE foobarbaz (id serial)')
organization.users.count.should eq 2
organization.destroy_cascade
Organization.where(id: organization.id).first.should be nil
::User.where(id: user.id).first.should be nil
::User.where(id: owner.id).first.should be nil
end
it 'destroys its groups through the extension' do
Carto::Group.any_instance.expects(:destroy_group_with_extension).once
FactoryGirl.create(:carto_group, organization: Carto::Organization.find(@organization.id))
@organization.destroy
end
it 'destroys assets' do
bypass_storage
asset = FactoryGirl.create(:organization_asset,
organization_id: @organization.id)
@organization.destroy
Carto::Asset.exists?(asset.id).should be_false
end
it 'calls :delete_in_central if delete_in_central parameter is true' do
pending "Don't implemented. See Organization#destroy_cascade"
@organization.expects(:delete_in_central).once
@organization.destroy_cascade(delete_in_central: true)
end
end
describe '#add_user_to_org' do
it 'Tests adding a user to an organization (but no owner)' do
org_quota = 123456789000
org_name = unique_name('org')
org_seats = 5
username = @user.username
organization = Organization.new
organization.name = org_name
organization.quota_in_bytes = org_quota
organization.seats = org_seats
organization.save
organization.valid?.should eq true
organization.errors.should eq Hash.new
@user.organization = organization
@user.save
user = ::User.where(username: username).first
user.should_not be nil
user.organization_id.should_not eq nil
user.organization_id.should eq organization.id
user.organization.should_not eq nil
user.organization.id.should eq organization.id
user.organization.name.should eq org_name
user.organization.quota_in_bytes.should eq org_quota
user.organization.seats.should eq org_seats
@user.organization = nil
@user.save
organization.destroy
end
it 'validates viewer and builder quotas' do
quota = 123456789000
name = unique_name('org')
seats = 1
viewer_seats = 1
organization = Organization.new(name: name, quota_in_bytes: quota, seats: seats, viewer_seats: viewer_seats).save
user = create_validated_user
CartoDB::UserOrganization.new(organization.id, user.id).promote_user_to_admin
organization.reload
user.reload
organization.remaining_seats.should eq 0
organization.remaining_viewer_seats.should eq 1
organization.users.should include(user)
viewer = create_validated_user(organization: organization, viewer: true)
viewer.valid? should be_true
viewer.reload
organization.reload
organization.remaining_seats.should eq 0
organization.remaining_viewer_seats.should eq 0
organization.users.should include(viewer)
builder = create_validated_user(organization: organization, viewer: false)
organization.reload
organization.remaining_seats.should eq 0
organization.remaining_viewer_seats.should eq 0
organization.users.should_not include(builder)
viewer2 = create_validated_user(organization: organization, viewer: true)
organization.reload
organization.remaining_seats.should eq 0
organization.remaining_viewer_seats.should eq 0
organization.users.should_not include(viewer2)
organization.seats = 0
organization.viewer_seats = 0
organization.valid?.should be_false
organization.errors.should include :seats, :viewer_seats
organization.destroy_cascade
end
it 'allows saving user if organization has no seats left' do
organization = FactoryGirl.create(:organization, seats: 2, viewer_seats: 0, quota_in_bytes: 10)
user = create_validated_user(quota_in_bytes: 1)
CartoDB::UserOrganization.new(organization.id, user.id).promote_user_to_admin
organization.reload
user.reload
builder = create_validated_user(organization: organization, viewer: false, quota_in_bytes: 2)
builder.organization.reload
builder.set_fields({ quota_in_bytes: 1 }, [:quota_in_bytes])
builder.save(raise_on_failure: true)
builder.reload
builder.quota_in_bytes.should eq 1
organization.destroy_cascade
end
it 'Tests setting a user as the organization owner' do
org_name = unique_name('org')
organization = Organization.new(quota_in_bytes: 123456789000, name: org_name, seats: 5).save
user = create_user(:quota_in_bytes => 524288000, :table_quota => 500)
user_org = CartoDB::UserOrganization.new(organization.id, user.id)
# This also covers the usecase of an user being moved to its own schema (without org)
user_org.promote_user_to_admin
organization.reload
user.reload
user.organization.id.should eq organization.id
user.organization.owner.id.should eq user.id
user.database_schema.should eq user.username
user_org = CartoDB::UserOrganization.new(organization.id, user.id)
expect {
user_org.promote_user_to_admin
}.to raise_error
user.destroy
end
end
describe '#org_members_and_owner_removal' do
it 'Tests removing a normal member from the organization' do
::User.any_instance.stubs(:create_in_central).returns(true)
::User.any_instance.stubs(:update_in_central).returns(true)
org_name = unique_name('org')
organization = Organization.new(quota_in_bytes: 123456789000, name: org_name, seats: 5).save
owner = create_user(:quota_in_bytes => 524288000, :table_quota => 500)
user_org = CartoDB::UserOrganization.new(organization.id, owner.id)
user_org.promote_user_to_admin
organization.reload
owner.reload
member1 = create_user(:quota_in_bytes => 524288000, :table_quota => 500, organization_id: organization.id)
member1.reload
organization.reload
member2 = create_user(:quota_in_bytes => 524288000, :table_quota => 500, organization_id: organization.id)
member2.reload
organization.users.count.should eq 3
results = member1.in_database(as: :public_user).fetch(%Q{
SELECT has_function_privilege('#{member1.database_public_username}', 'CDB_QueryTablesText(text)', 'execute')
}).first
results.nil?.should eq false
results[:has_function_privilege].should eq true
member1.destroy
organization.reload
organization.users.count.should eq 2
results = member2.in_database(as: :public_user).fetch(%Q{
SELECT has_function_privilege('#{member2.database_public_username}', 'CDB_QueryTablesText(text)', 'execute')
}).first
results.nil?.should eq false
results[:has_function_privilege].should eq true
# Can't remove owner if other members exist
expect {
owner.destroy
}.to raise_error CartoDB::BaseCartoDBError
member2.destroy
organization.reload
organization.users.count.should eq 1
results = owner.in_database(as: :public_user).fetch(%Q{
SELECT has_function_privilege('#{owner.database_public_username}', 'CDB_QueryTablesText(text)', 'execute')
}).first
results.nil?.should eq false
results[:has_function_privilege].should eq true
owner.destroy
expect {
organization.reload
}.to raise_error Sequel::Error
end
it 'Tests removing a normal member with analysis tables' do
::User.any_instance.stubs(:create_in_central).returns(true)
::User.any_instance.stubs(:update_in_central).returns(true)
org_name = unique_name('org')
organization = Organization.new(quota_in_bytes: 123456789000, name: org_name, seats: 5).save
owner = create_test_user('orgowner')
user_org = CartoDB::UserOrganization.new(organization.id, owner.id)
user_org.promote_user_to_admin
organization.reload
owner.reload
member1 = create_test_user('member1', organization)
create_random_table(member1, 'analysis_user_table')
create_random_table(member1, 'users_table')
member1.in_database.run(%{CREATE TABLE #{member1.database_schema}.analysis_4bd65e58e4_246c4acb2c67e4f3330d76c4be7c6deb8e07f344 (id serial)})
member1.reload
organization.reload
organization.users.count.should eq 2
results = member1.in_database(as: :public_user).fetch(%{
SELECT has_function_privilege('#{member1.database_public_username}', 'CDB_QueryTablesText(text)', 'execute')
}).first
results.nil?.should eq false
results[:has_function_privilege].should eq true
member1.destroy
organization.reload
organization.users.count.should eq 1
end
end
describe '#non_org_user_removal' do
it 'Tests removing a normal user' do
initial_count = ::User.all.count
user = create_user(:quota_in_bytes => 524288000, :table_quota => 50)
::User.all.count.should eq (initial_count + 1)
user.destroy
::User.all.count.should eq initial_count
::User.all.collect(&:id).should_not include(user.id)
end
end
describe '#users_in_same_db_removal_error' do
it "Tests that if 2+ users somehow have same database name, can't be deleted" do
user2 = create_user(:quota_in_bytes => 524288000, :table_quota => 50, :database_name => @user.database_name)
user2.database_name = @user.database_name
user2.save
expect {
user2.destroy
}.to raise_error CartoDB::BaseCartoDBError
end
end
describe '#unique_name' do
it 'Tests uniqueness of name' do
org_name = unique_name('org')
organization = Organization.new
organization.name = org_name
organization.quota_in_bytes = 123
organization.seats = 1
organization.errors
organization.valid?.should eq true
# Repeated username
organization.name = @user.username
organization.valid?.should eq false
organization.name = org_name
organization.save
organization2 = Organization.new
# Repeated name
organization2.name = org_name
organization2.quota_in_bytes = 123
organization2.seats = 1
organization2.valid?.should eq false
organization.destroy
end
end
describe "#get_api_calls and #get_geocodings" do
before(:each) do
@organization = create_organization_with_users(name: 'overquota-org')
end
after(:each) do
@organization.destroy
end
it "should return the sum of the api_calls for all organization users" do
::User.any_instance.stubs(:get_api_calls).returns (0..30).to_a
@organization.get_api_calls.should == (0..30).to_a.sum * @organization.users.size
end
end
describe '.overquota', focus: true do
before(:all) do
@organization = create_organization_with_users(name: 'overquota-org')
@owner = User.where(id: @organization.owner_id).first
end
after(:all) do
@organization.destroy
end
it "should return organizations over their geocoding quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.overquota.should be_empty
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(10)
Organization.any_instance.stubs(:get_geocoding_calls).returns 30
Organization.any_instance.stubs(:geocoding_quota).returns 10
Organization.overquota.map(&:id).should include(@organization.id)
Organization.overquota.size.should == Organization.count
end
it "should return organizations over their here isolines quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.overquota.should be_empty
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(10)
Organization.any_instance.stubs(:get_geocoding_calls).returns 0
Organization.any_instance.stubs(:geocoding_quota).returns 10
Organization.any_instance.stubs(:get_here_isolines_calls).returns 30
Organization.any_instance.stubs(:here_isolines_quota).returns 10
Organization.overquota.map(&:id).should include(@organization.id)
Organization.overquota.size.should == Organization.count
end
it "should return organizations over their data observatory snapshot quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.overquota.should be_empty
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(10)
Organization.any_instance.stubs(:get_geocoding_calls).returns 0
Organization.any_instance.stubs(:geocoding_quota).returns 10
Organization.any_instance.stubs(:get_obs_snapshot_calls).returns 30
Organization.any_instance.stubs(:obs_snapshot_quota).returns 10
Organization.overquota.map(&:id).should include(@organization.id)
Organization.overquota.size.should == Organization.count
end
it "should return organizations over their data observatory general quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.overquota.should be_empty
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(10)
Organization.any_instance.stubs(:get_geocoding_calls).returns 0
Organization.any_instance.stubs(:geocoding_quota).returns 10
Organization.any_instance.stubs(:get_obs_snapshot_calls).returns 0
Organization.any_instance.stubs(:obs_snapshot_quota).returns 10
Organization.any_instance.stubs(:get_obs_general_calls).returns 30
Organization.any_instance.stubs(:obs_general_quota).returns 10
Organization.overquota.map(&:id).should include(@organization.id)
Organization.overquota.size.should == Organization.count
end
it "should return organizations near their geocoding quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(120)
Organization.any_instance.stubs(:get_geocoding_calls).returns(81)
Organization.any_instance.stubs(:geocoding_quota).returns(100)
Organization.overquota.should be_empty
Organization.overquota(0.20).map(&:id).should include(@organization.id)
Organization.overquota(0.20).size.should == Organization.count
Organization.overquota(0.10).should be_empty
end
it "should return organizations near their here isolines quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(120)
Organization.any_instance.stubs(:get_geocoding_calls).returns(0)
Organization.any_instance.stubs(:geocoding_quota).returns(100)
Organization.any_instance.stubs(:get_here_isolines_calls).returns(81)
Organization.any_instance.stubs(:here_isolines_quota).returns(100)
Organization.any_instance.stubs(:get_obs_snapshot_calls).returns(0)
Organization.any_instance.stubs(:obs_snapshot_quota).returns(100)
Organization.any_instance.stubs(:get_obs_general_calls).returns(0)
Organization.any_instance.stubs(:obs_general_quota).returns(100)
Organization.any_instance.stubs(:get_mapzen_routing_calls).returns(81)
Organization.any_instance.stubs(:mapzen_routing_quota).returns(100)
Organization.overquota.should be_empty
Organization.overquota(0.20).map(&:id).should include(@organization.id)
Organization.overquota(0.20).size.should == Organization.count
Organization.overquota(0.10).should be_empty
end
it "should return organizations near their data observatory snapshot quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(120)
Organization.any_instance.stubs(:get_geocoding_calls).returns(0)
Organization.any_instance.stubs(:geocoding_quota).returns(100)
Organization.any_instance.stubs(:get_here_isolines_calls).returns(0)
Organization.any_instance.stubs(:here_isolines_quota).returns(100)
Organization.any_instance.stubs(:get_obs_general_calls).returns(0)
Organization.any_instance.stubs(:obs_general_quota).returns(100)
Organization.any_instance.stubs(:get_obs_snapshot_calls).returns(81)
Organization.any_instance.stubs(:obs_snapshot_quota).returns(100)
Organization.any_instance.stubs(:get_mapzen_routing_calls).returns(0)
Organization.any_instance.stubs(:mapzen_routing_quota).returns(100)
Organization.overquota.should be_empty
Organization.overquota(0.20).map(&:id).should include(@organization.id)
Organization.overquota(0.20).size.should == Organization.count
Organization.overquota(0.10).should be_empty
end
it "should return organizations near their data observatory general quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(120)
Organization.any_instance.stubs(:get_geocoding_calls).returns(0)
Organization.any_instance.stubs(:geocoding_quota).returns(100)
Organization.any_instance.stubs(:get_here_isolines_calls).returns(0)
Organization.any_instance.stubs(:here_isolines_quota).returns(100)
Organization.any_instance.stubs(:get_obs_snapshot_calls).returns(0)
Organization.any_instance.stubs(:obs_snapshot_quota).returns(100)
Organization.any_instance.stubs(:get_obs_general_calls).returns(81)
Organization.any_instance.stubs(:obs_general_quota).returns(100)
Organization.any_instance.stubs(:get_mapzen_routing_calls).returns(0)
Organization.any_instance.stubs(:mapzen_routing_quota).returns(100)
Organization.overquota.should be_empty
Organization.overquota(0.20).map(&:id).should include(@organization.id)
Organization.overquota(0.20).size.should == Organization.count
Organization.overquota(0.10).should be_empty
end
it "should return organizations over their mapzen routing quota" do
Organization.any_instance.stubs(:owner).returns(@owner)
Organization.overquota.should be_empty
Organization.any_instance.stubs(:get_api_calls).returns(0)
Organization.any_instance.stubs(:map_view_quota).returns(10)
Organization.any_instance.stubs(:get_geocoding_calls).returns 0
Organization.any_instance.stubs(:geocoding_quota).returns 10
Organization.any_instance.stubs(:get_here_isolines_calls).returns(0)
Organization.any_instance.stubs(:here_isolines_quota).returns(100)
Organization.any_instance.stubs(:get_mapzen_routing_calls).returns 30
Organization.any_instance.stubs(:mapzen_routing_quota).returns 10
Organization.overquota.map(&:id).should include(@organization.id)
Organization.overquota.size.should == Organization.count
end
end
it 'should validate password_expiration_in_d' do
organization = FactoryGirl.create(:organization)
organization.valid?.should be_true
organization.password_expiration_in_d.should_not be
# minimum 1 day
organization = FactoryGirl.create(:organization, password_expiration_in_d: 1)
organization.valid?.should be_true
organization.password_expiration_in_d.should eq 1
expect {
organization = FactoryGirl.create(:organization, password_expiration_in_d: 0)
}.to raise_error(Sequel::ValidationFailed, /password_expiration_in_d must be greater than 0 and lower than 366/)
# maximum 1 year
organization = FactoryGirl.create(:organization, password_expiration_in_d: 365)
organization.valid?.should be_true
organization.password_expiration_in_d.should eq 365
expect {
organization = FactoryGirl.create(:organization, password_expiration_in_d: 366)
}.to raise_error(Sequel::ValidationFailed, /password_expiration_in_d must be greater than 0 and lower than 366/)
# nil or blank means unlimited
organization = FactoryGirl.create(:organization, password_expiration_in_d: nil)
organization.valid?.should be_true
organization.password_expiration_in_d.should_not be
organization = FactoryGirl.create(:organization, password_expiration_in_d: '')
organization.valid?.should be_true
organization.password_expiration_in_d.should_not be
# defaults to global config if no value
organization = FactoryGirl.build(:organization, password_expiration_in_d: 1)
organization.valid?.should be_true
organization.save
organization = Carto::Organization.find(organization.id)
organization.valid?.should be_true
organization.password_expiration_in_d.should eq 1
organization.password_expiration_in_d = nil
organization.valid?.should be_true
organization.save
organization = Carto::Organization.find(organization.id)
organization.password_expiration_in_d.should_not be
# override default config if a value is set
organization = FactoryGirl.create(:organization, password_expiration_in_d: 10)
organization.valid?.should be_true
organization.password_expiration_in_d.should eq 10
# keep values configured
organization = Carto::Organization.find(organization.id)
organization.valid?.should be_true
organization.password_expiration_in_d.should eq 10
end
it 'should handle redis keys properly' do
@organization = create_organization_with_users(name: 'overquota-org')
$users_metadata.hkeys(@organization.key).should_not be_empty
@organization.destroy
$users_metadata.hkeys(@organization.key).should be_empty
end
def random_attributes(attributes = {})
random = unique_name('viz')
{
name: attributes.fetch(:name, random),
description: attributes.fetch(:description, "description #{random}"),
privacy: attributes.fetch(:privacy, Visualization::Member::PRIVACY_PUBLIC),
tags: attributes.fetch(:tags, ['tag 1']),
type: attributes.fetch(:type, Visualization::Member::TYPE_DERIVED),
user_id: attributes.fetch(:user_id, UUIDTools::UUID.timestamp_create.to_s)
}
end # random_attributes
end