blob: fb48faacfc07471ceab02740f61818fb2fac7846 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import json
import os
from cStringIO import StringIO
import urllib2
import PIL
from mock import patch
from tg import config
from nose.tools import assert_equal, assert_in, assert_not_equal
from ming.orm.ormsession import ThreadLocalORMSession, session
from paste.httpexceptions import HTTPFound
from pylons import app_globals as g, tmpl_context as c
import allura
from allura import model as M
from allura.tests import TestController
from allura.tests import decorators as td
from allura.lib import helpers as h
from allura.lib import utils
from alluratest.controller import setup_trove_categories
class TestNeighborhood(TestController):
def test_home_project(self):
r = self.app.get('/adobe/wiki/')
assert r.location.endswith('/adobe/wiki/Home/')
r = r.follow()
assert 'This is the "Adobe" neighborhood' in str(r), str(r)
r = self.app.get(
'/adobe/admin/', extra_environ=dict(username='test-user'),
status=403)
def test_redirect(self):
r = self.app.post('/adobe/_admin/update',
params=dict(redirect='wiki/Home/'),
extra_environ=dict(username='root'))
r = self.app.get('/adobe/')
assert r.location.endswith('/adobe/wiki/Home/')
def test_admin(self):
r = self.app.get('/adobe/_admin/', extra_environ=dict(username='root'))
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
r = self.app.get('/adobe/_admin/accolades',
extra_environ=dict(username='root'))
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.features['google_analytics'] = True
r = self.app.post('/adobe/_admin/update',
params=dict(name='Mozq1', css='',
homepage='# MozQ1!', tracking_id='U-123456'),
extra_environ=dict(username='root'))
r = self.app.post('/adobe/_admin/update',
params=dict(name='Mozq1', css='',
homepage='# MozQ1!\n[Root]'),
extra_environ=dict(username='root'))
# make sure project_template is validated as proper json
r = self.app.post('/adobe/_admin/update',
params=dict(project_template='{'),
extra_environ=dict(username='root'))
assert 'Invalid JSON' in r
def test_admin_overview_audit_log(self):
def check_log(message):
return M.AuditLog.query.find({'message': message}).count() == 1
nbhd = M.Neighborhood.query.get(name='Projects')
nbhd.features['css'] = 'custom'
nbhd.features['google_analytics'] = True
params = {
'name': 'Pjs',
'redirect': 'http://fake.org/',
'show_title': 'false',
'allow_browse': 'false',
'css': '.class { border: 1px; }',
'tracking_id': 'U-123456',
'homepage': '[Homepage]',
'project_list_url': 'http://fake.org/project_list',
'project_template': '{"name": "template"}',
'anchored_tools': 'wiki:Wiki',
'prohibited_tools': 'wiki, tickets'
}
self.app.post('/p/_admin/update', params=params,
extra_environ=dict(username='root'))
# must get as many log records as many values are updated
assert M.AuditLog.query.find().count() == len(params)
assert check_log('change neighborhood name to Pjs')
assert check_log('change neighborhood redirect to http://fake.org/')
assert check_log('change neighborhood show title to False')
assert check_log('change neighborhood allow browse to False')
assert check_log('change neighborhood css to .class { border: 1px; }')
assert check_log('change neighborhood homepage to [Homepage]')
assert check_log('change neighborhood project list url to '
'http://fake.org/project_list')
assert check_log('change neighborhood project template to '
'{"name": "template"}')
assert check_log('update neighborhood tracking_id')
assert check_log('update neighborhood prohibited tools')
def test_prohibited_tools(self):
self.app.post('/p/_admin/update',
params=dict(name='Projects',
prohibited_tools='wiki, tickets'),
extra_environ=dict(username='root'))
r = self.app.get('/p/_admin/overview', extra_environ=dict(username='root'))
assert 'wiki, tickets' in r
c.user = M.User.query.get(username='root')
c.project = M.Project.query.get(shortname='test')
data = c.project.nav_data(admin_options=True)
assert 'Wiki' not in data
assert 'Tickets' not in data
r = self.app.post('/p/_admin/update',
params=dict(name='Projects',
prohibited_tools='wiki, test'),
extra_environ=dict(username='root'))
assert 'error' in self.webflash(r), self.webflash(r)
@td.with_wiki
def test_anchored_tools(self):
neighborhood = M.Neighborhood.query.get(name='Projects')
r = self.app.post('/p/_admin/update',
params=dict(name='Projects',
anchored_tools='wiki:Wiki, tickets:Ticket'),
extra_environ=dict(username='root'))
assert 'error' not in self.webflash(r)
r = self.app.post('/p/_admin/update',
params=dict(name='Projects',
anchored_tools='w!iki:Wiki, tickets:Ticket'),
extra_environ=dict(username='root'))
assert 'error' in self.webflash(r)
assert_equal(neighborhood.anchored_tools, 'wiki:Wiki, tickets:Ticket')
r = self.app.post('/p/_admin/update',
params=dict(name='Projects',
anchored_tools='wiki:Wiki,'),
extra_environ=dict(username='root'))
assert 'error' in self.webflash(r)
assert_equal(neighborhood.anchored_tools, 'wiki:Wiki, tickets:Ticket')
r = self.app.post('/p/_admin/update',
params=dict(name='Projects',
anchored_tools='badname,'),
extra_environ=dict(username='root'))
assert 'error' in self.webflash(r)
assert_equal(neighborhood.anchored_tools, 'wiki:Wiki, tickets:Ticket')
r = self.app.get('/p/test/admin/overview')
top_nav = r.html.find(id='top_nav')
assert top_nav.find(href='/p/test/wiki/'), top_nav
assert top_nav.find(href='/p/test/tickets/'), top_nav
c.user = M.User.query.get(username='root')
c.project = M.Project.query.get(shortname='test')
data = c.project.nav_data(admin_options=True)
for tool in data['menu']:
if tool['name'].lower() == 'wiki':
menu = [name['text'] for name in tool['admin_options']]
assert 'Delete' not in menu
break
def test_show_title(self):
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
neighborhood = M.Neighborhood.query.get(name='Adobe')
# if not set show_title must be True
assert neighborhood.show_title
# title should be present
assert 'class="project_title"' in str(r)
r = self.app.post('/adobe/_admin/update',
params=dict(name='Mozq1', css='',
homepage='# MozQ1!',
tracking_id='U-123456',
show_title='false'),
extra_environ=dict(username='root'))
# no title now
r = self.app.get('/adobe/', extra_environ=dict(username='root'))
assert 'class="project_title"' not in str(r)
r = self.app.get('/adobe/wiki/Home/',
extra_environ=dict(username='root'))
assert 'class="project_title"' not in str(r)
# title must be present on project page
r = self.app.get('/adobe/adobe-1/admin/',
extra_environ=dict(username='root'))
assert 'class="project_title"' in str(r)
def test_admin_stats_del_count(self):
neighborhood = M.Neighborhood.query.get(name='Adobe')
proj = M.Project.query.get(neighborhood_id=neighborhood._id)
proj.deleted = True
ThreadLocalORMSession.flush_all()
r = self.app.get('/adobe/_admin/stats/',
extra_environ=dict(username='root'))
assert 'Deleted: 1' in r
assert 'Private: 0' in r
def test_admin_stats_priv_count(self):
neighborhood = M.Neighborhood.query.get(name='Adobe')
proj = M.Project.query.get(neighborhood_id=neighborhood._id)
proj.deleted = False
proj.private = True
ThreadLocalORMSession.flush_all()
r = self.app.get('/adobe/_admin/stats/',
extra_environ=dict(username='root'))
assert 'Deleted: 0' in r
assert 'Private: 1' in r
def test_admin_stats_adminlist(self):
neighborhood = M.Neighborhood.query.get(name='Adobe')
proj = M.Project.query.get(neighborhood_id=neighborhood._id)
proj.private = False
ThreadLocalORMSession.flush_all()
r = self.app.get('/adobe/_admin/stats/adminlist',
extra_environ=dict(username='root'))
pq = M.Project.query.find(
dict(neighborhood_id=neighborhood._id, deleted=False))
pq.sort('name')
projects = pq.skip(0).limit(int(25)).all()
for proj in projects:
admin_role = M.ProjectRole.query.get(
project_id=proj.root_project._id, name='Admin')
if admin_role is None:
continue
user_role_list = M.ProjectRole.query.find(
dict(project_id=proj.root_project._id, name=None)).all()
for ur in user_role_list:
if ur.user is not None and admin_role._id in ur.roles:
assert proj.name in r
assert ur.user.username in r
def test_icon(self):
file_name = 'neo-icon-set-454545-256x350.png'
file_path = os.path.join(
allura.__path__[0], 'nf', 'allura', 'images', file_name)
file_data = file(file_path).read()
upload = ('icon', file_name, file_data)
r = self.app.get('/adobe/_admin/', extra_environ=dict(username='root'))
r = self.app.post('/adobe/_admin/update',
params=dict(name='Mozq1', css='',
homepage='# MozQ1'),
extra_environ=dict(username='root'), upload_files=[upload])
r = self.app.get('/adobe/icon')
image = PIL.Image.open(StringIO(r.body))
assert image.size == (48, 48)
r = self.app.get('/adobe/icon?foo=bar')
def test_google_analytics(self):
# analytics allowed
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.features['google_analytics'] = True
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
assert 'Analytics Tracking ID' in r
r = self.app.get('/adobe/adobe-1/admin/overview',
extra_environ=dict(username='root'))
assert 'Analytics Tracking ID' in r
r = self.app.post('/adobe/_admin/update',
params=dict(name='Adobe', css='',
homepage='# MozQ1', tracking_id='U-123456'),
extra_environ=dict(username='root'))
r = self.app.post('/adobe/adobe-1/admin/update',
params=dict(tracking_id='U-654321'),
extra_environ=dict(username='root'))
r = self.app.get('/adobe/adobe-1/admin/overview',
extra_environ=dict(username='root'))
assert "_add_tracking('nbhd', 'U-123456');" in r, r
assert "_add_tracking('proj', 'U-654321');" in r
# analytics not allowed
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.features['google_analytics'] = False
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
assert 'Analytics Tracking ID' not in r
r = self.app.get('/adobe/adobe-1/admin/overview',
extra_environ=dict(username='root'))
assert 'Analytics Tracking ID' not in r
r = self.app.get('/adobe/adobe-1/admin/overview',
extra_environ=dict(username='root'))
assert "_add_tracking('nbhd', 'U-123456');" not in r
assert "_add_tracking('proj', 'U-654321');" not in r
def test_custom_css(self):
test_css = '.test{color:red;}'
custom_css = 'Custom CSS'
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.css = test_css
neighborhood.features['css'] = 'none'
r = self.app.get('/adobe/')
assert test_css not in r
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
assert custom_css not in r
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.features['css'] = 'picker'
r = self.app.get('/adobe/')
while isinstance(r.response, HTTPFound):
r = r.follow()
assert test_css in r
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
assert custom_css in r
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.features['css'] = 'custom'
r = self.app.get('/adobe/')
while isinstance(r.response, HTTPFound):
r = r.follow()
assert test_css in r
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
assert custom_css in r
def test_picker_css(self):
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.features['css'] = 'picker'
r = self.app.get('/adobe/_admin/overview',
extra_environ=dict(username='root'))
assert 'Project title, font' in r
assert 'Project title, color' in r
assert 'Bar on top' in r
assert 'Title bar, background' in r
assert 'Title bar, foreground' in r
r = self.app.post('/adobe/_admin/update',
params={'name': 'Adobe',
'css': '',
'homepage': '',
'css-projecttitlefont': 'arial,sans-serif',
'css-projecttitlecolor': 'green',
'css-barontop': '#555555',
'css-titlebarbackground': '#333',
'css-titlebarcolor': '#444'},
extra_environ=dict(username='root'), upload_files=[])
neighborhood = M.Neighborhood.query.get(name='Adobe')
assert '/*projecttitlefont*/.project_title{font-family:arial,sans-serif;}' in neighborhood.css
assert '/*projecttitlecolor*/.project_title{color:green;}' in neighborhood.css
assert '/*barontop*/.pad h2.colored {background-color:#555555; background-image: none;}' in neighborhood.css
assert '/*titlebarbackground*/.pad h2.title{background-color:#333; background-image: none;}' in neighborhood.css
assert "/*titlebarcolor*/.pad h2.title, .pad h2.title small a {color:#444;}" in neighborhood.css
def test_max_projects(self):
# Set max value to unlimit
neighborhood = M.Neighborhood.query.get(name='Projects')
neighborhood.features['max_projects'] = None
r = self.app.post('/p/register',
params=dict(
project_unixname='maxproject1', project_name='Max project1',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='root'), status=302)
assert '/p/maxproject1/admin' in r.location
# Set max value to 0
neighborhood = M.Neighborhood.query.get(name='Projects')
neighborhood.features['max_projects'] = 0
r = self.app.post('/p/register',
params=dict(
project_unixname='maxproject2', project_name='Max project2',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='root'))
while isinstance(r.response, HTTPFound):
r = r.follow()
assert 'You have exceeded the maximum number of projects' in r
def test_project_rate_limit(self):
# Set rate limit to unlimit
with h.push_config(config, **{'project.rate_limits': '{}'}):
r = self.app.post('/p/register',
params=dict(
project_unixname='rateproject1', project_name='Rate project1',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='test-user-1'), status=302)
assert '/p/rateproject1/admin' in r.location
# Set rate limit to 1 in first hour of user account
with h.push_config(config, **{'project.rate_limits': '{"3600": 1}'}):
r = self.app.post('/p/register',
params=dict(
project_unixname='rateproject2', project_name='Rate project2',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='test-user-1'))
while isinstance(r.response, HTTPFound):
r = r.follow()
assert 'Project creation rate limit exceeded. Please try again later.' in r
def test_project_rate_limit_admin(self):
# Set rate limit to unlimit
with h.push_config(config, **{'project.rate_limits': '{}'}):
r = self.app.post('/p/register',
params=dict(
project_unixname='rateproject1', project_name='Rate project1',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='root'), status=302)
assert '/p/rateproject1/admin' in r.location
# Set rate limit to 1 in first hour of user account
with h.push_config(config, **{'project.rate_limits': '{"3600": 1}'}):
r = self.app.post('/p/register',
params=dict(
project_unixname='rateproject2', project_name='Rate project2',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='root'))
assert '/p/rateproject2/admin' in r.location
def test_invite(self):
p_nbhd_id = str(M.Neighborhood.query.get(name='Projects')._id)
r = self.app.get('/adobe/_moderate/',
extra_environ=dict(username='root'))
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='adobe-1', invite='on',
neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'error' in r
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='no_such_user',
invite='on', neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'error' in r
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='test', invite='on',
neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'invited' in r, r
assert 'warning' not in r
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='test', invite='on',
neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'warning' in r
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='test', uninvite='on',
neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'uninvited' in r
assert 'warning' not in r
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='test', uninvite='on',
neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'warning' in r
r = self.app.post('/adobe/_moderate/invite',
params=dict(pid='test', invite='on',
neighborhood_id=p_nbhd_id),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'invited' in r
assert 'warning' not in r
def test_evict(self):
r = self.app.get('/adobe/_moderate/',
extra_environ=dict(username='root'))
r = self.app.post('/adobe/_moderate/evict',
params=dict(pid='test'),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'error' in r
r = self.app.post('/adobe/_moderate/evict',
params=dict(pid='adobe-1'),
extra_environ=dict(username='root'))
r = self.app.get(r.location, extra_environ=dict(username='root'))
assert 'adobe-1 evicted to Projects' in r
def test_home(self):
self.app.get('/adobe/')
def test_register(self):
r = self.app.get('/adobe/register', status=405)
r = self.app.post('/adobe/register',
params=dict(
project_unixname='', project_name='Nothing',
project_description='', neighborhood='Adobe'),
antispam=True,
extra_environ=dict(username='root'))
assert r.html.find('div', {'class': 'error'}
).string == 'Please use 3-15 small letters, numbers, and dashes.'
r = self.app.post('/adobe/register',
params=dict(
project_unixname='mymoz', project_name='My Moz',
project_description='', neighborhood='Adobe'),
antispam=True,
extra_environ=dict(username='*anonymous'),
status=302)
r = self.app.post('/adobe/register',
params=dict(
project_unixname='foo.mymoz', project_name='My Moz',
project_description='', neighborhood='Adobe'),
antispam=True,
extra_environ=dict(username='root'))
assert r.html.find('div', {'class': 'error'}
).string == 'Please use 3-15 small letters, numbers, and dashes.'
r = self.app.post('/p/register',
params=dict(
project_unixname='test', project_name='Tester',
project_description='', neighborhood='Projects'),
antispam=True,
extra_environ=dict(username='root'))
assert r.html.find('div', {'class': 'error'}
).string == 'This project name is taken.'
r = self.app.post('/adobe/register',
params=dict(
project_unixname='mymoz', project_name='My Moz',
project_description='', neighborhood='Adobe'),
antispam=True,
extra_environ=dict(username='root'),
status=302)
def test_register_private_fails_for_anon(self):
r = self.app.post(
'/p/register',
params=dict(
project_unixname='mymoz',
project_name='My Moz',
project_description='',
neighborhood='Projects',
private_project='on'),
antispam=True,
extra_environ=dict(username='*anonymous'),
status=302)
assert config.get('auth.login_url', '/auth/') in r.location, r.location
def test_register_private_fails_for_non_admin(self):
self.app.post(
'/p/register',
params=dict(
project_unixname='mymoz',
project_name='My Moz',
project_description='',
neighborhood='Projects',
private_project='on'),
antispam=True,
extra_environ=dict(username='test-user'),
status=403)
def test_register_private_fails_for_non_private_neighborhood(self):
# Turn off private
neighborhood = M.Neighborhood.query.get(name='Projects')
neighborhood.features['private_projects'] = False
r = self.app.get('/p/add_project', extra_environ=dict(username='root'))
assert 'private_project' not in r
r = self.app.post(
'/p/register',
params=dict(
project_unixname='myprivate1',
project_name='My Priv1',
project_description='',
neighborhood='Projects',
private_project='on'),
antispam=True,
extra_environ=dict(username='root'))
cookies = r.headers.getall('Set-Cookie')
flash_msg_cookies = map(urllib2.unquote, cookies)
assert any('Internal Error' in cookie for cookie in flash_msg_cookies)
proj = M.Project.query.get(
shortname='myprivate1', neighborhood_id=neighborhood._id)
assert proj is None
# Turn on private
neighborhood = M.Neighborhood.query.get(name='Projects')
neighborhood.features['private_projects'] = True
r = self.app.get('/p/add_project', extra_environ=dict(username='root'))
assert 'private_project' in r
self.app.post(
'/p/register',
params=dict(
project_unixname='myprivate2',
project_name='My Priv2',
project_description='',
neighborhood='Projects',
private_project='on'),
antispam=True,
extra_environ=dict(username='root'))
proj = M.Project.query.get(
shortname='myprivate2', neighborhood_id=neighborhood._id)
assert proj.private
def test_register_private_ok(self):
r = self.app.post(
'/p/register',
params=dict(
project_unixname='mymoz',
project_name='My Moz',
project_description='',
neighborhood='Projects',
private_project='on',
tools='wiki'),
antispam=True,
extra_environ=dict(username='root'),
status=302)
assert config.get('auth.login_url',
'/auth/') not in r.location, r.location
r = self.app.get(
'/p/mymoz/wiki/',
extra_environ=dict(username='root')).follow(extra_environ=dict(username='root'), status=200)
r = self.app.get(
'/p/mymoz/wiki/',
extra_environ=dict(username='*anonymous'),
status=302)
assert config.get('auth.login_url', '/auth/') in r.location, r.location
self.app.get(
'/p/mymoz/wiki/',
extra_environ=dict(username='test-user'),
status=403)
def test_project_template(self):
setup_trove_categories()
icon_url = 'file://' + \
os.path.join(allura.__path__[0], 'nf', 'allura',
'images', 'neo-icon-set-454545-256x350.png')
test_groups = [{
"name": "Viewer", # group will be created, all params are valid
"permissions": ["read"],
"usernames": ["user01"]
}, {
"name": "", # group won't be created - invalid name
"permissions": ["read"],
"usernames": ["user01"]
}, {
"name": "TestGroup1", # group won't be created - invalid perm name
"permissions": ["foobar"],
"usernames": ["user01"]
}, {
"name": "TestGroup2", # will be created; 'inspect' perm ignored
"permissions": ["read", "inspect"],
"usernames": ["user01", "user02"]
}, {
"name": "TestGroup3", # will be created with no users in group
"permissions": ["admin"]
}]
r = self.app.post('/adobe/_admin/update', params=dict(name='Mozq1',
css='',
homepage='# MozQ1!\n[Root]',
project_template="""{
"private":true,
"icon":{
"url":"%s",
"filename":"icon.png"
},
"tools":{
"wiki":{
"label":"Wiki",
"mount_point":"wiki",
"options":{
"show_right_bar":false,
"show_left_bar":false,
"show_discussion":false,
"some_url": "http://foo.com/$shortname/"
},
"home_text":"My home text!"
},
"discussion":{"label":"Discussion","mount_point":"discussion"},
"blog":{"label":"News","mount_point":"news","options":{
"show_discussion":false
}},
"admin":{"label":"Admin","mount_point":"admin"}
},
"tool_order":["wiki","discussion","news","admin"],
"labels":["mmi"],
"trove_cats":{
"topic":[247],
"developmentstatus":[11]
},
"groups": %s
}""" % (icon_url, json.dumps(test_groups))),
extra_environ=dict(username='root'))
r = self.app.post(
'/adobe/register',
params=dict(
project_unixname='testtemp',
project_name='Test Template',
project_description='',
neighborhood='Mozq1',
private_project='off'),
antispam=True,
extra_environ=dict(username='root'),
status=302).follow()
p = M.Project.query.get(shortname='testtemp')
# make sure the correct tools got installed in the right order
top_nav = r.html.find('div', {'id': 'top_nav'}).contents[1]
assert top_nav.contents[1].contents[1].contents[1]['href'] == '/adobe/testtemp/wiki/'
assert 'Wiki' in top_nav.contents[1].contents[1].contents[1].contents[0]
assert top_nav.contents[1].contents[3].contents[1]['href'] == '/adobe/testtemp/discussion/'
assert 'Discussion' in top_nav.contents[1].contents[3].contents[1].contents[0]
assert top_nav.contents[1].contents[5].contents[1]['href'] == '/adobe/testtemp/news/'
assert 'News' in top_nav.contents[1].contents[5].contents[1].contents[0]
assert top_nav.contents[1].contents[7].contents[1]['href'] == '/adobe/testtemp/admin/'
assert 'Admin' in top_nav.contents[1].contents[7].contents[1].contents[0]
# make sure project is private
r = self.app.get(
'/adobe/testtemp/wiki/',
extra_environ=dict(username='root')).follow(extra_environ=dict(username='root'), status=200)
r = self.app.get(
'/adobe/testtemp/wiki/',
extra_environ=dict(username='*anonymous'),
status=302)
# check the labels and trove cats
r = self.app.get('/adobe/testtemp/admin/trove')
assert 'mmi' in r
assert 'Topic :: Communications :: Telephony' in r
assert 'Development Status :: 5 - Production/Stable' in r
# check the wiki text
r = self.app.get('/adobe/testtemp/wiki/').follow()
assert "My home text!" in r
# check tool options
opts = p.app_config('wiki').options
assert_equal(False, opts.show_discussion)
assert_equal(False, opts.show_left_bar)
assert_equal(False, opts.show_right_bar)
assert_equal("http://foo.com/testtemp/", opts.some_url)
# check that custom groups/perms/users were setup correctly
roles = p.named_roles
for group in test_groups:
name = group.get('name')
permissions = group.get('permissions', [])
usernames = group.get('usernames', [])
if name in ('Viewer', 'TestGroup2', 'TestGroup3'):
role = M.ProjectRole.by_name(name, project=p)
# confirm role created in project
assert role in roles
for perm in permissions:
# confirm valid permissions added to role, and invalid
# permissions ignored
if perm in p.permissions:
assert M.ACE.allow(role._id, perm) in p.acl
else:
assert M.ACE.allow(role._id, perm) not in p.acl
# confirm valid users received role
for username in usernames:
user = M.User.by_username(username)
if user and user._id:
assert role in M.ProjectRole.by_user(
user, project=p).roles
# confirm roles with invalid json data are not created
if name in ('', 'TestGroup1'):
assert name not in roles
def test_projects_anchored_tools(self):
r = self.app.post('/adobe/_admin/update', params=dict(name='Adobe',
css='',
homepage='# Adobe!\n[Root]',
project_template="""{
"private":true,
"tools":{
"wiki":{
"label":"Wiki",
"mount_point":"wiki",
"options":{
"show_right_bar":false,
"show_left_bar":false,
"show_discussion":false,
"some_url": "http://foo.com/$shortname/"
},
"home_text":"My home text!"
},
"admin":{"label":"Admin","mount_point":"admin"}
},
"tool_order":["wiki","admin"],
}"""),
extra_environ=dict(username='root'))
neighborhood = M.Neighborhood.query.get(name='Adobe')
neighborhood.anchored_tools = 'wiki:Wiki'
r = self.app.post(
'/adobe/register',
params=dict(
project_unixname='testtemp',
project_name='Test Template',
project_description='',
neighborhood='Adobe',
private_project='off'),
antispam=True,
extra_environ=dict(username='root'))
r = self.app.get('/adobe/testtemp/admin/overview')
assert r.html.find('div', id='top_nav').find(
'a', href='/adobe/testtemp/wiki/'), r.html
assert r.html.find('div', id='top_nav').find(
'a', href='/adobe/testtemp/admin/'), r.html
def test_name_check(self):
for name in ('My+Moz', 'Te%st!', 'ab', 'a' * 16):
r = self.app.get(
'/p/check_names?neighborhood=Projects&project_unixname=%s' % name)
assert_equal(
r.json,
{'project_unixname': 'Please use 3-15 small letters, numbers, and dashes.'})
r = self.app.get(
'/p/check_names?neighborhood=Projects&project_unixname=mymoz')
assert_equal(r.json, {})
r = self.app.get(
'/p/check_names?neighborhood=Projects&project_unixname=test')
assert_equal(r.json,
{'project_unixname': 'This project name is taken.'})
@td.with_tool('test/sub1', 'Wiki', 'wiki')
def test_neighborhood_project(self):
self.app.get('/adobe/adobe-1/admin/', status=200)
self.app.get('/p/test/sub1/wiki/')
self.app.get('/p/test/sub1/', status=302)
self.app.get('/p/test/no-such-app/', status=404)
def test_neighborhood_namespace(self):
# p/test exists, so try creating adobe/test
self.app.get('/adobe/test/wiki/', status=404)
r = self.app.post('/adobe/register',
params=dict(
project_unixname='test', project_name='Test again',
project_description='', neighborhood='Adobe', tools='wiki'),
antispam=True,
extra_environ=dict(username='root'))
assert r.status_int == 302, r.html.find(
'div', {'class': 'error'}).string
r = self.app.get('/adobe/test/wiki/').follow(status=200)
def test_neighborhood_awards(self):
file_name = 'adobe_icon.png'
file_path = os.path.join(
allura.__path__[0], 'public', 'nf', 'images', file_name)
file_data = file(file_path).read()
upload = ('icon', file_name, file_data)
r = self.app.get('/adobe/_admin/awards',
extra_environ=dict(username='root'))
r = self.app.post('/adobe/_admin/awards/create',
params=dict(short='FOO', full='A basic foo award'),
extra_environ=dict(username='root'), upload_files=[upload])
r = self.app.post('/adobe/_admin/awards/create',
params=dict(short='BAR',
full='A basic bar award with no icon'),
extra_environ=dict(username='root'))
foo_id = str(M.Award.query.find(dict(short='FOO')).first()._id)
bar_id = str(M.Award.query.find(dict(short='BAR')).first()._id)
r = self.app.post('/adobe/_admin/awards/%s/update' % bar_id,
params=dict(short='BAR2',
full='Updated description.'),
extra_environ=dict(username='root')).follow().follow()
assert 'BAR2' in r
assert 'Updated description.' in r
r = self.app.get('/adobe/_admin/awards/%s' %
foo_id, extra_environ=dict(username='root'))
r = self.app.get('/adobe/_admin/awards/%s/icon' %
foo_id, extra_environ=dict(username='root'))
image = PIL.Image.open(StringIO(r.body))
assert image.size == (48, 48)
self.app.post('/adobe/_admin/awards/grant',
params=dict(grant='FOO', recipient='adobe-1',
url='http://award.org', comment='Winner!'),
extra_environ=dict(username='root'))
r = self.app.get('/adobe/_admin/accolades',
extra_environ=dict(username='root'))
assert_in('Winner!', r)
assert_in('http://award.org', r)
self.app.get('/adobe/_admin/awards/%s/adobe-1' %
foo_id, extra_environ=dict(username='root'))
self.app.post('/adobe/_admin/awards/%s/adobe-1/revoke' % foo_id,
extra_environ=dict(username='root'))
self.app.post('/adobe/_admin/awards/%s/delete' % foo_id,
extra_environ=dict(username='root'))
def test_add_a_project_link(self):
from pylons import tmpl_context as c
# Install Home tool for all neighborhoods
for nb in M.Neighborhood.query.find().all():
p = nb.neighborhood_project
with h.push_config(c, user=M.User.query.get()):
p.install_app('home', 'home', 'Home', ordinal=0)
r = self.app.get('/p/')
assert 'Add a Project' in r
r = self.app.get('/u/', extra_environ=dict(username='test-user'))
assert 'Add a Project' not in r
r = self.app.get('/adobe/', extra_environ=dict(username='test-user'))
assert 'Add a Project' not in r
r = self.app.get('/u/', extra_environ=dict(username='root'))
assert 'Add a Project' in r
r = self.app.get('/adobe/', extra_environ=dict(username='root'))
assert 'Add a Project' in r
def test_help(self):
r = self.app.get('/p/_admin/help/',
extra_environ=dict(username='root'))
assert 'macro' in r
@td.with_user_project('test-user')
def test_profile_tools(self):
r = self.app.get('/u/test-user/',
extra_environ=dict(username='test-user')).follow()
assert r.html.find('div', 'profile-section tools').find(
'a', href='/u/test-user/profile/'), r.html
def test_user_project_creates_on_demand(self):
M.User.register(dict(username='donald-duck'), make_project=False)
ThreadLocalORMSession.flush_all()
self.app.get('/u/donald-duck/')
def test_disabled_user_has_no_user_project(self):
M.User.register(dict(username='donald-duck'))
self.app.get('/u/donald-duck/') # assert it's there
M.User.query.update(dict(username='donald-duck'),
{'$set': {'disabled': True}})
self.app.get('/u/donald-duck/', status=404)
def test_more_projects_link(self):
r = self.app.get('/adobe/adobe-1/admin/')
link = r.html.find(
'div', {'class': 'neighborhood_title_link'}).find('a')
assert 'View More Projects' in str(link)
assert link['href'] == '/adobe/'
def test_nav_json(self):
self.app.get('/p/_nav.json')
class TestPhoneVerificationOnProjectRegistration(TestController):
def test_phone_verification_fragment_renders(self):
self.app.get('/p/phone_verification_fragment', status=200)
self.app.get('/adobe/phone_verification_fragment', status=200)
def test_verify_phone_no_params(self):
with h.push_config(config, **{'project.verify_phone': 'true'}):
self.app.get('/p/verify_phone', status=404)
def test_verify_phone_error(self):
with h.push_config(config, **{'project.verify_phone': 'true'}):
r = self.app.get('/p/verify_phone', {'number': '1234567890'})
expected = {'status': 'error',
'error': 'Phone service is not configured'}
assert_equal(r.json, expected)
rid = r.session.get('phone_verification.request_id')
hash = r.session.get('phone_verification.number_hash')
assert_equal(rid, None)
assert_equal(hash, None)
@patch.object(g, 'phone_service', autospec=True)
def test_verify_phone(self, phone_service):
with h.push_config(config, **{'project.verify_phone': 'true'}):
phone_service.verify.return_value = {
'request_id': 'request-id', 'status': 'ok'}
r = self.app.get('/p/verify_phone', {'number': '1-555-444-3333'})
phone_service.verify.assert_called_once_with('15554443333')
assert_equal(r.json, {'status': 'ok'})
rid = r.session.get('phone_verification.request_id')
hash = r.session.get('phone_verification.number_hash')
assert_equal(rid, 'request-id')
assert_equal(hash, 'f9ac49faef45d18746ced08d001e23b179107940')
@patch.object(g, 'phone_service', autospec=True)
def test_verify_phone_escapes_error(self, phone_service):
phone_service.verify.return_value = {
'status': 'error',
'error': '<script>alert("hacked");</script>',
}
with h.push_config(config, **{'project.verify_phone': 'true'}):
r = self.app.get('/p/verify_phone', {'number': '555-444-3333'})
expected = {
'status': 'error',
'error': u'&lt;script&gt;alert(&#34;hacked&#34;);&lt;/script&gt;',
}
assert_equal(r.json, expected)
@patch.object(g, 'phone_service', autospec=True)
def test_verify_phone_already_used(self, phone_service):
with h.push_config(config, **{'project.verify_phone': 'true'}):
u = M.User.register(dict(username='existing-user'), make_project=False)
u.set_tool_data('phone_verification', number_hash=utils.phone_number_hash('1-555-444-9999'))
session(u).flush(u)
phone_service.verify.return_value = {'request_id': 'request-id', 'status': 'ok'}
r = self.app.get('/p/verify_phone', {'number': '1-555-444-9999'})
assert_equal(r.json, {
'status': 'error',
'error': u'That phone number has already been used.'
})
def test_check_phone_verification_no_params(self):
with h.push_config(config, **{'project.verify_phone': 'true'}):
self.app.get('/p/check_phone_verification', status=404)
@patch.object(g, 'phone_service', autospec=True)
def test_check_phone_verification_error(self, phone_service):
with h.push_config(config, **{'project.verify_phone': 'true'}):
phone_service.check.return_value = {'status': 'error'}
req_id = 'request-id'
# make request to verify first to initialize session
phone_service.verify.return_value = {
'request_id': req_id, 'status': 'ok'}
r = self.app.get('/p/verify_phone', {'number': '1234567890'})
r = self.app.get('/p/check_phone_verification', {'pin': '1234'})
assert_equal(r.json, {'status': 'error'})
phone_service.check.assert_called_once_with(req_id, '1234')
user = M.User.by_username('test-admin')
hash = user.get_tool_data('phone_verification', 'number_hash')
assert_equal(hash, None)
@patch.object(g, 'phone_service', autospec=True)
def test_check_phone_verification_ok(self, phone_service):
with h.push_config(config, **{'project.verify_phone': 'true'}):
phone_service.check.return_value = {'status': 'ok'}
req_id = 'request-id'
# make request to verify first to initialize session
phone_service.verify.return_value = {
'request_id': req_id, 'status': 'ok'}
r = self.app.get('/p/verify_phone', {'number': '11234567890'})
r = self.app.get('/p/check_phone_verification', {'pin': '1234'})
assert_equal(r.json, {'status': 'ok'})
phone_service.check.assert_called_once_with(req_id, '1234')
user = M.User.by_username('test-admin')
hash = user.get_tool_data('phone_verification', 'number_hash')
assert_equal(hash, '54c61c96d5d5aea5254c2d4f41508a938e5501b4')
@patch.object(g, 'phone_service', autospec=True)
def test_check_phone_verification_escapes_error(self, phone_service):
phone_service.check.return_value = {
'status': 'error',
'error': '<script>alert("hacked");</script>',
}
with h.push_config(config, **{'project.verify_phone': 'true'}):
r = self.app.get('/p/check_phone_verification', {'pin': '1234'})
expected = {
'status': 'error',
'error': u'&lt;script&gt;alert(&#34;hacked&#34;);&lt;/script&gt;',
}
assert_equal(r.json, expected)
def test_register_phone_not_verified(self):
with h.push_config(config, **{'project.verify_phone': 'true'}):
r = self.app.post(
'/p/register',
params=dict(
project_unixname='phonetest',
project_name='Phone Test',
project_description='',
neighborhood='Projects'),
extra_environ=dict(username='test-user'),
antispam=True)
overlay = r.html.find('div', {'id': 'phone_verification_overlay'})
assert_not_equal(overlay, None)
header = overlay.find('h2')
iframe = overlay.find('iframe')
assert_equal(header.getText(), 'Phone Verification Required')
assert_equal(iframe.get('src'), '/p/phone_verification_fragment')
class TestProjectImport(TestController):
def test_not_found(self):
self.app.get('/p/import_project/asdf/', status=404)
# positive tests exist within ForgeImporter package