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

[allura] branch master updated (e0ae8ce -> 604dd9f)

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

kentontaylor pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git.


    from e0ae8ce  [#8339] make sure to preserve cookie portions for notifications, even if they don't qualify for the current pageview
     new 6dbd0c0  [#8340] remove ez_setup file
     new 423aaef  [#8340] test coverage for repo tasks/scripts
     new ba5f00c  [#8340] repo: remove dead code, add some test coverage
     new 6d277c7  [#8340] tracker test coverage and remove dead code
     new 4547f6d  [#8340] various admin/auth tests and dead code removal (url for editing custom groups no longer used)
     new 6acb9f0  [#8340] some wiki increased coverage, fix bug by restoring some code erronously removed earlier
     new d12db37  [#8340] tests for scripts/commands
     new 0418178  [#8340] misc other coverage
     new 34ac702  [#8340] more blog tests
     new 5099080  [#8340] more misc coverage
     new 604dd9f  [#8340] discussion test coverage improvement

The 11 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/config/app_cfg.py                    |   1 -
 Allura/allura/controllers/secure.py                |  23 --
 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/lib/app_globals.py                   |   5 -
 Allura/allura/lib/helpers.py                       |  60 +----
 Allura/allura/lib/macro.py                         |   9 +-
 Allura/allura/lib/solr.py                          |   2 +-
 Allura/allura/lib/stats.py                         |  85 -------
 Allura/allura/model/monq_model.py                  |  12 +-
 Allura/allura/model/repository.py                  |  85 -------
 Allura/allura/scripts/refresh_last_commits.py      |   2 +-
 Allura/allura/tests/decorators.py                  |  17 +-
 Allura/allura/tests/functional/test_admin.py       |  27 +--
 Allura/allura/tests/functional/test_auth.py        | 125 +++++++---
 Allura/allura/tests/functional/test_root.py        |  29 ++-
 Allura/allura/tests/functional/test_site_admin.py  |   9 +
 .../allura/tests/functional/test_trovecategory.py  |  19 +-
 Allura/allura/tests/model/test_repo.py             |  27 +++
 Allura/allura/tests/scripts/test_reindexes.py      |  74 ++++++
 Allura/allura/tests/test_commands.py               |  46 +++-
 Allura/allura/tests/test_globals.py                |  39 ++++
 Allura/allura/tests/test_helpers.py                |  16 +-
 Allura/allura/tests/test_mail_util.py              |  16 ++
 Allura/allura/tests/test_utils.py                  |  18 +-
 Allura/ez_setup/README.txt                         |  14 --
 Allura/ez_setup/__init__.py                        | 260 ---------------------
 Allura/setup.py                                    |   7 +-
 AlluraTest/alluratest/test_syntax.py               |   1 -
 ForgeBlog/forgeblog/tests/functional/test_root.py  |  22 +-
 ForgeBlog/forgeblog/tests/test_app.py              |  20 +-
 ForgeDiscussion/forgediscussion/tests/test_app.py  |  22 +-
 .../forgegit/tests/functional/test_controllers.py  |  27 +++
 ForgeGit/forgegit/tests/test_tasks.py              |  45 +++-
 .../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 +--
 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 +
 coverage-report-all.sh                             |  41 ----
 45 files changed, 675 insertions(+), 826 deletions(-)
 delete mode 100644 Allura/allura/controllers/secure.py
 delete mode 100644 Allura/allura/lib/stats.py
 create mode 100644 Allura/allura/tests/scripts/test_reindexes.py
 delete mode 100644 Allura/ez_setup/README.txt
 delete mode 100644 Allura/ez_setup/__init__.py
 delete mode 100644 ForgeTracker/forgetracker/templates/tracker/new_bin.html
 delete mode 100755 coverage-report-all.sh


[allura] 04/11: [#8340] tracker test coverage and remove dead code

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 6d277c756f3fa4d1ba2aa9254b057e1a94aa530f
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] 09/11: [#8340] more blog tests

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 34ac702276aa4b56f42d16425a503b54c1339726
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] 11/11: [#8340] discussion test coverage improvement

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 604dd9f253e7c4cc8db291b04df9dec09add8420
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] 10/11: [#8340] more misc coverage

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 5099080db69184f039f945b67921df3e87f2c198
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] 07/11: [#8340] tests for scripts/commands

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit d12db37aaec4144dcb41874da5b388b09f77c957
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] 02/11: [#8340] test coverage for repo tasks/scripts

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 423aaeffbb0a141060d4bcabaf84857c9fd7b373
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Tue Nov 12 13:07:09 2019 -0500

    [#8340] test coverage for repo tasks/scripts
---
 Allura/allura/scripts/refresh_last_commits.py |  2 +-
 ForgeGit/forgegit/tests/test_tasks.py         | 54 ++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/scripts/refresh_last_commits.py b/Allura/allura/scripts/refresh_last_commits.py
index 49b2772..84399d8 100644
--- a/Allura/allura/scripts/refresh_last_commits.py
+++ b/Allura/allura/scripts/refresh_last_commits.py
@@ -46,7 +46,7 @@ class RefreshLastCommits(ScriptTask):
                 repo_types.append(repo_type)
             return repo_types
         parser = argparse.ArgumentParser(description='Using existing commit data, '
-                                         'refresh the last commit metadata in MongoDB. Run for all repos (no args), '
+                                         'refresh the "last commit" metadata in MongoDB. Run for all repos (no args), '
                                          'or restrict by neighborhood, project, or code tool mount point.')
         parser.add_argument('--nbhd', action='store', default='', dest='nbhd',
                             help='Restrict update to a particular neighborhood, e.g. /p/.')
diff --git a/ForgeGit/forgegit/tests/test_tasks.py b/ForgeGit/forgegit/tests/test_tasks.py
index 26d1ee5..812a8b4 100644
--- a/ForgeGit/forgegit/tests/test_tasks.py
+++ b/ForgeGit/forgegit/tests/test_tasks.py
@@ -14,18 +14,22 @@
 #       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
 
 from ming.orm import ThreadLocalORMSession
 from tg import tmpl_context as c
 
 from alluratest.controller import setup_basic_test, setup_global_objects
+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 import model as M
 from forgegit.tests import with_git
+from forgegit.tests.functional.test_controllers import _TestCase as GitRealDataBaseTestCase
 
 
 class TestGitTasks(unittest.TestCase):
@@ -58,3 +62,51 @@ class TestGitTasks(unittest.TestCase):
             M.main_orm_session.flush()
             f.assert_called_with('test_path', None, 'test_url')
             assert ns + 1 == M.Notification.query.find().count()
+
+
+class TestCoreAlluraTasks(GitRealDataBaseTestCase):
+    """
+    Not git-specific things we are testing, but the git tool is a useful standard repo type to use for it
+    """
+
+    def setUp(self):
+        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 ')
+
+        # 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 ')
+
+    def test_refresh_last_commits(self):
+        repo = c.app.repo
+        repo.refresh()
+
+        opts = RefreshLastCommits.parser().parse_args(
+            ['--nbhd', '/p/', '--project', 'test', '--clean', '--repo-types', 'git'])
+        with LogCapture() as logs:
+            RefreshLastCommits.execute(opts)
+
+        self._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] 08/11: [#8340] misc other coverage

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 04181788fbb708169952baa805654a429355d306
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] 05/11: [#8340] various admin/auth tests and dead code removal (url for editing custom groups no longer used)

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 4547f6d956839821247071f6744454c6ee16b4ba
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] 06/11: [#8340] some wiki increased coverage, fix bug by restoring some code erronously removed earlier

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 6acb9f0a55f7f65becf63f271665d31246c1d7bf
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!
 


[allura] 03/11: [#8340] repo: remove dead code, add some test coverage

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit ba5f00c1c031aaa47fc96e89d0946329e104c729
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Tue Nov 12 13:29:45 2019 -0500

    [#8340] repo: remove dead code, add some test coverage
---
 Allura/allura/model/repository.py                  | 85 ----------------------
 Allura/allura/tests/model/test_repo.py             | 27 +++++++
 .../forgegit/tests/functional/test_controllers.py  | 27 +++++++
 3 files changed, 54 insertions(+), 85 deletions(-)

diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 98200fa..203e3ef 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -528,15 +528,6 @@ class Repository(Artifact, ActivityObject):
     def paged_diffs(self, commit_id, start=0, end=None,  onlyChangedFiles=False):
         return self._impl.paged_diffs(commit_id, start, end, onlyChangedFiles)
 
-    def _log(self, rev, skip, limit):
-        head = self.commit(rev)
-        if head is None:
-            return
-        for _id in self.commitlog([head._id], skip, limit):
-            ci = head.query.get(_id=_id)
-            ci.set_context(self)
-            yield ci
-
     def init_as_clone(self, source_path, source_name, source_url):
         self.upstream_repo.name = source_name
         self.upstream_repo.url = source_url
@@ -1158,33 +1149,6 @@ class Commit(RepoObject, ActivityObject):
     def symbolic_ids(self):
         return self.repo.symbolics_for_commit(self)
 
-    def get_parent(self, index=0):
-        '''Get the parent of this commit.
-
-        If there is no parent commit, or if an invalid index is given,
-        returns None.
-        '''
-        try:
-            cache = getattr(c, 'model_cache', '') or ModelCache()
-            ci = cache.get(Commit, dict(_id=self.parent_ids[index]))
-            if not ci:
-                return None
-            ci.set_context(self.repo)
-            return ci
-        except IndexError:
-            return None
-
-    def climb_commit_tree(self, predicate=None):
-        '''
-        Returns a generator that walks up the commit tree along
-        the first-parent ancestory, starting with this commit,
-        optionally filtering by a predicate.'''
-        ancestor = self
-        while ancestor:
-            if predicate is None or predicate(ancestor):
-                yield ancestor
-            ancestor = ancestor.get_parent()
-
     def url(self):
         if self.repo is None:
             self.repo = self.guess_repo()
@@ -1253,13 +1217,6 @@ class Commit(RepoObject, ActivityObject):
                     cur = cur[part]
         return cur
 
-    def has_path(self, path):
-        try:
-            self.get_path(path)
-            return True
-        except KeyError:
-            return False
-
     @LazyProperty
     def changed_paths(self):
         '''
@@ -1352,19 +1309,6 @@ class Tree(RepoObject):
     parent = None
     name = None
 
-    def compute_hash(self):
-        '''Compute a hash based on the contents of the tree.  Note that this
-        hash does not necessarily correspond to any actual DVCS hash.
-        '''
-        lines = (
-            ['tree' + x.name + x.id for x in self.tree_ids]
-            + ['blob' + x.name + x.id for x in self.blob_ids]
-            + [x.type + x.name + x.id for x in self.other_ids])
-        sha_obj = sha1()
-        for line in sorted(lines):
-            sha_obj.update(line)
-        return sha_obj.hexdigest()
-
     def __getitem__(self, name):
         cache = getattr(c, 'model_cache', '') or ModelCache()
         obj = self.by_name[name]
@@ -1578,11 +1522,6 @@ class Blob(object):
     def text(self):
         return self.open().read()
 
-    @classmethod
-    def diff(cls, v0, v1):
-        differ = SequenceMatcher(v0, v1)
-        return differ.get_opcodes()
-
 
 class LastCommit(RepoObject):
 
@@ -1720,8 +1659,6 @@ class ModelCache(object):
         # keyed by query, holds _id
         self._query_cache = defaultdict(OrderedDict)
         self._instance_cache = defaultdict(OrderedDict)  # keyed by _id
-        self._synthetic_ids = defaultdict(set)
-        self._synthetic_id_queries = defaultdict(set)
 
     def _normalize_query(self, query):
         _query = query
@@ -1763,9 +1700,6 @@ class ModelCache(object):
                                                              None)))
             if _id is None:
                 _id = val._model_cache_id = bson.ObjectId()
-                self._synthetic_ids[cls].add(_id)
-            if _id in self._synthetic_ids:
-                self._synthetic_id_queries[cls].add(_query)
             self._query_cache[cls][_query] = _id
             self._instance_cache[cls][_id] = val
         else:
@@ -1815,25 +1749,6 @@ class ModelCache(object):
         key, val = cache.popitem(last=False)
         return val
 
-    def expire_new_instances(self, cls):
-        '''
-        Expire any instances that were "new" or had no _id value.
-
-        If a lot of new instances of a class are being created, it's possible
-        for a query to pull a copy from mongo when a copy keyed by the synthetic
-        ID is still in the cache, potentially causing de-sync between the copies
-        leading to one with missing data overwriting the other.  Clear new
-        instances out of the cache relatively frequently (depending on the query
-        and instance cache sizes) to avoid this.
-        '''
-        for _query in self._synthetic_id_queries[cls]:
-            self._query_cache[cls].pop(_query)
-        self._synthetic_id_queries[cls] = set()
-        for _id in self._synthetic_ids[cls]:
-            instance = self._instance_cache[cls].pop(_id)
-            self._try_flush(instance, expunge=True)
-        self._synthetic_ids[cls] = set()
-
     def num_queries(self, cls=None):
         if cls is None:
             return sum([len(c) for c in self._query_cache.values()])
diff --git a/Allura/allura/tests/model/test_repo.py b/Allura/allura/tests/model/test_repo.py
index 7a4410a..5007d28 100644
--- a/Allura/allura/tests/model/test_repo.py
+++ b/Allura/allura/tests/model/test_repo.py
@@ -94,6 +94,33 @@ class RepoTestBase(unittest.TestCase):
             with mock.patch.dict(config, values, clear=True):
                 self.assertEqual(result, repo.refresh_url())
 
+    def test_clone_command_categories(self):
+        c.app = mock.Mock(**{'config._id': 'deadbeef'})
+        repo = M.repository.Repository(tool='git')
+        cmd_cats = repo.clone_command_categories(anon=False)
+        assert_equal(cmd_cats, [
+            {'key': 'file', 'name': 'File', 'title': u'Filesystem'}
+        ])
+
+        cmd_cats = repo.clone_command_categories(anon=True)
+        assert_equal(cmd_cats, [
+            {'key': 'file', 'name': 'File', 'title': u'Filesystem'}
+        ])
+
+        repo = M.repository.Repository(tool='something-else')  # no "something-else" in config so will use defaults
+        cmd_cats = repo.clone_command_categories(anon=False)
+        assert_equal(cmd_cats, [
+            {'key': 'rw', 'name': 'RW', 'title': 'Read/Write'},
+            {'key': 'ro', 'name': 'RO', 'title': 'Read Only'},
+            {'key': 'https', 'name': 'HTTPS', 'title': 'HTTPS'}
+        ])
+
+        cmd_cats = repo.clone_command_categories(anon=True)
+        assert_equal(cmd_cats, [
+            {'key': 'ro', 'name': 'RO', 'title': 'Read Only'},
+            {'key': 'https_anon', 'name': 'HTTPS', 'title': 'HTTPS'}
+        ])
+
 
 class TestLastCommit(unittest.TestCase):
     def setUp(self):
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 1f2aa0c..4b0c079 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -156,6 +156,11 @@ class TestRootController(_TestCase):
              u'parents': [u'6a45885ae7347f1cac5103b0050cc1be6a1496c8'],
              u'message': u'Add README', u'row': 2})
 
+    def test_commit_browser_basic_view(self):
+        resp = self.app.get('/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/basic')
+        resp.mustcontain('Rick')
+        resp.mustcontain('Change README')
+
     def test_log(self):
         resp = self.app.get('/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/log/')
         assert 'Initial commit' in resp
@@ -457,6 +462,15 @@ class TestRootController(_TestCase):
         r = self.app.get('/p/test/src-git/markdown_syntax_dialog')
         assert_in('<h1>Markdown Syntax Guide</h1>', r)
 
+    def test_refresh(self):
+        r = self.app.get('/p/test/src-git/refresh')
+        assert_in('refresh queued', r)
+        assert_equal(1, M.MonQTask.query.find(dict(task_name='allura.tasks.repo_tasks.refresh')).count())
+
+        r = self.app.get('/p/test/src-git/refresh', extra_environ={'HTTP_REFERER': '/p/test/src-git/'}, status=302)
+        assert_in('is being refreshed', self.webflash(r))
+        assert_equal(2, M.MonQTask.query.find(dict(task_name='allura.tasks.repo_tasks.refresh')).count())
+
 
 class TestRestController(_TestCase):
     def test_index(self):
@@ -660,6 +674,13 @@ class TestFork(_TestCase):
         r = self.app.get('/p/test/src-git/merge-requests/1/', status=200)
         assert_commit_details(r)
 
+        r = self.app.get('/p/test/src-git/merge-requests/1/can_merge_task_status')
+        assert_equal(r.json, {'status': 'ready'})
+        r = self.app.get('/p/test/src-git/merge-requests/1/can_merge_result')
+        assert_equal(r.json, {'can_merge': None})
+        r = self.app.get('/p/test/src-git/merge-requests/1/merge_task_status')
+        assert_equal(r.json, {'status': None})
+
     def test_merge_request_detail_noslash(self):
         self._request_merge()
         r = self.app.get('/p/test/src-git/merge-requests/1', status=301)
@@ -686,6 +707,12 @@ class TestFork(_TestCase):
         assert_regexp_matches(r.html.findAll('span')[-2].getText(), r'[0-9]+ seconds? ago')
         assert_regexp_matches(r.html.findAll('span')[-1].getText(), r'[0-9]+ seconds? ago')
 
+        r = self.app.get('/p/test/src-git/merge-requests/?status=rejected')
+        assert 'href="%s/"' % mr_num not in r, r
+
+        r = self.app.get('/p/test/src-git/merge-requests/?status=all')
+        assert 'href="%s/"' % mr_num in r, r
+
     def test_merge_request_update_status(self):
         r, mr_num = self._request_merge()
         r = self.app.post('/p/test/src-git/merge-requests/%s/save' % mr_num,


[allura] 01/11: [#8340] remove ez_setup file

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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 6dbd0c048d21aa360186d075796335dab75de4ef
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Mon Nov 11 16:06:30 2019 -0500

    [#8340] remove ez_setup file
---
 Allura/ez_setup/README.txt           |  14 --
 Allura/ez_setup/__init__.py          | 260 -----------------------------------
 Allura/setup.py                      |   7 +-
 AlluraTest/alluratest/test_syntax.py |   1 -
 coverage-report-all.sh               |  41 ------
 5 files changed, 1 insertion(+), 322 deletions(-)

diff --git a/Allura/ez_setup/README.txt b/Allura/ez_setup/README.txt
deleted file mode 100644
index 77c986d..0000000
--- a/Allura/ez_setup/README.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-This directory exists so that Subversion-based projects can share a single
-copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it
-automatically updated in their projects when ``setuptools`` is updated.
-
-For your convenience, you may use the following svn:externals definition::
-
-    ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
-
-You can set this by executing this command in your project directory::
-
-    svn propedit svn:externals .
-
-And then adding the line shown above to the file that comes up for editing.
-Then, whenever you update your project, ``ez_setup`` will be updated as well.
diff --git a/Allura/ez_setup/__init__.py b/Allura/ez_setup/__init__.py
deleted file mode 100644
index b8e23b3..0000000
--- a/Allura/ez_setup/__init__.py
+++ /dev/null
@@ -1,260 +0,0 @@
-#!python
-
-#       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.
-
-"""Bootstrap setuptools installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
-    from ez_setup import use_setuptools
-    use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-import sys
-DEFAULT_VERSION = "0.6c7"
-DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
-
-md5_data = {
-    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
-    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
-    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
-    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
-    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
-    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
-    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
-    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
-    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
-    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
-    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
-    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
-    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
-    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
-    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
-    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
-    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
-    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
-    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
-    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
-    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
-    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
-    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
-    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
-    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
-    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
-    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
-}
-
-import sys
-import os
-
-
-def _validate_md5(egg_name, data):
-    if egg_name in md5_data:
-        from md5 import md5
-        digest = md5(data).hexdigest()
-        if digest != md5_data[egg_name]:
-            print >>sys.stderr, (
-                "md5 validation of %s failed!  (Possible download problem?)"
-                % egg_name
-            )
-            sys.exit(2)
-    return data
-
-
-def use_setuptools(
-    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
-    download_delay=15
-):
-    """Automatically find/download setuptools and make it available on sys.path
-
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end with
-    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
-    it is not already available.  If `download_delay` is specified, it should
-    be the number of seconds that will be paused before initiating a download,
-    should one be required.  If an older version of setuptools is installed,
-    this routine will print a message to ``sys.stderr`` and raise SystemExit in
-    an attempt to abort the calling script.
-    """
-    try:
-        import setuptools
-        if setuptools.__version__ == '0.0.1':
-            print >>sys.stderr, (
-                "You have an obsolete version of setuptools installed.  Please\n"
-                "remove it from your system entirely before rerunning this script."
-            )
-            sys.exit(2)
-    except ImportError:
-        egg = download_setuptools(
-            version, download_base, to_dir, download_delay)
-        sys.path.insert(0, egg)
-        import setuptools
-        setuptools.bootstrap_install_from = egg
-
-    import pkg_resources
-    try:
-        pkg_resources.require("setuptools>=" + version)
-
-    except pkg_resources.VersionConflict, e:
-        # XXX could we install in a subprocess here?
-        print >>sys.stderr, (
-            "The required version of setuptools (>=%s) is not available, and\n"
-            "can't be installed while this script is running. Please install\n"
-            " a more recent version first.\n\n(Currently using %r)"
-        ) % (version, e.args[0])
-        sys.exit(2)
-
-
-def download_setuptools(
-    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
-    delay=15
-):
-    """Download setuptools from a specified location and return its filename
-
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end
-    with a '/'). `to_dir` is the directory where the egg will be downloaded.
-    `delay` is the number of seconds to pause before an actual download attempt.
-    """
-    import urllib2
-    import shutil
-    egg_name = "setuptools-%s-py%s.egg" % (version, sys.version[:3])
-    url = download_base + egg_name
-    saveto = os.path.join(to_dir, egg_name)
-    src = dst = None
-    if not os.path.exists(saveto):  # Avoid repeated downloads
-        try:
-            from distutils import log
-            if delay:
-                log.warn("""
----------------------------------------------------------------------------
-This script requires setuptools version %s to run (even to display
-help).  I will attempt to download it for you (from
-%s), but
-you may need to enable firewall access for this script first.
-I will start the download in %d seconds.
-
-(Note: if this machine does not have network access, please obtain the file
-
-   %s
-
-and place it in this directory before rerunning this script.)
----------------------------------------------------------------------------""",
-                         version, download_base, delay, url
-                         )
-                from time import sleep
-                sleep(delay)
-            log.warn("Downloading %s", url)
-            src = urllib2.urlopen(url)
-            # Read/write all in one block, so we don't create a corrupt file
-            # if the download is interrupted.
-            data = _validate_md5(egg_name, src.read())
-            dst = open(saveto, "wb")
-            dst.write(data)
-        finally:
-            if src:
-                src.close()
-            if dst:
-                dst.close()
-    return os.path.realpath(saveto)
-
-
-def main(argv, version=DEFAULT_VERSION):
-    """Install or upgrade setuptools and EasyInstall"""
-
-    try:
-        import setuptools
-    except ImportError:
-        egg = None
-        try:
-            egg = download_setuptools(version, delay=0)
-            sys.path.insert(0, egg)
-            from setuptools.command.easy_install import main
-            return main(list(argv) + [egg])   # we're done here
-        finally:
-            if egg and os.path.exists(egg):
-                os.unlink(egg)
-    else:
-        if setuptools.__version__ == '0.0.1':
-            # tell the user to uninstall obsolete version
-            use_setuptools(version)
-
-    req = "setuptools>=" + version
-    import pkg_resources
-    try:
-        pkg_resources.require(req)
-    except pkg_resources.VersionConflict:
-        try:
-            from setuptools.command.easy_install import main
-        except ImportError:
-            from easy_install import main
-        main(list(argv) + [download_setuptools(delay=0)])
-        sys.exit(0)  # try to force an exit
-    else:
-        if argv:
-            from setuptools.command.easy_install import main
-            main(argv)
-        else:
-            print "Setuptools version", version, "or greater has been installed."
-            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
-
-
-def update_md5(filenames):
-    """Update our built-in md5 registry"""
-
-    import re
-    from md5 import md5
-
-    for name in filenames:
-        base = os.path.basename(name)
-        f = open(name, 'rb')
-        md5_data[base] = md5(f.read()).hexdigest()
-        f.close()
-
-    data = ["    %r: %r,\n" % it for it in md5_data.items()]
-    data.sort()
-    repl = "".join(data)
-
-    import inspect
-    srcfile = inspect.getsourcefile(sys.modules[__name__])
-    f = open(srcfile, 'rb')
-    src = f.read()
-    f.close()
-
-    match = re.search("\nmd5_data = {\n([^}]+)}", src)
-    if not match:
-        print >>sys.stderr, "Internal error!"
-        sys.exit(2)
-
-    src = src[:match.start(1)] + repl + src[match.end(1):]
-    f = open(srcfile, 'w')
-    f.write(src)
-    f.close()
-
-
-if __name__ == '__main__':
-    if len(sys.argv) > 2 and sys.argv[1] == '--md5update':
-        update_md5(sys.argv[2:])
-    else:
-        main(sys.argv[1:])
diff --git a/Allura/setup.py b/Allura/setup.py
index e9d7878..6f25a17 100644
--- a/Allura/setup.py
+++ b/Allura/setup.py
@@ -17,12 +17,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-try:
-    from setuptools import setup, find_packages
-except ImportError:
-    from ez_setup import use_setuptools
-    use_setuptools()
-    from setuptools import setup, find_packages
+from setuptools import setup, find_packages
 
 exec open('allura/version.py').read()
 
diff --git a/AlluraTest/alluratest/test_syntax.py b/AlluraTest/alluratest/test_syntax.py
index a77d109..cc4646d 100644
--- a/AlluraTest/alluratest/test_syntax.py
+++ b/AlluraTest/alluratest/test_syntax.py
@@ -57,7 +57,6 @@ def test_no_prints():
         'Allura/allura/command/',
         'Allura/ldap-setup.py',
         'Allura/ldap-userconfig.py',
-        'Allura/ez_setup/',
         'Allura/allura/lib/AsciiDammit.py',
         '/scripts/',
         'ForgeSVN/setup.py',
diff --git a/coverage-report-all.sh b/coverage-report-all.sh
deleted file mode 100755
index c4e7fb6..0000000
--- a/coverage-report-all.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh
-#
-#       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.
-#
-# This script calculates global codebase coverage, based on coverage
-# of individual application packages.
-#
-
-DIRS="Allura Forge*"
-EXCLUDES='*/migrations*,*/ez_setup/*,*allura/command/*'
-
-for dir in $DIRS; do
-    if [ ! -f $dir/.coverage ]; then
-        echo "$dir/.coverage not found - please run ./run_test --with-coverage first"
-    else
-        ln -sf $dir/.coverage .coverage.$dir
-    fi
-done
-
-coverage combine
-coverage report --ignore-errors --include='Allura/*,Forge*' --omit=$EXCLUDES
-
-if [ "$1" = "--html" ]; then
-    coverage html --ignore-errors --include='Allura/*,Forge*' --omit=$EXCLUDES -d report.coverage
-    coverage html --ignore-errors --include='Allura/*' --omit=$EXCLUDES -d Allura.coverage
-fi