You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2019/11/18 17:34:03 UTC

[allura] branch db/8340 updated (3879db3 -> 9824d40)

This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a change to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git.


 discard 3879db3  misc
 discard a1fc72f  fixup - tracker
 discard 636ef8e  misc - discussion
 discard 8f045bc  misc
 discard d3f789d  misc
 discard 8de16c8  [#8340] more blog tests
 discard 6205d4e  [#8340] misc other coverage
 discard cd4a53e  [#8340] tests for scripts/commands
 discard 1376d66  [#8340] some wiki increased coverage, fix bug by restoring some code erronously removed earlier
 discard c648dbb  [#8340] various admin/auth tests and dead code removal (url for editing custom groups no longer used)
 discard 44c7ce0  [#8340] tracker test coverage and remove dead code
     new e9fbadd  [#8340] tracker test coverage and remove dead code
     new fc37882  [#8340] various admin/auth tests and dead code removal (url for editing custom groups no longer used)
     new fa88085  [#8340] some wiki increased coverage, fix bug by restoring some code erronously removed earlier
     new 5c60ff6  [#8340] tests for scripts/commands
     new 8ae804a  [#8340] misc other coverage
     new e649bf9  [#8340] more blog tests
     new 2c3a18b  [#8340] more misc coverage
     new 9824d40  [#8340] discussion test coverage improvement

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (3879db3)
            \
             N -- N -- N   refs/heads/db/8340 (9824d40)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Allura/allura/tests/functional/test_auth.py | 80 ++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 31 deletions(-)


[allura] 02/08: [#8340] various admin/auth tests and dead code removal (url for editing custom groups no longer used)

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit fc378829bb0708215b694687a1a14b9be00b06dd
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Tue Nov 12 17:57:43 2019 -0500

    [#8340] various admin/auth tests and dead code removal (url for editing custom groups no longer used)
---
 Allura/allura/ext/admin/admin_main.py              |  90 ---------------
 .../allura/ext/admin/templates/project_group.html  |   4 -
 Allura/allura/ext/admin/widgets.py                 |  17 ---
 Allura/allura/tests/functional/test_admin.py       |  23 +---
 Allura/allura/tests/functional/test_auth.py        | 125 +++++++++++++++------
 Allura/allura/tests/functional/test_site_admin.py  |   9 ++
 .../allura/tests/functional/test_trovecategory.py  |  19 +++-
 7 files changed, 123 insertions(+), 164 deletions(-)

diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index d63b734..b7a9ce8 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -61,7 +61,6 @@ class W:
     label_edit = ffw.LabelEdit()
     group_card = aw.GroupCard()
     permission_card = aw.PermissionCard()
-    group_settings = aw.GroupSettings()
     new_group_settings = aw.NewGroupSettings()
     screenshot_admin = aw.ScreenshotAdmin()
     screenshot_list = ProjectScreenshots(draggable=True)
@@ -1162,57 +1161,11 @@ class GroupsController(BaseController):
         return dict()
 
     @without_trailing_slash
-    @expose()
-    @require_post()
-    @h.vardec
-    def update(self, card=None, **kw):
-        for pr in card:
-            group = M.ProjectRole.query.get(_id=ObjectId(pr['id']))
-            assert group.project == c.project, 'Security violation'
-            user_ids = pr.get('value', [])
-            new_users = pr.get('new', [])
-            if isinstance(user_ids, basestring):
-                user_ids = [user_ids]
-            if isinstance(new_users, basestring):
-                new_users = [new_users]
-            # Handle new users in groups
-            user_added = False
-            for username in new_users:
-                user = M.User.by_username(username.strip())
-                if not user:
-                    flash('User %s not found' % username, 'error')
-                    redirect('.')
-                if not user._id:
-                    continue  # never add anon users to groups
-                M.AuditLog.log('add user %s to %s', username, group.name)
-                M.ProjectRole.by_user(
-                    user, upsert=True).roles.append(group._id)
-                user_added = True
-            # Make sure we aren't removing all users from the Admin group
-            if group.name == u'Admin' and not (user_ids or user_added):
-                flash('You must have at least one user with the Admin role.',
-                      'warning')
-                redirect('.')
-            # Handle users removed from groups
-            user_ids = set(
-                uid and ObjectId(uid)
-                for uid in user_ids)
-            for role in M.ProjectRole.query.find(dict(user_id={'$ne': None}, roles=group._id)):
-                if role.user_id and role.user_id not in user_ids:
-                    role.roles = [
-                        rid for rid in role.roles if rid != group._id]
-                    M.AuditLog.log('remove user %s from %s',
-                                   role.user.username, group.name)
-        g.post_event('project_updated')
-        redirect('.')
-
-    @without_trailing_slash
     @expose('jinja:allura.ext.admin:templates/project_group.html')
     def new(self):
         c.form = W.new_group_settings
         return dict(
             group=None,
-            show_settings=True,
             action="create")
 
     @expose()
@@ -1228,49 +1181,6 @@ class GroupsController(BaseController):
         g.post_event('project_updated')
         redirect('.')
 
-    @expose()
-    def _lookup(self, name, *remainder):
-        return GroupController(name), remainder
-
-
-class GroupController(BaseController):
-    def __init__(self, name):
-        self._group = M.ProjectRole.query.get(_id=ObjectId(name))
-
-    @with_trailing_slash
-    @expose('jinja:allura.ext.admin:templates/project_group.html')
-    def index(self, **kw):
-        if self._group.name in ('Admin', 'Developer', 'Member'):
-            show_settings = False
-            action = None
-        else:
-            show_settings = True
-            action = self._group.settings_href + 'update'
-        c.form = W.group_settings
-        return dict(
-            group=self._group,
-            show_settings=show_settings,
-            action=action)
-
-    @expose()
-    @h.vardec
-    @require_post()
-    @validate(W.group_settings)
-    def update(self, _id=None, delete=None, name=None, **kw):
-        pr = M.ProjectRole.by_name(name)
-        if pr and pr._id != _id._id:
-            flash('%s already exists' % name, 'error')
-            redirect('..')
-        if delete:
-            _id.delete()
-            M.AuditLog.log('delete group %s', _id.name)
-            flash('%s deleted' % name)
-            redirect('..')
-        M.AuditLog.log('update group name %s=>%s', _id.name, name)
-        _id.name = name
-        flash('%s updated' % name)
-        redirect('..')
-
 
 class AuditController(BaseController):
     @with_trailing_slash
diff --git a/Allura/allura/ext/admin/templates/project_group.html b/Allura/allura/ext/admin/templates/project_group.html
index 927b776..a84c095 100644
--- a/Allura/allura/ext/admin/templates/project_group.html
+++ b/Allura/allura/ext/admin/templates/project_group.html
@@ -16,8 +16,4 @@
        specific language governing permissions and limitations
        under the License.
 -#}
-{% if show_settings %}
 {{c.form.display(value=group, action=action)}}
-{% else %}
-<p>No settings available for reserved groups</p>
-{% endif %}
diff --git a/Allura/allura/ext/admin/widgets.py b/Allura/allura/ext/admin/widgets.py
index 4c877e5..d27f8fa 100644
--- a/Allura/allura/ext/admin/widgets.py
+++ b/Allura/allura/ext/admin/widgets.py
@@ -114,23 +114,6 @@ class PermissionCard(CardField):
         return role._id
 
 
-class GroupSettings(ff.CsrfForm):
-    submit_text = None
-
-    @property
-    def hidden_fields(self):
-        f = super(GroupSettings, self).hidden_fields
-        f.append(ew.HiddenField(name='_id', validator=V.Ming(M.ProjectRole)))
-        return f
-
-    class fields(ew_core.NameList):
-        name = ew.InputField(label='Name')
-
-    class buttons(ew_core.NameList):
-        save = ew.SubmitButton(label='Save')
-        delete = ew.SubmitButton(label='Delete Group')
-
-
 class NewGroupSettings(ff.AdminFormResponsive):
     submit_text = 'Save'
 
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 004fe73..3b2c4df 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -662,9 +662,6 @@ class TestProjectAdmin(TestController):
         users = dev_holder.find('ul', {'class': 'users'}).findAll(
             'li', {'class': 'deleter'})
         assert 'test-user' in users[0]['data-user']
-        # Make sure we can open role page for builtin role
-        r = self.app.get('/admin/groups/' + developer_id +
-                         '/', validate_chunk=True)
 
     def test_new_admin_subscriptions(self):
         """Newly added admin must be subscribed to all the tools in the project"""
@@ -810,33 +807,21 @@ class TestProjectAdmin(TestController):
         role_holder = r.html.find('table', {'id': 'usergroup_admin'}).findAll('tr')[4]
         assert 'RoleNew1' in str(role_holder)
         role_id = role_holder['data-group']
-        r = self.app.get('/admin/groups/' + role_id + '/', validate_chunk=True)
-        r = self.app.post('/admin/groups/' + str(role_id) + '/update', params={'_id': role_id, 'name': 'Developer'})
-        assert 'error' in self.webflash(r)
-        assert 'already exists' in self.webflash(r)
-
-        with audits('update group name RoleNew1=>rleNew2'):
-            r = self.app.post('/admin/groups/' + str(role_id) + '/update',
-                              params={'_id': role_id, 'name': 'rleNew2'}).follow()
-        assert 'RoleNew1' not in r
-        assert 'rleNew2' in r
 
         # add test-user to role
         role_holder = r.html.find('table', {'id': 'usergroup_admin'}).findAll('tr')[4]
-        rleNew2_id = role_holder['data-group']
-        with audits('add user test-user to rleNew2'):
+        with audits('add user test-user to RoleNew1'):
             r = self.app.post('/admin/groups/add_user', params={
-                'role_id': rleNew2_id,
+                'role_id': role_id,
                 'username': 'test-user'})
 
-        with audits('delete group rleNew2'):
+        with audits('delete group RoleNew1'):
             r = self.app.post('/admin/groups/delete_group', params={
-                'group_name': 'rleNew2'})
+                'group_name': 'RoleNew1'})
         assert 'deleted' in self.webflash(r)
         r = self.app.get('/admin/groups/', status=200)
         roles = [str(t) for t in r.html.findAll('td', {'class': 'group'})]
         assert 'RoleNew1' not in roles
-        assert 'rleNew2' not in roles
 
         # make sure can still access homepage after one of user's roles were
         # deleted
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index a7c6893..307460f 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -197,6 +197,37 @@ class TestAuth(TestController):
 
             # changing password covered in TestPasswordExpire
 
+    def test_login_disabled(self):
+        u = M.User.query.get(username='test-user')
+        u.disabled = True
+        r = self.app.get('/auth/', extra_environ={'username': '*anonymous'})
+        f = r.forms[0]
+        encoded = self.app.antispam_field_names(f)
+        f[encoded['username']] = 'test-user'
+        f[encoded['password']] = 'foo'
+        with audits('Failed login', user=True):
+            r = f.submit(extra_environ={'username': '*anonymous'})
+
+    def test_login_pending(self):
+        u = M.User.query.get(username='test-user')
+        u.pending = True
+        r = self.app.get('/auth/', extra_environ={'username': '*anonymous'})
+        f = r.forms[0]
+        encoded = self.app.antispam_field_names(f)
+        f[encoded['username']] = 'test-user'
+        f[encoded['password']] = 'foo'
+        with audits('Failed login', user=True):
+            r = f.submit(extra_environ={'username': '*anonymous'})
+
+    def test_login_overlay(self):
+        r = self.app.get('/auth/login_fragment/', extra_environ={'username': '*anonymous'})
+        f = r.forms[0]
+        encoded = self.app.antispam_field_names(f)
+        f[encoded['username']] = 'test-user'
+        f[encoded['password']] = 'foo'
+        with audits('Successful login', user=True):
+            r = f.submit(extra_environ={'username': '*anonymous'})
+
     def test_logout(self):
         self.app.extra_environ = {'disable_auth_magic': 'True'}
         nav_pattern = ('nav', {'class': 'nav-main'})
@@ -1645,6 +1676,10 @@ To update your password on %s, please visit the following URL:
                                                  })
         assert_in('Unable to process reset, please try again', r.follow().body)
 
+    def test_hash_invalid(self):
+        r = self.app.get('/auth/forgotten_password/123412341234', status=302)
+        assert_in('Unable to process reset, please try again', r.follow().body)
+
     @patch('allura.lib.plugin.AuthenticationProvider')
     def test_provider_disabled(self, AP):
         user = M.User.query.get(username='test-admin')
@@ -1729,9 +1764,15 @@ class TestOAuth(TestController):
         r = self.app.post('/auth/oauth/register',
                           params={'application_name': 'oautstapp', 'application_description': 'Oauth rulez',
                                   '_session_id': self.app.cookies['_session_id'],
-                                  }).follow()
+                                  }, status=302)
+        r = self.app.get('/auth/oauth/')
         assert_equal(r.forms[1].action, 'generate_access_token')
-        r.forms[1].submit()
+        r = r.forms[1].submit(extra_environ={'username': 'test-user'})  # not the right user
+        assert_in("Invalid app ID", self.webflash(r))                   # gets an error
+        r = self.app.get('/auth/oauth/')                                # do it again
+        r = r.forms[1].submit()                                         # as correct user
+        assert_equal('', self.webflash(r))
+
         r = self.app.get('/auth/oauth/')
         assert 'Bearer Token:' in r
         assert_not_equal(
@@ -1744,37 +1785,55 @@ class TestOAuth(TestController):
         assert_equal(
             M.OAuthAccessToken.for_user(M.User.by_username('test-admin')), [])
 
-    @mock.patch('allura.controllers.rest.oauth.Server')
-    @mock.patch('allura.controllers.rest.oauth.Request')
-    def test_interactive(self, Request, Server):
-        M.OAuthConsumerToken.consumer = mock.Mock()
-        user = M.User.by_username('test-admin')
-        M.OAuthConsumerToken(
-            api_key='api_key',
-            user_id=user._id,
-            description='ctok_desc',
-        )
-        ThreadLocalORMSession.flush_all()
-        Request.from_request.return_value = {
-            'oauth_consumer_key': 'api_key',
-            'oauth_callback': 'http://my.domain.com/callback',
-        }
-        r = self.app.post('/rest/oauth/request_token', params={})
-        rtok = parse_qs(r.body)['oauth_token'][0]
-        r = self.app.post('/rest/oauth/authorize',
-                          params={'oauth_token': rtok})
-        r = r.forms[0].submit('yes')
-        assert r.location.startswith('http://my.domain.com/callback')
-        pin = parse_qs(urlparse(r.location).query)['oauth_verifier'][0]
-        Request.from_request.return_value = {
-            'oauth_consumer_key': 'api_key',
-            'oauth_token': rtok,
-            'oauth_verifier': pin,
-        }
-        r = self.app.get('/rest/oauth/access_token')
-        atok = parse_qs(r.body)
-        assert_equal(len(atok['oauth_token']), 1)
-        assert_equal(len(atok['oauth_token_secret']), 1)
+    def test_interactive(self):
+        with mock.patch('allura.controllers.rest.oauth.Server') as Server, \
+                mock.patch('allura.controllers.rest.oauth.Request') as Request:   # these are the oauth2 libs
+            #M.OAuthConsumerToken.consumer = mock.Mock()
+            user = M.User.by_username('test-admin')
+            M.OAuthConsumerToken(
+                api_key='api_key',
+                user_id=user._id,
+                description='ctok_desc',
+            )
+            ThreadLocalORMSession.flush_all()
+            Request.from_request.return_value = {
+                'oauth_consumer_key': 'api_key',
+                'oauth_callback': 'http://my.domain.com/callback',
+            }
+            r = self.app.post('/rest/oauth/request_token', params={})
+            rtok = parse_qs(r.body)['oauth_token'][0]
+            r = self.app.post('/rest/oauth/authorize',
+                              params={'oauth_token': rtok})
+            r = r.forms[0].submit('yes')
+            assert r.location.startswith('http://my.domain.com/callback')
+            pin = parse_qs(urlparse(r.location).query)['oauth_verifier'][0]
+            Request.from_request.return_value = {
+                'oauth_consumer_key': 'api_key',
+                'oauth_token': rtok,
+                'oauth_verifier': pin,
+            }
+            r = self.app.get('/rest/oauth/access_token')
+            atok = parse_qs(r.body)
+            assert_equal(len(atok['oauth_token']), 1)
+            assert_equal(len(atok['oauth_token_secret']), 1)
+
+        # now use the tokens & secrets to make a full OAuth request:
+        oauth_secret = atok['oauth_token_secret'][0]
+        oauth_token = atok['oauth_token'][0]
+        consumer = oauth2.Consumer('api_key', oauth_secret)
+        M.OAuthConsumerToken.consumer = consumer
+        access_token = oauth2.Token(oauth_token, oauth_secret)
+        oauth_client = oauth2.Client(consumer, access_token)
+        # use the oauth2 lib, but intercept the request and then send it to self.app.get
+        with mock.patch('oauth2.httplib2.Http.request', name='hl2req') as oa2_req:
+            oauth_client.request('http://localhost/rest/p/test/', 'GET')
+            oa2url = oa2_req.call_args[0][1]
+            oa2url = oa2url.replace('http://localhost', '')
+            print(oa2url)
+            oa2kwargs = oa2_req.call_args[1]
+        self.app.get(oa2url, headers=oa2kwargs['headers'], status=200)
+        self.app.get(oa2url.replace('oauth_signature=', 'removed='), headers=oa2kwargs['headers'], status=401)
+
 
     @mock.patch('allura.controllers.rest.oauth.Server')
     @mock.patch('allura.controllers.rest.oauth.Request')
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index d7eebc6..8b23c1e 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -137,6 +137,15 @@ class TestSiteAdmin(TestController):
             url, extra_environ=dict(username='*anonymous'), status=302)
         r = self.app.get(url)
         assert 'math.ceil' in r, r
+        assert 'ready' in r, r
+
+        # test resubmit too
+        M.MonQTask.run_ready()
+        r = self.app.get(url)
+        assert 'complete' in r, r
+        r = r.forms['resubmit-task-form'].submit()
+        r = r.follow()
+        assert 'ready' in r, r
 
     def test_task_new(self):
         r = self.app.get('/nf/admin/task_manager/new')
diff --git a/Allura/allura/tests/functional/test_trovecategory.py b/Allura/allura/tests/functional/test_trovecategory.py
index 4bb6eed..346a9f1 100644
--- a/Allura/allura/tests/functional/test_trovecategory.py
+++ b/Allura/allura/tests/functional/test_trovecategory.py
@@ -18,7 +18,7 @@ from bs4 import BeautifulSoup
 import mock
 
 from tg import config
-from nose.tools import assert_equals, assert_true
+from nose.tools import assert_equals, assert_true, assert_in
 from ming.orm import session
 
 from allura import model as M
@@ -133,3 +133,20 @@ class TestTroveCategoryController(TestController):
         </ul>
         """.strip(), 'html.parser')
         assert_equals(str(expected), str(rendered_tree))
+
+    def test_delete(self):
+        self.create_some_cats()
+        session(M.TroveCategory).flush()
+        assert_equals(5, M.TroveCategory.query.find().count())
+
+        r = self.app.get('/categories/1')
+        form = r.forms[0]
+        r = form.submit()
+        assert_in("This category contains at least one sub-category, therefore it can't be removed",
+                  self.webflash(r))
+
+        r = self.app.get('/categories/2')
+        form = r.forms[0]
+        r = form.submit()
+        assert_in("Category removed", self.webflash(r))
+        assert_equals(4, M.TroveCategory.query.find().count())
\ No newline at end of file


[allura] 08/08: [#8340] discussion test coverage improvement

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 9824d40a9043fb4c884b4ab867cc04798783bbe9
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Fri Nov 15 18:13:33 2019 -0500

    [#8340] discussion test coverage improvement
---
 Allura/allura/controllers/secure.py               | 23 -----------------------
 ForgeDiscussion/forgediscussion/tests/test_app.py | 22 ++++++++++++++++++++--
 2 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/Allura/allura/controllers/secure.py b/Allura/allura/controllers/secure.py
deleted file mode 100644
index 899ad42..0000000
--- a/Allura/allura/controllers/secure.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#       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.
-
-"""Sample controller with all its actions protected."""
-
-# This controller is only used when you activate auth. You can safely remove
-# this file from your project.
diff --git a/ForgeDiscussion/forgediscussion/tests/test_app.py b/ForgeDiscussion/forgediscussion/tests/test_app.py
index 4e23cfa..e980fc1 100644
--- a/ForgeDiscussion/forgediscussion/tests/test_app.py
+++ b/ForgeDiscussion/forgediscussion/tests/test_app.py
@@ -23,16 +23,34 @@ import tempfile
 import json
 import os
 from operator import attrgetter
+from cgi import FieldStorage
 
 from nose.tools import assert_equal
 from tg import tmpl_context as c
 from cStringIO import StringIO
+
+from forgediscussion.site_stats import posts_24hr
 from ming.orm import ThreadLocalORMSession
-from cgi import FieldStorage
 
 from allura import model as M
+from allura.tests import decorators as td
 from forgediscussion.tests.functional.test_rest import TestDiscussionApiBase
-from forgediscussion.model.forum import Forum
+from forgediscussion.model.forum import Forum, ForumPost
+
+
+class TestApp(TestDiscussionApiBase):  # creates some sample data
+
+    @td.with_discussion
+    def test_uninstall(self):
+        assert ForumPost.query.get(text='Hi boys and girls')
+        # c.app.uninstall(c.project) errors out, but works ok in test_uninstall for repo tools.  So instead:
+        c.project.uninstall_app('discussion')
+        assert not ForumPost.query.get(text='Hi boys and girls')
+
+    @td.with_discussion
+    def test_tickets_stats_24hr(self):
+        # invoked normally via entry point
+        assert_equal(2, posts_24hr())
 
 
 class TestBulkExport(TestDiscussionApiBase):


[allura] 04/08: [#8340] tests for scripts/commands

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 5c60ff656fcb013422ca02c8bcb3eda5a2a0974c
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Wed Nov 13 16:34:46 2019 -0500

    [#8340] tests for scripts/commands
---
 Allura/allura/tests/decorators.py             | 17 +++++-
 Allura/allura/tests/scripts/test_reindexes.py | 74 +++++++++++++++++++++++++++
 Allura/allura/tests/test_commands.py          | 46 +++++++++++++++--
 ForgeGit/forgegit/tests/test_tasks.py         | 17 ++----
 4 files changed, 135 insertions(+), 19 deletions(-)

diff --git a/Allura/allura/tests/decorators.py b/Allura/allura/tests/decorators.py
index 7c42206..726a402 100644
--- a/Allura/allura/tests/decorators.py
+++ b/Allura/allura/tests/decorators.py
@@ -14,7 +14,7 @@
 #       KIND, either express or implied.  See the License for the
 #       specific language governing permissions and limitations
 #       under the License.
-
+import logging
 import sys
 import re
 from functools import wraps
@@ -198,3 +198,18 @@ def out_audits(*messages, **kwargs):
     for message in messages:
         assert not M.AuditLog.query.find(dict(
             message=re.compile(preamble + message))).count(), 'Found unexpected: "%s"' % message
+
+
+# not a decorator but use it with LogCapture() decorator
+def assert_logmsg_and_no_warnings_or_errors(logs, msg):
+    """
+    :param testfixtures.logcapture.LogCapture logs: LogCapture() instance
+    :param str msg: Message to look for
+    """
+    found_msg = False
+    for r in logs.records:
+        if msg in r.getMessage():
+            found_msg = True
+        if r.levelno > logging.INFO:
+            raise AssertionError('unexpected log {} {}'.format(r.levelname, r.getMessage()))
+    assert found_msg, 'Did not find {} in logs: {}'.format(msg, '\n'.join([r.getMessage() for r in logs.records]))
diff --git a/Allura/allura/tests/scripts/test_reindexes.py b/Allura/allura/tests/scripts/test_reindexes.py
new file mode 100644
index 0000000..d6dbeb0
--- /dev/null
+++ b/Allura/allura/tests/scripts/test_reindexes.py
@@ -0,0 +1,74 @@
+#       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.
+from nose.tools import assert_in, assert_equal
+from testfixtures import LogCapture
+
+from allura.scripts.reindex_projects import ReindexProjects
+from allura.scripts.reindex_users import ReindexUsers
+from allura.tests.decorators import assert_logmsg_and_no_warnings_or_errors
+from alluratest.controller import setup_basic_test
+from allura import model as M
+
+
+class TestReindexProjects(object):
+
+    def setUp(self):
+        setup_basic_test()
+
+    def run_script(self, options):
+        cls = ReindexProjects
+        opts = cls.parser().parse_args(options)
+        cls.execute(opts)
+
+    def test(self):
+        with LogCapture() as logs:
+            self.run_script(['-n', '/p/', '-p', 'test'])
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex project test')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex done')
+
+    def test_with_tasks(self):
+        with LogCapture() as logs:
+            self.run_script(['-n', '/p/', '-p', 'test', '--tasks'])
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex project test')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex queued')
+        assert_equal(M.MonQTask.query.find({'task_name': 'allura.tasks.index_tasks.add_projects'}).count(), 1)
+
+
+class TestReindexUsers(object):
+
+    def setUp(self):
+        setup_basic_test()
+
+    def run_script(self, options):
+        cls = ReindexUsers
+        opts = cls.parser().parse_args(options)
+        cls.execute(opts)
+
+    def test(self):
+        with LogCapture() as logs:
+            self.run_script([])
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex user root')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex user test-user-1')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex done')
+
+    def test_with_tasks(self):
+        with LogCapture() as logs:
+            self.run_script(['--tasks'])
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex user root')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex user test-user-1')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Reindex queued')
+        assert_equal(M.MonQTask.query.find({'task_name': 'allura.tasks.index_tasks.add_users'}).count(), 1)
diff --git a/Allura/allura/tests/test_commands.py b/Allura/allura/tests/test_commands.py
index ee97f7e..3ad18b6 100644
--- a/Allura/allura/tests/test_commands.py
+++ b/Allura/allura/tests/test_commands.py
@@ -16,6 +16,8 @@
 #       under the License.
 
 from nose.tools import assert_raises, assert_in
+from testfixtures import OutputCapture
+
 from datadiff.tools import assert_equal
 
 from ming.base import Object
@@ -26,7 +28,7 @@ import pkg_resources
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura.command import base, script, set_neighborhood_features, \
-    create_neighborhood, show_models, taskd_cleanup
+    create_neighborhood, show_models, taskd_cleanup, taskd
 from allura import model as M
 from allura.lib.exceptions import InvalidNBFeatureValueError
 from allura.tests import decorators as td
@@ -262,6 +264,18 @@ class TestEnsureIndexCommand(object):
         ])
 
 
+class TestTaskCommand(object):
+
+    def test_commit(self):
+        exit_code = taskd.TaskCommand('task').run([test_config, 'commit'])
+        assert_equal(M.MonQTask.query.find({'task_name': 'allura.tasks.index_tasks.commit'}).count(), 1)
+        assert_equal(exit_code, 0)
+
+    def test_list(self):
+        exit_code = taskd.TaskCommand('task').run([test_config, 'list'])
+        assert_equal(exit_code, 0)
+
+
 class TestTaskdCleanupCommand(object):
 
     def setUp(self):
@@ -376,7 +390,19 @@ def test_status_log_retries():
     assert cmd._taskd_status.mock_calls == expected_calls
 
 
-class TestBackgroundCommand(object):
+class TestShowModels(object):
+
+    def test_show_models(self):
+        cmd = show_models.ShowModelsCommand('models')
+        with OutputCapture() as output:
+            cmd.run([test_config])
+        assert_in('''allura.model.notification.SiteNotification
+         - <FieldProperty content>
+         - <FieldProperty page_regex>
+        ''', output.captured)
+
+
+class TestReindexAsTask(object):
 
     cmd = 'allura.command.show_models.ReindexCommand'
     task_name = 'allura.command.base.run_command'
@@ -401,9 +427,13 @@ class TestBackgroundCommand(object):
     def test_invalid_args(self):
         M.MonQTask.query.remove()
         show_models.ReindexCommand.post('--invalid-option')
-        with td.raises(Exception) as e:
-            M.MonQTask.run_ready()
-        assert_in('Error parsing args', str(e.exc))
+        try:
+            with td.raises(Exception) as e:
+                M.MonQTask.run_ready()
+            assert_in('Error parsing args', str(e.exc))
+        finally:
+            # cleanup - remove bad MonQTask
+            M.MonQTask.query.remove()
 
 
 class TestReindexCommand(object):
@@ -418,6 +448,7 @@ class TestReindexCommand(object):
         assert not g.solr.delete.called, 'solr.delete() must not be called'
 
     @patch('pysolr.Solr')
+    @td.with_wiki  # so there's some artifacts to reindex
     def test_solr_hosts_1(self, Solr):
         cmd = show_models.ReindexCommand('reindex')
         cmd.options, args = cmd.parser.parse_args([
@@ -494,3 +525,8 @@ class TestReindexCommand(object):
         cmd.options = Mock(ming_config=None)
         with td.raises(pymongo.errors.InvalidDocument):
             cmd._post_add_artifacts(range(5))
+
+    @td.with_wiki  # so there's some artifacts to reindex
+    def test_ming_config(self):
+        cmd = show_models.ReindexCommand('reindex')
+        cmd.run([test_config, '-p', 'test', '--tasks', '--ming-config', 'test.ini'])
diff --git a/ForgeGit/forgegit/tests/test_tasks.py b/ForgeGit/forgegit/tests/test_tasks.py
index 812a8b4..4563457 100644
--- a/ForgeGit/forgegit/tests/test_tasks.py
+++ b/ForgeGit/forgegit/tests/test_tasks.py
@@ -14,7 +14,6 @@
 #       KIND, either express or implied.  See the License for the
 #       specific language governing permissions and limitations
 #       under the License.
-import logging
 import unittest
 import mock
 from testfixtures import LogCapture
@@ -27,6 +26,7 @@ from allura.scripts.refreshrepo import RefreshRepo
 from allura.scripts.refresh_last_commits import RefreshLastCommits
 from allura.lib import helpers as h
 from allura.tasks import repo_tasks
+from allura.tests.decorators import assert_logmsg_and_no_warnings_or_errors
 from allura import model as M
 from forgegit.tests import with_git
 from forgegit.tests.functional.test_controllers import _TestCase as GitRealDataBaseTestCase
@@ -73,28 +73,19 @@ class TestCoreAlluraTasks(GitRealDataBaseTestCase):
         super(TestCoreAlluraTasks, self).setUp()
         self.setup_with_tools()
 
-    def _assert_logmsg_and_no_warnings_or_errors(self, logs, msg):
-        found_msg = False
-        for r in logs.records:
-            if msg in r.getMessage():
-                found_msg = True
-            if r.levelno > logging.INFO:
-                raise AssertionError('unexpected log {} {}'.format(r.levelname, r.getMessage()))
-        assert found_msg, 'Did not find {} in logs: {}'.format(msg, '\n'.join([str(r) for r in logs.records]))
-
     def test_refreshrepo(self):
         opts = RefreshRepo.parser().parse_args(
             ['--nbhd', '/p/', '--project', 'test', '--clean', '--all', '--repo-types', 'git'])
         with LogCapture() as logs:
             RefreshRepo.execute(opts)
-        self._assert_logmsg_and_no_warnings_or_errors(logs, 'Refreshing ALL commits in ')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Refreshing ALL commits in ')
 
         # run again with some different params
         opts = RefreshRepo.parser().parse_args(
             ['--nbhd', '/p/', '--project', 'test', '--clean-after', '2010-01-01T00:00:00'])
         with LogCapture() as logs:
             RefreshRepo.execute(opts)
-        self._assert_logmsg_and_no_warnings_or_errors(logs, 'Refreshing NEW commits in ')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Refreshing NEW commits in ')
 
     def test_refresh_last_commits(self):
         repo = c.app.repo
@@ -105,7 +96,7 @@ class TestCoreAlluraTasks(GitRealDataBaseTestCase):
         with LogCapture() as logs:
             RefreshLastCommits.execute(opts)
 
-        self._assert_logmsg_and_no_warnings_or_errors(logs, 'Refreshing all last commits ')
+        assert_logmsg_and_no_warnings_or_errors(logs, 'Refreshing all last commits ')
 
         # mostly just making sure nothing errored, but here's at least one thing we can assert:
         assert repo.status == 'ready'


[allura] 07/08: [#8340] more misc coverage

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 2c3a18bd2ae07a7c1f1742385871839ad1cc0f28
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Fri Nov 15 12:42:51 2019 -0500

    [#8340] more misc coverage
---
 Allura/allura/lib/macro.py                  |  2 +-
 Allura/allura/lib/stats.py                  | 85 -----------------------------
 Allura/allura/tests/functional/test_root.py |  5 ++
 Allura/allura/tests/test_globals.py         | 11 ++++
 Allura/allura/tests/test_mail_util.py       | 16 ++++++
 Allura/allura/tests/test_utils.py           | 18 +++++-
 6 files changed, 50 insertions(+), 87 deletions(-)

diff --git a/Allura/allura/lib/macro.py b/Allura/allura/lib/macro.py
index b01eaad..4dc1f54 100644
--- a/Allura/allura/lib/macro.py
+++ b/Allura/allura/lib/macro.py
@@ -312,7 +312,7 @@ def project_screenshots():
     from allura.lib.widgets.project_list import ProjectScreenshots
     ps = ProjectScreenshots()
     g.resource_manager.register(ps)
-    response = ps.display(project=c.project)
+    response = ps.display(project=c.project, h=h)
     return response
 
 
diff --git a/Allura/allura/lib/stats.py b/Allura/allura/lib/stats.py
deleted file mode 100644
index d5244b1..0000000
--- a/Allura/allura/lib/stats.py
+++ /dev/null
@@ -1,85 +0,0 @@
-#       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.
-
-from __future__ import with_statement
-from time import time
-from contextlib import contextmanager
-from tg import request
-
-
-class StatsRecord(object):
-
-    def __init__(self, request, active):
-        self.timers = dict(
-            mongo=0,
-            template=0,
-            total=0)
-        self.url = request.environ['PATH_INFO']
-        self.active = active
-        # Avoid double-timing things
-        self._now_timing = set()
-
-    def __repr__(self):
-        stats = ' '.join(
-            ('%s=%.0fms' % (k, v * 1000))
-            for k, v in sorted(self.timers.iteritems()))
-        return '%s: %s' % (self.url, stats)
-
-    def asdict(self):
-        return dict(
-            url=self.url,
-            timers=self.timers)
-
-    @contextmanager
-    def timing(self, name):
-        if self.active and name not in self._now_timing:
-            self._now_timing.add(name)
-            self.timers.setdefault(name, 0)
-            begin = time()
-            try:
-                yield
-            finally:
-                end = time()
-                self.timers[name] += end - begin
-                self._now_timing.remove(name)
-        else:
-            yield
-
-
-class timing(object):
-
-    '''Decorator to time a method call'''
-
-    def __init__(self, timer):
-        self.timer = timer
-
-    def __call__(self, func):
-        def inner(*l, **kw):
-            try:
-                stats = request.environ['sf.stats']
-            except TypeError:
-                return func(*l, **kw)
-            with stats.timing(self.timer):
-                return func(*l, **kw)
-        inner.__name__ = func.__name__
-        return inner
-
-    def decorate(self, obj, names):
-        names = names.split()
-        for name in names:
-            setattr(obj, name,
-                    self(getattr(obj, name)))
diff --git a/Allura/allura/tests/functional/test_root.py b/Allura/allura/tests/functional/test_root.py
index ca078fb..e6a6bc7 100644
--- a/Allura/allura/tests/functional/test_root.py
+++ b/Allura/allura/tests/functional/test_root.py
@@ -199,6 +199,11 @@ class TestRootController(TestController):
                          NeighborhoodController.index.__wrapped__)
             set_transaction_name.assert_called_with('foo')
 
+    def test_error_page(self):
+        # hard to force a real error (esp. with middleware debugging being different for tests) but we can hit direct:
+        r = self.app.get('/error/document')
+        r.mustcontain("We're sorry but we weren't able to process")
+
 
 class TestRootWithSSLPattern(TestController):
     def setUp(self):
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index 7176654..f7b2be4 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -667,6 +667,17 @@ def test_project_blog_posts_macro():
         assert_in('by <em>Test Admin</em>', r)
 
 
+def test_project_screenshots_macro():
+    with h.push_context('test', neighborhood='Projects'):
+        M.ProjectFile(project_id=c.project._id, category='screenshot', caption='caption', filename='test_file.jpg')
+        ThreadLocalORMSession.flush_all()
+
+        r = g.markdown_wiki.convert('[[project_screenshots]]')
+
+        assert_in('href="/p/test/screenshot/test_file.jpg"', r)
+        assert_in('src="/p/test/screenshot/test_file.jpg/thumb"', r)
+
+
 def get_project_names(r):
     """
     Extracts a list of project names from a wiki page HTML.
diff --git a/Allura/allura/tests/test_mail_util.py b/Allura/allura/tests/test_mail_util.py
index 8568191..7b75f16 100644
--- a/Allura/allura/tests/test_mail_util.py
+++ b/Allura/allura/tests/test_mail_util.py
@@ -27,6 +27,7 @@ from ming.orm import ThreadLocalORMSession
 from tg import config as tg_config
 
 from alluratest.controller import setup_basic_test, setup_global_objects
+from allura.command.smtp_server import MailServer
 from allura.lib.utils import ConfigProxy
 from allura.app import Application
 from allura.lib.mail_util import (
@@ -323,3 +324,18 @@ def test_parse_message_id():
         'de31888f6be2d87dc377d9e713876bb514548625.patches@libjpeg-turbo.p.domain.net',
         'de31888f6be2d87dc377d9e713876bb514548625.patches@libjpeg-turbo.p.domain.net',
     ])
+
+
+class TestMailServer(object):
+
+    def setUp(self):
+        setup_basic_test()
+
+    @mock.patch('allura.command.base.log', autospec=True)
+    def test(self, log):
+        listen_port = ('0.0.0.0', 8825)
+        mailserver = MailServer(listen_port, None)
+        mailserver.process_message('127.0.0.1', 'foo@bar.com', ['1234@tickets.test.p.localhost'],
+                                   u'this is the email body with headers and everything Ο'.encode('utf-8'))
+        assert_equal([], log.exception.call_args_list)
+        log.info.assert_called_with('Msg passed along')
diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py
index 8887cef..12017e2 100644
--- a/Allura/allura/tests/test_utils.py
+++ b/Allura/allura/tests/test_utils.py
@@ -410,4 +410,20 @@ def test_is_nofollow_url():
 def test_close_ipv4_addrs():
     assert utils.close_ipv4_addrs('1.2.3.4', '1.2.3.4')
     assert utils.close_ipv4_addrs('1.2.3.4', '1.2.3.255')
-    assert not utils.close_ipv4_addrs('1.2.3.4', '1.2.4.4')
\ No newline at end of file
+    assert not utils.close_ipv4_addrs('1.2.3.4', '1.2.4.4')
+
+
+def test_lsub_utf8():
+    assert_equal(b'asdf',
+                 utils.lsub_utf8(h.really_unicode('asdf').encode('utf-8'), 40))
+    assert_equal(b'as\xf0\x9f\x98\x84\xc2\xb6\xc2\xba\xc2\xb6',
+                 utils.lsub_utf8(h.really_unicode(u'as😄¶º¶').encode('utf-8'), 40))
+    assert_equal(b'as\xf0\x9f\x98\x84',
+                 utils.lsub_utf8(h.really_unicode(u'as😄¶º¶').encode('utf-8'), 6))
+    # these would truncate the smiley:
+    assert_equal(b'as',
+                 utils.lsub_utf8(h.really_unicode(u'as😄¶º¶').encode('utf-8'), 5))
+    assert_equal(b'as',
+                 utils.lsub_utf8(h.really_unicode(u'as😄¶º¶').encode('utf-8'), 4))
+    assert_equal(b'as',
+                 utils.lsub_utf8(h.really_unicode(u'as😄¶º¶').encode('utf-8'), 3))


[allura] 06/08: [#8340] more blog tests

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit e649bf942c11b1e9096b6064ef6bacf635cc59e2
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Thu Nov 14 17:47:27 2019 -0500

    [#8340] more blog tests
---
 Allura/allura/tests/test_globals.py               | 23 +++++++++++++++++++++++
 ForgeBlog/forgeblog/tests/functional/test_root.py | 22 +++++++++++++++++++++-
 ForgeBlog/forgeblog/tests/test_app.py             | 20 +++++++++++++++++++-
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index 33453cb..7176654 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -644,6 +644,29 @@ def test_hideawards_macro():
         assert_not_in('Award short', r)
 
 
+@td.with_tool('test', 'Blog', 'blog')
+def test_project_blog_posts_macro():
+    from forgeblog import model as BM
+    with h.push_context('test', 'blog', neighborhood='Projects'):
+        BM.BlogPost.new(
+            title='Test title',
+            text='test post',
+            state='published',
+        )
+        BM.BlogPost.new(
+            title='Test title2',
+            text='test post2',
+            state='published',
+        )
+
+        r = g.markdown_wiki.convert('[[project_blog_posts]]')
+        assert_in('Test title</a></h3>', r)
+        assert_in('Test title2</a></h3>', r)
+        assert_in('<div class="markdown_content"><p>test post</p></div>', r)
+        assert_in('<div class="markdown_content"><p>test post2</p></div>', r)
+        assert_in('by <em>Test Admin</em>', r)
+
+
 def get_project_names(r):
     """
     Extracts a list of project names from a wiki page HTML.
diff --git a/ForgeBlog/forgeblog/tests/functional/test_root.py b/ForgeBlog/forgeblog/tests/functional/test_root.py
index 47b4d87..310f690 100644
--- a/ForgeBlog/forgeblog/tests/functional/test_root.py
+++ b/ForgeBlog/forgeblog/tests/functional/test_root.py
@@ -21,7 +21,7 @@ import datetime
 import json
 
 import tg
-from nose.tools import assert_equal
+from nose.tools import assert_equal, assert_in
 from mock import patch
 
 from allura.lib import helpers as h
@@ -268,3 +268,23 @@ class Test(TestController):
             wf = json.loads(self.webflash(r))
             assert_equal(wf['status'], 'error')
             assert_equal(wf['message'], 'Create/edit rate limit exceeded. Please try again later.')
+
+    def test_admin_external_feed_invalid(self):
+        r = self.app.get('/blog/')
+        r = self.app.get('/admin/blog/exfeed')
+        form = r.forms[0]
+        form['new_exfeed'].value = 'asdfasdf'
+        r = form.submit()
+        assert_in('Invalid', self.webflash(r))
+
+    def test_admin_external_feed_ok(self):
+        # sidebar menu doesn't expose link to this, unless "forgeblog.exfeed" config is true, but can use form anyway
+        r = self.app.get('/blog/')
+        r = self.app.get('/admin/blog/exfeed')
+        form = r.forms[0]
+        form['new_exfeed'].value = 'https://example.com/feed.rss'
+        r = form.submit()
+        assert_in('External feeds updated', self.webflash(r))
+
+        r = self.app.get('/admin/blog/exfeed')
+        r.mustcontain('https://example.com/feed.rss')
diff --git a/ForgeBlog/forgeblog/tests/test_app.py b/ForgeBlog/forgeblog/tests/test_app.py
index ac50f8b..24e6bfa 100644
--- a/ForgeBlog/forgeblog/tests/test_app.py
+++ b/ForgeBlog/forgeblog/tests/test_app.py
@@ -20,6 +20,7 @@
 import tempfile
 import json
 import os
+from cgi import FieldStorage
 
 from nose.tools import assert_equal
 from tg import tmpl_context as c
@@ -31,7 +32,24 @@ from allura.lib import helpers as h
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura.tests import decorators as td
 from forgeblog import model as BM
-from cgi import FieldStorage
+
+
+class TestApp(object):
+
+    def setUp(self):
+        setup_basic_test()
+
+    @td.with_tool('test', 'Blog', 'blog')
+    def test_uninstall(self):
+        BM.BlogPost.new(
+            title='Test title',
+            text='test post',
+        )
+        ThreadLocalORMSession.flush_all()
+        assert BM.BlogPost.query.get(title='Test title')
+        # c.app.uninstall(c.project) errors out, but works ok in test_uninstall for repo tools.  So instead:
+        c.project.uninstall_app('blog')
+        assert not BM.BlogPost.query.get(title='Test title')
 
 
 class TestBulkExport(object):


[allura] 01/08: [#8340] tracker test coverage and remove dead code

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit e9fbaddd73796847885ccf122b88560184e9cc0b
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Tue Nov 12 17:00:42 2019 -0500

    [#8340] tracker test coverage and remove dead code
---
 Allura/allura/lib/solr.py                          |  2 +-
 Allura/allura/model/monq_model.py                  | 12 +++--
 .../forgetracker/templates/tracker/bin.html        |  1 -
 .../forgetracker/templates/tracker/new_bin.html    | 27 -----------
 .../forgetracker/tests/functional/test_root.py     | 21 +++++++++
 ForgeTracker/forgetracker/tests/test_app.py        | 53 ++++++++++++++++++++++
 ForgeTracker/forgetracker/tracker_main.py          | 28 ++----------
 7 files changed, 86 insertions(+), 58 deletions(-)

diff --git a/Allura/allura/lib/solr.py b/Allura/allura/lib/solr.py
index 222fdef..e4adba8 100644
--- a/Allura/allura/lib/solr.py
+++ b/Allura/allura/lib/solr.py
@@ -161,7 +161,7 @@ class MockSOLR(object):
             if part in ('&&', 'AND'):
                 continue
             if part in ('||', 'OR'):
-                log.warn("MockSOLR doesn't implement OR yet; treating as AND")
+                log.warn("MockSOLR doesn't implement OR yet; treating as AND. q={} fq={}".format(q, fq))
                 continue
             if ':' in part:
                 field, value = part.split(':', 1)
diff --git a/Allura/allura/model/monq_model.py b/Allura/allura/model/monq_model.py
index b28ca12..3f48aae 100644
--- a/Allura/allura/model/monq_model.py
+++ b/Allura/allura/model/monq_model.py
@@ -98,6 +98,11 @@ class MonQTask(MappedClass):
     kwargs = FieldProperty({None: None})
     result = FieldProperty(None, if_missing=None)
 
+    sort = [
+        ('priority', ming.DESCENDING),
+        ('time_queue', ming.ASCENDING),
+    ]
+
     def __repr__(self):
         from allura import model as M
         project = M.Project.query.get(_id=self.context.project_id)
@@ -179,9 +184,6 @@ class MonQTask(MappedClass):
         and no tasks are available, return None.  If waitfunc raises a
         StopIteration, stop waiting for a task
         '''
-        sort = [
-            ('priority', ming.DESCENDING),
-            ('time_queue', ming.ASCENDING)]
         while True:
             try:
                 query = dict(state=state)
@@ -196,7 +198,7 @@ class MonQTask(MappedClass):
                             process=process)
                     },
                     new=True,
-                    sort=sort)
+                    sort=cls.sort)
                 if obj is not None:
                     return obj
             except pymongo.errors.OperationFailure, exc:
@@ -227,7 +229,7 @@ class MonQTask(MappedClass):
     def run_ready(cls, worker=None):
         '''Run all the tasks that are currently ready'''
         i = 0
-        for i, task in enumerate(cls.query.find(dict(state='ready')).all()):
+        for i, task in enumerate(cls.query.find(dict(state='ready')).sort(cls.sort).all()):
             task.process = worker
             task()
         return i
diff --git a/ForgeTracker/forgetracker/templates/tracker/bin.html b/ForgeTracker/forgetracker/templates/tracker/bin.html
index 2f33ff3..d919ee6 100644
--- a/ForgeTracker/forgetracker/templates/tracker/bin.html
+++ b/ForgeTracker/forgetracker/templates/tracker/bin.html
@@ -17,7 +17,6 @@
        under the License.
 -#}
 {% extends g.theme.master %}
-{% do g.register_forge_css('css/hilite.css') %}
 {% do g.register_app_css('css/tracker.css') %}
 
 {% block title %}{{c.project.name}} / {{app.config.options.mount_label}} / Saved Searches{% endblock %}
diff --git a/ForgeTracker/forgetracker/templates/tracker/new_bin.html b/ForgeTracker/forgetracker/templates/tracker/new_bin.html
deleted file mode 100644
index 6af78a7..0000000
--- a/ForgeTracker/forgetracker/templates/tracker/new_bin.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{#-
-       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.
--#}
-{% extends g.theme.master %}
-
-{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}} / New search for {{q}}{% endblock %}
-
-{% block header %}New search for {{q}}{% endblock %}
-
-{% block content %}
-{{c.bin_form.display(value=dict(summary=q,terms=q), action='save_bin')}}
-{% endblock %}
\ No newline at end of file
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 5eb9315..4456465 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -354,6 +354,27 @@ class TestFunctionalController(TrackerTestController):
         r = self.app.get('/bugs/milestone_counts')
         assert_equal(r.body, json.dumps(counts))
 
+    def test_bin_counts(self):
+        self.new_ticket(summary='test new')
+        self.new_ticket(summary='test new private', private=True)
+        M.MonQTask.run_ready()
+
+        r = self.app.get('/bugs/bin_counts')
+        assert_equal(r.json, {"bin_counts": [{"count": 2, "label": "Changes"},
+                                             {"count": 0, "label": "Closed Tickets"},
+                                             {"count": 2, "label": "Open Tickets"}]})
+
+        """
+        forgetracker.model.ticket.Globals.bin_count doesn't do a permission check like corresponding milestone_count
+        
+        # Private tickets shouldn't be included in counts if user doesn't
+        # have read access to private tickets.
+        r = self.app.get('/bugs/bin_counts', extra_environ=dict(username='*anonymous'))
+        assert_equal(r.json, {"bin_counts": [{"count": 1, "label": "Changes"},
+                                             {"count": 0, "label": "Closed Tickets"},
+                                             {"count": 1, "label": "Open Tickets"}]})
+        """
+
     def test_milestone_progress(self):
         self.new_ticket(summary='Ticket 1', **{'_milestone': '1.0'})
         self.new_ticket(summary='Ticket 2', **{'_milestone': '1.0',
diff --git a/ForgeTracker/forgetracker/tests/test_app.py b/ForgeTracker/forgetracker/tests/test_app.py
index cfc0596..9128d30 100644
--- a/ForgeTracker/forgetracker/tests/test_app.py
+++ b/ForgeTracker/forgetracker/tests/test_app.py
@@ -24,14 +24,67 @@ from nose.tools import assert_equal, assert_true
 from tg import tmpl_context as c
 from cgi import FieldStorage
 from cStringIO import StringIO
+
+from alluratest.controller import setup_basic_test
 from ming.orm import ThreadLocalORMSession
 
 from allura import model as M
 from allura.tests import decorators as td
 from forgetracker import model as TM
+from forgetracker.site_stats import tickets_stats_24hr
 from forgetracker.tests.functional.test_root import TrackerTestController
 
 
+class TestApp(object):
+
+    def setUp(self):
+        setup_basic_test()
+
+    @td.with_tracker
+    def test_inbound_email(self):
+        ticket = TM.Ticket.new()
+        ticket.summary = 'test ticket'
+        ticket.description = 'test description'
+
+        # send a message with no ticket matching it
+        message_id = '123@localhost'
+        message = 'test message'
+        msg = dict(payload=message, message_id=message_id, headers={'Subject': 'test'})
+        c.app.handle_message('1', msg)
+        # message gets added as a post on the ticket
+        post = M.Post.query.get(_id=message_id)
+        assert_equal(post["text"], message)
+
+    @td.with_tracker
+    def test_inbound_email_no_match(self):
+        # send a message with no ticket matching it
+        message_id = '123@localhost'
+        message = 'test message'
+        msg = dict(payload=message, message_id=message_id, headers={'Subject': 'test'})
+        # no ticket matching it
+        c.app.handle_message('6789', msg)
+        # no new message
+        post = M.Post.query.get(_id=message_id)
+        assert_equal(post, None)
+
+    @td.with_tracker
+    def test_uninstall(self):
+        t = TM.Ticket.new()
+        t.summary = 'new ticket'
+        ThreadLocalORMSession.flush_all()
+        assert TM.Ticket.query.get(summary='new ticket')
+        # c.app.uninstall(c.project) errors out, but works ok in test_uninstall for repo tools.  So instead:
+        c.project.uninstall_app('bugs')
+        assert not TM.Ticket.query.get(summary='new ticket')
+
+    @td.with_tracker
+    def test_tickets_stats_24hr(self):
+        # invoked normally via entry point
+        TM.Ticket.new()
+        TM.Ticket.new()
+        assert_equal(2, tickets_stats_24hr())
+
+
 class TestBulkExport(TrackerTestController):
 
     @td.with_tracker
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index f4861a8..f7f6c63 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -282,7 +282,9 @@ class ForgeTrackerApp(Application):
         except:
             log.exception('Error getting ticket %s', topic)
             return
-        if ticket.discussion_disabled:
+        if not ticket:
+            log.info('No such ticket num: %s', topic)
+        elif ticket.discussion_disabled:
             log.info('Discussion disabled for ticket %s', ticket.ticket_num)
         else:
             self.handle_artifact_message(ticket, message)
@@ -647,7 +649,6 @@ class RootController(BaseController, FeedController):
     def _check_security(self):
         require_access(c.app, 'read')
 
-
     @expose('json:')
     def bin_counts(self, *args, **kw):
         bin_counts = []
@@ -1150,22 +1151,10 @@ class BinController(BaseController, AdminControllerMixin):
         return dict(bins=self.app.bins, count=count, app=self.app)
 
     @with_trailing_slash
-    @expose('jinja:forgetracker:templates/tracker/bin.html')
-    def bins(self):
-        count = len(self.app.bins)
-        return dict(bins=self.app.bins, count=count, app=self.app)
-
-    @with_trailing_slash
-    @expose('jinja:forgetracker:templates/tracker/new_bin.html')
-    def newbin(self, q=None, **kw):
-        c.bin_form = W.bin_form
-        return dict(q=q or '', bin=bin or '', modelname='Bin', page='New Bin', globals=self.app.globals)
-
-    @with_trailing_slash
     @h.vardec
     @expose('jinja:forgetracker:templates/tracker/bin.html')
     @require_post()
-    @validate(W.bin_form, error_handler=newbin)
+    @validate(W.bin_form, error_handler=index)
     def save_bin(self, **bin_form):
         """Update existing search bin or create a new one.
 
@@ -1204,15 +1193,6 @@ class BinController(BaseController, AdminControllerMixin):
         self.app.globals.invalidate_bin_counts()
         redirect('.')
 
-    @with_trailing_slash
-    @expose()
-    @require_post()
-    @validate(validators=dict(bin=V.Ming(TM.Bin)))
-    def delbin(self, bin=None):
-        require(lambda: bin.app_config_id == self.app.config._id)
-        bin.delete()
-        redirect(request.referer or '/')
-
     @without_trailing_slash
     @h.vardec
     @expose('jinja:forgetracker:templates/tracker/bin.html')


[allura] 05/08: [#8340] misc other coverage

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 8ae804a67eda9129cfe05de7dae913307ff8402c
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Wed Nov 13 18:13:32 2019 -0500

    [#8340] misc other coverage
---
 Allura/allura/config/app_cfg.py              |  1 -
 Allura/allura/lib/app_globals.py             |  5 ---
 Allura/allura/lib/helpers.py                 | 60 ++--------------------------
 Allura/allura/lib/macro.py                   |  7 ++++
 Allura/allura/tests/decorators.py            |  2 +-
 Allura/allura/tests/functional/test_admin.py |  4 ++
 Allura/allura/tests/functional/test_root.py  | 24 +++++++++--
 Allura/allura/tests/test_globals.py          |  5 +++
 Allura/allura/tests/test_helpers.py          | 16 ++++----
 9 files changed, 50 insertions(+), 74 deletions(-)

diff --git a/Allura/allura/config/app_cfg.py b/Allura/allura/config/app_cfg.py
index 222fa54..f5b1464 100644
--- a/Allura/allura/config/app_cfg.py
+++ b/Allura/allura/config/app_cfg.py
@@ -103,7 +103,6 @@ class AlluraJinjaRenderer(JinjaRenderer):
             cache_size=config.get('jinja_cache_size', -1),
             extensions=['jinja2.ext.do', 'jinja2.ext.i18n'])
         jinja2_env.install_gettext_translations(tg.i18n)
-        jinja2_env.filters['filesizeformat'] = helpers.do_filesizeformat
         jinja2_env.filters['datetimeformat'] = helpers.datetimeformat
         jinja2_env.filters['filter'] = lambda s, t=None: filter(t and jinja2_env.tests[t], s)
         jinja2_env.filters['nl2br'] = helpers.nl2br_jinja_filter
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index f558b74..c748579 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -562,11 +562,6 @@ class Globals(object):
         'h.set_context() is preferred over this method'
         c.app = c.project.app_instance(name)
 
-    def postload_contents(self):
-        text = '''
-'''
-        return json.dumps(dict(text=text))
-
     def year(self):
         return datetime.datetime.utcnow().year
 
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 82285f9..69342c5 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -58,7 +58,7 @@ from tg.decorators import before_validate
 from formencode.variabledecode import variable_decode
 import formencode
 from jinja2 import Markup
-from jinja2.filters import contextfilter, escape
+from jinja2.filters import contextfilter, escape, do_filesizeformat
 from paste.deploy.converters import asbool, aslist, asint
 
 from webhelpers import date, feedgenerator, html, number, misc, text
@@ -233,14 +233,6 @@ def make_neighborhoods(ids):
     return _make_xs('Neighborhood', ids)
 
 
-def make_projects(ids):
-    return _make_xs('Project', ids)
-
-
-def make_users(ids):
-    return _make_xs('User', ids)
-
-
 def make_roles(ids):
     return _make_xs('ProjectRole', ids)
 
@@ -265,6 +257,8 @@ def make_app_admin_only(app):
 
 @contextmanager
 def push_config(obj, **kw):
+    # if you need similar for a dict, use mock.patch.dict
+
     saved_attrs = {}
     new_attrs = []
     for k, v in kw.iteritems():
@@ -745,40 +739,6 @@ def render_any_markup(name, txt, code_mode=False, linenumbers_style=TABLE):
                 txt = '<pre>%s</pre>' % txt
     return Markup(txt)
 
-# copied from jinja2 dev
-# latest release, 2.6, implements this incorrectly
-# can remove and use jinja2 implementation after upgrading to 2.7
-
-
-def do_filesizeformat(value, binary=False):
-    """Format the value like a 'human-readable' file size (i.e. 13 kB,
-4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
-Giga, etc.), if the second parameter is set to `True` the binary
-prefixes are used (Mebi, Gibi).
-"""
-    bytes = float(value)
-    base = binary and 1024 or 1000
-    prefixes = [
-        (binary and 'KiB' or 'kB'),
-        (binary and 'MiB' or 'MB'),
-        (binary and 'GiB' or 'GB'),
-        (binary and 'TiB' or 'TB'),
-        (binary and 'PiB' or 'PB'),
-        (binary and 'EiB' or 'EB'),
-        (binary and 'ZiB' or 'ZB'),
-        (binary and 'YiB' or 'YB')
-    ]
-    if bytes == 1:
-        return '1 Byte'
-    elif bytes < base:
-        return '%d Bytes' % bytes
-    else:
-        for i, prefix in enumerate(prefixes):
-            unit = base ** (i + 2)
-            if bytes < unit:
-                return '%.1f %s' % ((base * bytes / unit), prefix)
-        return '%.1f %s' % ((base * bytes / unit), prefix)
-
 
 def nl2br_jinja_filter(value):
     result = '<br>\n'.join(escape(line) for line in value.split('\n'))
@@ -1165,20 +1125,6 @@ def login_overlay(exceptions=None):
         c.show_login_overlay = True
 
 
-def get_filter(ctx, filter_name):
-    """
-    Gets a named Jinja2 filter, passing through
-    any context requested by the filter.
-    """
-    filter_ = ctx.environment.filters[filter_name]
-    if getattr(filter_, 'contextfilter', False):
-        return partial(filter_, ctx)
-    elif getattr(filter_, 'evalcontextfilter', False):
-        return partial(filter_, ctx.eval_ctx)
-    elif getattr(filter_, 'environmentfilter', False):
-        return partial(filter_, ctx.environment)
-
-
 def unidiff(old, new):
     """Returns unified diff between `one` and `two`."""
     return '\n'.join(difflib.unified_diff(
diff --git a/Allura/allura/lib/macro.py b/Allura/allura/lib/macro.py
index abb4776..b01eaad 100644
--- a/Allura/allura/lib/macro.py
+++ b/Allura/allura/lib/macro.py
@@ -20,6 +20,8 @@ import random
 import shlex
 import logging
 import traceback
+import urllib2
+
 import oembed
 import jinja2
 from operator import attrgetter
@@ -461,6 +463,11 @@ def embed(url=None):
             html = consumer.embed(url)['html']
         except oembed.OEmbedNoEndpoint:
             html = None
+        except urllib2.HTTPError as e:
+            if e.code == 404:
+                return 'Video not available'
+            else:
+                raise
 
     if html:
         # youtube has a trailing ")" at the moment
diff --git a/Allura/allura/tests/decorators.py b/Allura/allura/tests/decorators.py
index 726a402..f044c0e 100644
--- a/Allura/allura/tests/decorators.py
+++ b/Allura/allura/tests/decorators.py
@@ -200,7 +200,7 @@ def out_audits(*messages, **kwargs):
             message=re.compile(preamble + message))).count(), 'Found unexpected: "%s"' % message
 
 
-# not a decorator but use it with LogCapture() decorator
+# not a decorator but use it with LogCapture() context manager
 def assert_logmsg_and_no_warnings_or_errors(logs, msg):
     """
     :param testfixtures.logcapture.LogCapture logs: LogCapture() instance
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 3b2c4df..534863e 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -951,6 +951,10 @@ class TestProjectAdmin(TestController):
             assert url.endswith('/admin/ext/foo'), url
             assert_equals('here the foo settings go', foo_page.body)
 
+    def test_nbhd_invitations(self):
+        r = self.app.get('/admin/invitations')
+        r.mustcontain('Neighborhood Invitation(s) for test')
+
 
 class TestExport(TestController):
 
diff --git a/Allura/allura/tests/functional/test_root.py b/Allura/allura/tests/functional/test_root.py
index 34da302..ca078fb 100644
--- a/Allura/allura/tests/functional/test_root.py
+++ b/Allura/allura/tests/functional/test_root.py
@@ -28,8 +28,11 @@ functional tests exercise the whole application and its WSGI stack.
 Please read http://pythonpaste.org/webtest/ for more information.
 
 """
+import os
+from urllib import quote
+
 from tg import tmpl_context as c
-from nose.tools import assert_equal
+from nose.tools import assert_equal, assert_in
 from ming.orm.ormsession import ThreadLocalORMSession
 import mock
 from IPython.testing.decorators import module_not_available, skipif
@@ -37,7 +40,7 @@ from IPython.testing.decorators import module_not_available, skipif
 from allura.tests import decorators as td
 from allura.tests import TestController
 from allura import model as M
-from allura.lib.helpers import push_config
+from allura.lib import helpers as h
 from alluratest.controller import setup_trove_categories
 
 
@@ -69,6 +72,14 @@ class TestRootController(TestController):
         assert cat_links[0].find('a').get('href') == '/browse/clustering'
         assert cat_links[0].find('a').find('span').string == 'Clustering'
 
+    def test_validation(self):
+        # this is not configured ON currently, so adding an individual test to get coverage of the validator itself
+        with mock.patch.dict(os.environ, ALLURA_VALIDATION='all'):
+            self.app.get('/neighborhood')
+            self.app.get('/nf/markdown_to_html?markdown=aaa&project=test&app=bugs&neighborhood=%s'
+                         % M.Neighborhood.query.get(name='Projects')._id,
+                         validate_chunk=True)
+
     def test_sidebar_escaping(self):
         # use this as a convenient way to get something in the sidebar
         M.ProjectCategory(name='test-xss', label='<script>alert(1)</script>')
@@ -123,7 +134,7 @@ class TestRootController(TestController):
         # Install home app
         nb = M.Neighborhood.query.get(name='Adobe')
         p = nb.neighborhood_project
-        with push_config(c, user=M.User.query.get(username='test-admin')):
+        with h.push_config(c, user=M.User.query.get(username='test-admin')):
             p.install_app('home', 'home', 'Home', ordinal=0)
 
         response = self.app.get('/adobe/')
@@ -165,6 +176,13 @@ class TestRootController(TestController):
             '/nf/markdown_to_html?markdown=*aaa*bb[wiki:Home]&project=test&app=bugs&neighborhood=%s' % n._id, validate_chunk=True)
         assert '<p><em>aaa</em>bb<a class="alink" href="/p/test/wiki/Home/">[wiki:Home]</a></p>' in r, r
 
+        # this happens to trigger an error
+        bad_markdown = '<foo {bar}>'
+        r = self.app.get('/nf/markdown_to_html?markdown=%s&project=test&app=bugs&neighborhood=%s' %
+                         (quote(bad_markdown), n._id))
+        r.mustcontain('The markdown supplied could not be parsed correctly.')
+        r.mustcontain('<pre>&lt;foo {bar}&gt;</pre>')
+
     def test_slash_redirect(self):
         self.app.get('/p', status=301)
         self.app.get('/p/', status=302)
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index e8787fb..33453cb 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -345,6 +345,11 @@ def test_macro_embed(oembed_fetch):
               r.replace('\n', ''))
 
 
+def test_macro_embed_video_gone():
+    r = g.markdown_wiki.convert('[[embed url=https://www.youtube.com/watch?v=OWsFqPZ3v-0]]')
+    assert_equal(r, '<div class="markdown_content"><p>Video not available</p></div>')
+
+
 def test_macro_embed_notsupported():
     r = g.markdown_wiki.convert('[[embed url=http://vimeo.com/46163090]]')
     assert_equal(
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 3abde3b..8fccf90 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -107,11 +107,6 @@ def test_find_project():
     assert proj is None
 
 
-def test_make_users():
-    r = h.make_users([None]).next()
-    assert r.username == '*anonymous', r
-
-
 def test_make_roles():
     h.set_context('test', 'wiki', neighborhood='Projects')
     pr = M.ProjectRole.anonymous()
@@ -594,8 +589,11 @@ def test_base64uri_img():
 
 
 def test_base64uri_text():
-    b64txt = h.base64uri('blah blah blah 123 456 foo bar baz', mimetype='text/plain')
-    assert b64txt.startswith('data:text/plain;base64,'), b64txt
+    b64txt = h.base64uri('blah blah blah\n123 456\nfoo bar baz', mimetype='text/plain')
+    assert_equals(b64txt, 'data:text/plain;base64,YmxhaCBibGFoIGJsYWgKMTIzIDQ1Ngpmb28gYmFyIGJheg==')
+
+    b64txt = h.base64uri('blah blah blah\n123 456\nfoo bar baz', mimetype='text/plain', windows_line_endings=True)
+    assert_equals(b64txt, 'data:text/plain;base64,YmxhaCBibGFoIGJsYWgNCjEyMyA0NTYNCmZvbyBiYXIgYmF6')
 
 
 def test_slugify():
@@ -649,3 +647,7 @@ def test_hide_private_info():
 
     with h.push_config(h.tg.config, hide_private_info=False):
         assert_equals(h.hide_private_info('foo bar baz@bing.com'), 'foo bar baz@bing.com')
+
+
+def test_emojize():
+    assert_equals(h.emojize(':smile:'), u'😄')


[allura] 03/08: [#8340] some wiki increased coverage, fix bug by restoring some code erronously removed earlier

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8340
in repository https://gitbox.apache.org/repos/asf/allura.git

commit fa88085708702fd3684fe9f879c31354047dd36b
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Wed Nov 13 11:33:02 2019 -0500

    [#8340] some wiki increased coverage, fix bug by restoring some code erronously removed earlier
---
 ForgeWiki/forgewiki/tests/functional/test_rest.py |  4 +++
 ForgeWiki/forgewiki/tests/functional/test_root.py | 30 +++++++++++++++++++++++
 ForgeWiki/forgewiki/tests/test_app.py             | 10 ++++++--
 ForgeWiki/forgewiki/wiki_main.py                  | 11 +++++++++
 4 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/ForgeWiki/forgewiki/tests/functional/test_rest.py b/ForgeWiki/forgewiki/tests/functional/test_rest.py
index 09c9b16..b06aa3d 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_rest.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_rest.py
@@ -39,6 +39,10 @@ class TestWikiApi(TestRestApiBase):
     def setup_with_tools(self):
         h.set_context('test', 'wiki', neighborhood='Projects')
 
+    def test_get_root(self):
+        r = self.app.get('/rest/p/test/wiki/')
+        assert_equal(r.json, {'pages': ['Home']})
+
     def test_get_page(self):
         r = self.app.get('/p/test/wiki/Home/')
         discussion_url = r.html.find('form', id='edit_post')['action'][:-4]
diff --git a/ForgeWiki/forgewiki/tests/functional/test_root.py b/ForgeWiki/forgewiki/tests/functional/test_root.py
index d79937a..29be374 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_root.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_root.py
@@ -258,6 +258,11 @@ class TestRootController(TestController):
         assert not response.html.find('a', {'data-dialog-id': '1'})
         assert not response.html.find('a', {'data-dialog-id': '2'})
 
+        # view an older version
+        response = self.app.get('/wiki/tést/?version=1')
+        response.mustcontain('text1')
+        response.mustcontain(no='text2')
+
     def test_page_diff(self):
         self.app.post(
             '/wiki/tést/update',
@@ -652,6 +657,19 @@ class TestRootController(TestController):
         wiki_page2 = self.app.get('/wiki/tést/')
         assert not wiki_page2.html.find('div', {'class': 'editbox'})
 
+    def test_change_home_page(self):
+        self.app.post('/wiki/tést/update', params={
+            'title': 'our_néw_home',
+            'text': 'sometext',
+            'labels': '',
+            })
+        homepage_admin = self.app.get('/admin/wiki/home', validate_chunk=True)
+        assert_equal(homepage_admin.form['new_home'].value, 'Home')
+        homepage_admin.form['new_home'].value = 'our_néw_home'
+        homepage_admin.form.submit()
+        root_path = self.app.get('/wiki/', status=302)
+        assert root_path.location.endswith('/wiki/our_néw_home/'), root_path.location
+
     def test_edit_mount_label(self):
         r = self.app.get('/admin/wiki/edit_label', validate_chunk=True)
         assert r.form['mount_label'].value == 'Wiki'
@@ -773,6 +791,18 @@ class TestRootController(TestController):
         n = M.Notification.query.get(subject="[test:wiki] test-admin removed page bbb")
         assert '222' in n.text
 
+        # view deleted page
+        response = response.click('bbb')
+        assert '(deleted)' in response
+        deletedpath = response.request.path_info
+
+        # undelete it
+        undelete_url = deletedpath + 'undelete'
+        response = self.app.post(undelete_url)
+        assert_equal(response.json, {'location': './edit'})
+        response = self.app.get(deletedpath + 'edit')
+        assert 'Edit bbb' in response
+
     def test_mailto_links(self):
         self.app.get('/wiki/test_mailto/')
         params = {
diff --git a/ForgeWiki/forgewiki/tests/test_app.py b/ForgeWiki/forgewiki/tests/test_app.py
index 7934540..5abf101 100644
--- a/ForgeWiki/forgewiki/tests/test_app.py
+++ b/ForgeWiki/forgewiki/tests/test_app.py
@@ -124,7 +124,7 @@ class TestBulkExport(object):
         assert not os.path.exists(os.path.join(temp_dir, 'wiki', str(self.page._id), 'test_file'))
 
 
-class TestEmail(object):
+class TestApp(object):
 
     def setUp(self):
         setup_basic_test()
@@ -159,4 +159,10 @@ class TestEmail(object):
         msg = dict(payload=message, message_id=message_id, headers={'Subject': 'test'})
         self.wiki.handle_message('A_New_Hope', msg)
         post = M.Post.query.get(_id=message_id)
-        assert_equal(post["text"], message)
\ No newline at end of file
+        assert_equal(post["text"], message)
+
+    def test_uninstall(self):
+        assert WM.Page.query.get(title='A New Hope')
+        # c.app.uninstall(c.project) errors out, but works ok in test_uninstall for repo tools.  So instead:
+        c.project.uninstall_app('wiki')
+        assert not WM.Page.query.get(title='A New Hope')
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 50c80e2..55704e4 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -150,6 +150,17 @@ class ForgeWikiApp(Application):
             page_name = self.default_root_page_name
         return page_name
 
+    @root_page_name.setter
+    def root_page_name(self, new_root_page_name):
+        globals = WM.Globals.query.get(app_config_id=self.config._id)
+        if globals is not None:
+            globals.root = new_root_page_name
+        elif new_root_page_name != self.default_root_page_name:
+            globals = WM.Globals(
+                app_config_id=self.config._id, root=new_root_page_name)
+        if globals is not None:
+            session(globals).flush(globals)
+
     def default_root_page_text(self):
         return """Welcome to your wiki!