diff options
author | gnowgi <nagarjun@gnowledge.org> | 2012-03-15 16:19:20 +0530 |
---|---|---|
committer | gnowgi <nagarjun@gnowledge.org> | 2012-03-15 16:19:20 +0530 |
commit | 7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch) | |
tree | d2afc3463fd49625a9be482012f5c3bfcf7c42b9 /gstudio/tests | |
download | gnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz |
master trunk of gnowsys-studio
Diffstat (limited to 'gstudio/tests')
28 files changed, 4381 insertions, 0 deletions
diff --git a/gstudio/tests/__init__.py b/gstudio/tests/__init__.py new file mode 100644 index 0000000..bf03c0a --- /dev/null +++ b/gstudio/tests/__init__.py @@ -0,0 +1,121 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Unit tests for Gstudio""" +from unittest import TestSuite +from unittest import TestLoader +from django.conf import settings + +from gstudio.tests.nodetype import NodetypeTestCase # ~0.2s +from gstudio.tests.nodetype import NodetypeHtmlContentTestCase # ~0.5s +from gstudio.tests.nodetype import NodetypeGetBaseModelTestCase +from gstudio.tests.signals import SignalsTestCase +from gstudio.tests.metatype import MetatypeTestCase +from gstudio.tests.admin import NodetypeAdminTestCase +from gstudio.tests.admin import MetatypeAdminTestCase +from gstudio.tests.managers import ManagersTestCase # ~1.2s +from gstudio.tests.feeds import GstudioFeedsTestCase # ~0.4s +from gstudio.tests.views import GstudioViewsTestCase # ~1.5s ouch... +from gstudio.tests.views import GstudioCustomDetailViews # ~0.3s +from gstudio.tests.pingback import PingBackTestCase # ~0.3s +from gstudio.tests.metaweblog import MetaWeblogTestCase # ~0.6s +from gstudio.tests.comparison import ComparisonTestCase +from gstudio.tests.quick_nodetype import QuickNodetypeTestCase # ~0.4s +from gstudio.tests.sitemaps import GstudioSitemapsTestCase # ~0.3s +from gstudio.tests.ping import DirectoryPingerTestCase +from gstudio.tests.ping import ExternalUrlsPingerTestCase +from gstudio.tests.templatetags import TemplateTagsTestCase # ~0.4s +from gstudio.tests.moderator import NodetypeCommentModeratorTestCase # ~0.1s +from gstudio.tests.spam_checker import SpamCheckerTestCase +from gstudio.tests.url_shortener import URLShortenerTestCase +from gstudio.signals import disconnect_gstudio_signals +# TOTAL ~ 6.6s + + +def suite(): + """Suite of TestCases for Django""" + suite = TestSuite() + loader = TestLoader() + + test_cases = (ManagersTestCase, NodetypeTestCase, + NodetypeGetBaseModelTestCase, SignalsTestCase, + NodetypeHtmlContentTestCase, MetatypeTestCase, + GstudioViewsTestCase, GstudioFeedsTestCase, + GstudioSitemapsTestCase, ComparisonTestCase, + DirectoryPingerTestCase, ExternalUrlsPingerTestCase, + TemplateTagsTestCase, QuickNodetypeTestCase, + URLShortenerTestCase, NodetypeCommentModeratorTestCase, + GstudioCustomDetailViews, SpamCheckerTestCase, + NodetypeAdminTestCase, MetatypeAdminTestCase) + + if 'django_xmlrpc' in settings.INSTALLED_APPS: + test_cases += (PingBackTestCase, MetaWeblogTestCase) + + for test_class in test_cases: + tests = loader.loadTestsFromTestCase(test_class) + suite.addTests(tests) + + return suite + +disconnect_gstudio_signals() diff --git a/gstudio/tests/admin.py b/gstudio/tests/admin.py new file mode 100644 index 0000000..281ca3e --- /dev/null +++ b/gstudio/tests/admin.py @@ -0,0 +1,151 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + +"""Test cases for Gstudio's admin""" +from django.test import TestCase +from django.contrib.auth.models import User + +from gstudio import settings +from gstudio.models import Nodetype +from gstudio.models import Metatype + + +class NodetypeAdminTestCase(TestCase): + """Test case for Nodetype Admin""" + urls = 'gstudio.tests.urls' + + def setUp(self): + self.original_wysiwyg = settings.WYSIWYG + settings.WYSIWYG = None + User.objects.create_superuser('admin', 'admin@example.com', 'password') + metatype_1 = Metatype.objects.create(title='Metatype 1', slug='cat-1') + Metatype.objects.create(title='Metatype 2', slug='cat-2', + parent=metatype_1) + + self.client.login(username='admin', password='password') + + def tearDown(self): + settings.WYSIWYG = self.original_wysiwyg + + def test_nodetype_add_and_change(self): + """Test the insertion of a nodetype""" + self.assertEquals(Nodetype.objects.count(), 0) + post_data = {'title': u'New nodetype', + 'template': u'gstudio/nodetype_detail.html', + 'creation_date_0': u'2011-01-01', + 'creation_date_1': u'12:00:00', + 'start_publication_0': u'2011-01-01', + 'start_publication_1': u'12:00:00', + 'end_publication_0': u'2042-03-15', + 'end_publication_1': u'00:00:00', + 'status': u'2', + 'sites': u'1', + 'content': u'My content'} + + response = self.client.post('/admin/gstudio/nodetype/add/', post_data) + self.assertEquals(response.status_code, 200) + self.assertEquals(Nodetype.objects.count(), 0) + + post_data.update({'slug': u'new-nodetype'}) + response = self.client.post('/admin/gstudio/nodetype/add/', + post_data, follow=True) + self.assertEquals(response.redirect_chain, + [('http://testserver/admin/gstudio/nodetype/', 302)]) + self.assertEquals(Nodetype.objects.count(), 1) + + +class MetatypeAdminTestCase(TestCase): + """Test cases for Metatype Admin""" + urls = 'gstudio.tests.urls' + + def setUp(self): + User.objects.create_superuser('admin', 'admin@example.com', 'password') + self.client.login(username='admin', password='password') + + def test_metatype_add_and_change(self): + """Test the insertion of a Metatype, change error, and new insert""" + self.assertEquals(Metatype.objects.count(), 0) + post_data = {'title': u'New metatype', + 'slug': u'new-metatype'} + response = self.client.post('/admin/gstudio/metatype/add/', + post_data, follow=True) + self.assertEquals(response.redirect_chain, + [('http://testserver/admin/gstudio/metatype/', 302)]) + self.assertEquals(Metatype.objects.count(), 1) + + post_data.update({'parent': u'1'}) + response = self.client.post('/admin/gstudio/metatype/1/', post_data) + self.assertEquals(response.status_code, 200) + + response = self.client.post('/admin/gstudio/metatype/add/', post_data) + self.assertEquals(response.status_code, 200) + self.assertEquals(Metatype.objects.count(), 1) + + post_data.update({'slug': u'new-metatype-2'}) + response = self.client.post('/admin/gstudio/metatype/add/', + post_data, follow=True) + self.assertEquals(response.redirect_chain, + [('http://testserver/admin/gstudio/metatype/', 302)]) + self.assertEquals(Metatype.objects.count(), 2) diff --git a/gstudio/tests/comparison.py b/gstudio/tests/comparison.py new file mode 100644 index 0000000..a90959c --- /dev/null +++ b/gstudio/tests/comparison.py @@ -0,0 +1,115 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + +"""Test cases for Gstudio's comparison""" +from django.test import TestCase + +from gstudio.models import Nodetype +from gstudio.comparison import pearson_score +from gstudio.comparison import VectorBuilder +from gstudio.comparison import ClusteredModel + + +class ComparisonTestCase(TestCase): + """Test cases for comparison tools""" + + def test_pearson_score(self): + self.assertEquals(pearson_score([42], [42]), 0.0) + self.assertEquals(pearson_score([0, 1, 2], [0, 1, 2]), 0.0) + self.assertEquals(pearson_score([0, 1, 3], [0, 1, 2]), + 0.051316701949486232) + self.assertEquals(pearson_score([0, 1, 2], [0, 1, 3]), + 0.051316701949486232) + + def test_clustered_model(self): + params = {'title': 'My nodetype 1', 'content': 'My content 1', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-1'} + Nodetype.objects.create(**params) + params = {'title': 'My nodetype 2', 'content': 'My content 2', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-2'} + Nodetype.objects.create(**params) + cm = ClusteredModel(Nodetype.objects.all()) + self.assertEquals(cm.dataset().values(), ['1', '2']) + cm = ClusteredModel(Nodetype.objects.all(), + ['title', 'excerpt', 'content']) + self.assertEquals(cm.dataset().values(), ['My nodetype 1 My content 1', + 'My nodetype 2 My content 2']) + + def test_vector_builder(self): + vectors = VectorBuilder(Nodetype.objects.all(), + ['title', 'excerpt', 'content']) + params = {'title': 'My nodetype 1', 'content': + 'This is my first content', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-1'} + Nodetype.objects.create(**params) + params = {'title': 'My nodetype 2', 'content': + 'My second nodetype', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-2'} + Nodetype.objects.create(**params) + columns, dataset = vectors() + self.assertEquals(columns, ['content', 'This', 'my', 'is', '1', + 'second', '2', 'first']) + self.assertEquals(dataset.values(), [[1, 1, 1, 1, 1, 0, 0, 1], + [0, 0, 0, 0, 0, 1, 1, 0]]) diff --git a/gstudio/tests/custom_spam_checker.py b/gstudio/tests/custom_spam_checker.py new file mode 100644 index 0000000..989aea2 --- /dev/null +++ b/gstudio/tests/custom_spam_checker.py @@ -0,0 +1,75 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + +"""Custom spam checker backend for testing Gstudio""" +from django.core.exceptions import ImproperlyConfigured + + +raise ImproperlyConfigured('This backend only exists for testing') + + +def backend(nodetype): + """Custom spam checker backend for testing Gstudio""" + return False diff --git a/gstudio/tests/custom_url_shortener.py b/gstudio/tests/custom_url_shortener.py new file mode 100644 index 0000000..d5fce4d --- /dev/null +++ b/gstudio/tests/custom_url_shortener.py @@ -0,0 +1,75 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + +"""Custom url shortener backend for testing Gstudio""" +from django.core.exceptions import ImproperlyConfigured + + +raise ImproperlyConfigured('This backend only exists for testing') + + +def backend(nodetype): + """Custom url shortener backend for testing Gstudio""" + return '' diff --git a/gstudio/tests/custom_views_detail_urls.py b/gstudio/tests/custom_views_detail_urls.py new file mode 100644 index 0000000..50d1634 --- /dev/null +++ b/gstudio/tests/custom_views_detail_urls.py @@ -0,0 +1,108 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test urls for the gstudio project""" +from functools import wraps + +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns + +from gstudio.views.tags import tag_detail +from gstudio.views.authors import author_detail +from gstudio.views.metatypes import metatype_detail +from gstudio.tests.urls import urlpatterns as test_urlpatterns + + +def call_with_template_and_extra_context( + view, template_name='gstudio/nodetype_list.html', + extra_context={'extra': 'context'}): + + @wraps(view) + def wrapper(*args, **kwargs): + return view(template_name=template_name, + extra_context=extra_context, + *args, **kwargs) + + return wrapper + +custom_tag_detail = call_with_template_and_extra_context(tag_detail) +custom_author_detail = call_with_template_and_extra_context(author_detail) +custom_metatype_detail = call_with_template_and_extra_context(metatype_detail) + + +urlpatterns = patterns( + '', + url(r'^authors/(?P<username>[.+-@\w]+)/$', + custom_author_detail, name='gstudio_author_detail'), + url(r'^authors/(?P<username>[.+-@\w]+)/page/(?P<page>\d+)/$', + custom_author_detail, name='gstudio_author_detail_paginated'), + url(r'^metatypes/(?P<path>[-\/\w]+)/page/(?P<page>\d+)/$', + custom_metatype_detail, name='gstudio_metatype_detail_paginated'), + url(r'^metatypes/(?P<path>[-\/\w]+)/$', + custom_metatype_detail, name='gstudio_metatype_detail'), + url(r'^tags/(?P<tag>[- \w]+)/$', + custom_tag_detail, name='gstudio_tag_detail'), + url(r'^tags/(?P<tag>[- \w]+)/page/(?P<page>\d+)/$', + custom_tag_detail, name='gstudio_tag_detail_paginated'), + ) + test_urlpatterns diff --git a/gstudio/tests/feeds.py b/gstudio/tests/feeds.py new file mode 100644 index 0000000..da22995 --- /dev/null +++ b/gstudio/tests/feeds.py @@ -0,0 +1,360 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's feeds""" +from datetime import datetime + +from django.test import TestCase +from django.conf import settings +from django.contrib import comments +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.utils.translation import ugettext as _ +from django.utils.feedgenerator import Atom1Feed +from django.utils.feedgenerator import DefaultFeed +from django.core.exceptions import ObjectDoesNotExist +from django.contrib.contenttypes.models import ContentType + +from tagging.models import Tag + +from gstudio.models import Nodetype +from gstudio.models import Metatype +from gstudio.managers import PUBLISHED +from gstudio import feeds +from gstudio.feeds import NodetypeFeed +from gstudio.feeds import LatestNodetypes +from gstudio.feeds import MetatypeNodetypes +from gstudio.feeds import AuthorNodetypes +from gstudio.feeds import TagNodetypes +from gstudio.feeds import SearchNodetypes +from gstudio.feeds import NodetypeDiscussions +from gstudio.feeds import NodetypeComments +from gstudio.feeds import NodetypePingbacks +from gstudio.feeds import NodetypeTrackbacks + + +class GstudioFeedsTestCase(TestCase): + """Test cases for the Feed classes provided""" + urls = 'gstudio.tests.urls' + + def setUp(self): + self.site = Site.objects.get_current() + self.author = User.objects.create(username='admin', + email='admin@example.com') + self.metatype = Metatype.objects.create(title='Tests', slug='tests') + self.nodetype_ct_id = ContentType.objects.get_for_model(Nodetype).pk + + def create_published_nodetype(self): + params = {'title': 'My test nodetype', + 'content': 'My test content with image ' + '<img src="/image.jpg" />', + 'slug': 'my-test-nodetype', + 'tags': 'tests', + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + nodetype = Nodetype.objects.create(**params) + nodetype.sites.add(self.site) + nodetype.metatypes.add(self.metatype) + nodetype.authors.add(self.author) + return nodetype + + def create_discussions(self, nodetype): + comment = comments.get_model().objects.create(comment='My Comment', + user=self.author, + content_object=nodetype, + site=self.site) + pingback = comments.get_model().objects.create(comment='My Pingback', + user=self.author, + content_object=nodetype, + site=self.site) + pingback.flags.create(user=self.author, flag='pingback') + trackback = comments.get_model().objects.create(comment='My Trackback', + user=self.author, + content_object=nodetype, + site=self.site) + trackback.flags.create(user=self.author, flag='trackback') + return [comment, pingback, trackback] + + def test_nodetype_feed(self): + original_feeds_format = feeds.FEEDS_FORMAT + feeds.FEEDS_FORMAT = '' + nodetype = self.create_published_nodetype() + feed = NodetypeFeed() + self.assertEquals(feed.item_pubdate(nodetype), nodetype.creation_date) + self.assertEquals(feed.item_metatypes(nodetype), [self.metatype.title]) + self.assertEquals(feed.item_author_name(nodetype), self.author.username) + self.assertEquals(feed.item_author_email(nodetype), self.author.email) + self.assertEquals( + feed.item_author_link(nodetype), + 'http://example.com/authors/%s/' % self.author.username) + # Test a NoReverseMatch for item_author_link + self.author.username = '[]' + self.author.save() + feed.item_author_name(nodetype) + self.assertEquals(feed.item_author_link(nodetype), 'http://example.com') + feeds.FEEDS_FORMAT = original_feeds_format + + def test_nodetype_feed_enclosure(self): + original_feeds_format = feeds.FEEDS_FORMAT + feeds.FEEDS_FORMAT = '' + nodetype = self.create_published_nodetype() + feed = NodetypeFeed() + self.assertEquals( + feed.item_enclosure_url(nodetype), 'http://example.com/image.jpg') + nodetype.content = 'My test content with image <img src="image.jpg" />', + nodetype.save() + self.assertEquals( + feed.item_enclosure_url(nodetype), 'http://example.com/image.jpg') + nodetype.content = 'My test content with image ' \ + '<img src="http://test.com/image.jpg" />' + nodetype.save() + self.assertEquals( + feed.item_enclosure_url(nodetype), 'http://test.com/image.jpg') + nodetype.image = 'image_field.jpg' + nodetype.save() + self.assertEquals(feed.item_enclosure_url(nodetype), + '%simage_field.jpg' % settings.MEDIA_URL) + self.assertEquals(feed.item_enclosure_length(nodetype), '100000') + self.assertEquals(feed.item_enclosure_mime_type(nodetype), 'image/jpeg') + feeds.FEEDS_FORMAT = original_feeds_format + + def test_latest_nodetypes(self): + self.create_published_nodetype() + feed = LatestNodetypes() + self.assertEquals(feed.link(), '/') + self.assertEquals(len(feed.items()), 1) + self.assertEquals(feed.title(), + 'example.com - %s' % _('Latest nodetypes')) + self.assertEquals( + feed.description(), + _('The latest nodetypes for the site %s') % 'example.com') + + def test_metatype_nodetypes(self): + self.create_published_nodetype() + feed = MetatypeNodetypes() + self.assertEquals(feed.get_object('request', '/tests/'), self.metatype) + self.assertEquals(len(feed.items(self.metatype)), 1) + self.assertEquals(feed.link(self.metatype), '/metatypes/tests/') + self.assertEquals( + feed.title(self.metatype), + _('Nodetypes for the metatype %s') % self.metatype.title) + self.assertEquals( + feed.description(self.metatype), + _('The latest nodetypes for the metatype %s') % self.metatype.title) + + def test_author_nodetypes(self): + self.create_published_nodetype() + feed = AuthorNodetypes() + self.assertEquals(feed.get_object('request', 'admin'), self.author) + self.assertEquals(len(feed.items(self.author)), 1) + self.assertEquals(feed.link(self.author), '/authors/admin/') + self.assertEquals(feed.title(self.author), + _('Nodetypes for author %s') % self.author.username) + self.assertEquals(feed.description(self.author), + _('The latest nodetypes by %s') % self.author.username) + + def test_tag_nodetypes(self): + self.create_published_nodetype() + feed = TagNodetypes() + tag = Tag(name='tests') + self.assertEquals(feed.get_object('request', 'tests').name, 'tests') + self.assertEquals(len(feed.items('tests')), 1) + self.assertEquals(feed.link(tag), '/tags/tests/') + self.assertEquals(feed.title(tag), + _('Nodetypes for the tag %s') % tag.name) + self.assertEquals(feed.description(tag), + _('The latest nodetypes for the tag %s') % tag.name) + + def test_search_nodetypes(self): + class FakeRequest: + def __init__(self, val): + self.GET = {'pattern': val} + self.create_published_nodetype() + feed = SearchNodetypes() + self.assertRaises(ObjectDoesNotExist, + feed.get_object, FakeRequest('te')) + self.assertEquals(feed.get_object(FakeRequest('test')), 'test') + self.assertEquals(len(feed.items('test')), 1) + self.assertEquals(feed.link('test'), '/search/?pattern=test') + self.assertEquals(feed.title('test'), + _("Results of the search for '%s'") % 'test') + self.assertEquals( + feed.description('test'), + _("The nodetypes containing the pattern '%s'") % 'test') + + def test_nodetype_discussions(self): + nodetype = self.create_published_nodetype() + comments = self.create_discussions(nodetype) + feed = NodetypeDiscussions() + self.assertEquals(feed.get_object( + 'request', 2010, 1, 1, nodetype.slug), nodetype) + self.assertEquals(feed.link(nodetype), '/2010/01/01/my-test-nodetype/') + self.assertEquals(len(feed.items(nodetype)), 3) + self.assertEquals(feed.item_pubdate(comments[0]), + comments[0].submit_date) + self.assertEquals(feed.item_link(comments[0]), + '/comments/cr/%i/1/#c1' % self.nodetype_ct_id) + self.assertEquals(feed.item_author_name(comments[0]), 'admin') + self.assertEquals(feed.item_author_email(comments[0]), + 'admin@example.com') + self.assertEquals(feed.item_author_link(comments[0]), '') + self.assertEquals(feed.title(nodetype), + _('Discussions on %s') % nodetype.title) + self.assertEquals( + feed.description(nodetype), + _('The latest discussions for the nodetype %s') % nodetype.title) + + def test_nodetype_comments(self): + nodetype = self.create_published_nodetype() + comments = self.create_discussions(nodetype) + feed = NodetypeComments() + self.assertEquals(list(feed.items(nodetype)), [comments[0]]) + self.assertEquals(feed.item_link(comments[0]), + '/comments/cr/%i/1/#comment_1' % self.nodetype_ct_id) + self.assertEquals(feed.title(nodetype), + _('Comments on %s') % nodetype.title) + self.assertEquals( + feed.description(nodetype), + _('The latest comments for the nodetype %s') % nodetype.title) + self.assertEquals( + feed.item_enclosure_url(comments[0]), + 'http://www.gravatar.com/avatar/e64c7d89f26b' + 'd1972efa854d13d7dd61.jpg?s=80&r=g') + self.assertEquals(feed.item_enclosure_length(nodetype), '100000') + self.assertEquals(feed.item_enclosure_mime_type(nodetype), 'image/jpeg') + + def test_nodetype_pingbacks(self): + nodetype = self.create_published_nodetype() + comments = self.create_discussions(nodetype) + feed = NodetypePingbacks() + self.assertEquals(list(feed.items(nodetype)), [comments[1]]) + self.assertEquals(feed.item_link(comments[1]), + '/comments/cr/%i/1/#pingback_2' % self.nodetype_ct_id) + self.assertEquals(feed.title(nodetype), + _('Pingbacks on %s') % nodetype.title) + self.assertEquals( + feed.description(nodetype), + _('The latest pingbacks for the nodetype %s') % nodetype.title) + + def test_nodetype_trackbacks(self): + nodetype = self.create_published_nodetype() + comments = self.create_discussions(nodetype) + feed = NodetypeTrackbacks() + self.assertEquals(list(feed.items(nodetype)), [comments[2]]) + self.assertEquals(feed.item_link(comments[2]), + '/comments/cr/%i/1/#trackback_3' % self.nodetype_ct_id) + self.assertEquals(feed.title(nodetype), + _('Trackbacks on %s') % nodetype.title) + self.assertEquals( + feed.description(nodetype), + _('The latest trackbacks for the nodetype %s') % nodetype.title) + + def test_nodetype_feed_no_authors(self): + original_feeds_format = feeds.FEEDS_FORMAT + feeds.FEEDS_FORMAT = '' + nodetype = self.create_published_nodetype() + nodetype.authors.clear() + feed = NodetypeFeed() + self.assertEquals(feed.item_author_name(nodetype), None) + feeds.FEEDS_FORMAT = original_feeds_format + + def test_nodetype_feed_rss_or_atom(self): + original_feeds_format = feeds.FEEDS_FORMAT + feeds.FEEDS_FORMAT = '' + feed = LatestNodetypes() + self.assertEquals(feed.feed_type, DefaultFeed) + feeds.FEEDS_FORMAT = 'atom' + feed = LatestNodetypes() + self.assertEquals(feed.feed_type, Atom1Feed) + self.assertEquals(feed.subtitle, feed.description) + feeds.FEEDS_FORMAT = original_feeds_format + + def test_discussion_feed_with_same_slugs(self): + """ + https://github.com/gnowgi/django-gstudio/issues/104 + + OK, Here I will reproduce the original case: getting a discussion + type feed, with a same slug. + + The correction of this case, will need some changes in the + get_object method. + """ + nodetype = self.create_published_nodetype() + + feed = NodetypeDiscussions() + self.assertEquals(feed.get_object( + 'request', 2010, 1, 1, nodetype.slug), nodetype) + + params = {'title': 'My test nodetype, part II', + 'content': 'My content ', + 'slug': 'my-test-nodetype', + 'tags': 'tests', + 'creation_date': datetime(2010, 2, 1), + 'status': PUBLISHED} + nodetype_same_slug = Nodetype.objects.create(**params) + nodetype_same_slug.sites.add(self.site) + nodetype_same_slug.authors.add(self.author) + + self.assertEquals(feed.get_object( + 'request', 2010, 2, 1, nodetype_same_slug.slug), nodetype_same_slug) diff --git a/gstudio/tests/managers.py b/gstudio/tests/managers.py new file mode 100644 index 0000000..dd57933 --- /dev/null +++ b/gstudio/tests/managers.py @@ -0,0 +1,313 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's managers""" +from datetime import datetime + +from django.test import TestCase +from django.contrib.auth.models import User +from django.contrib.sites.models import Site + +from tagging.models import Tag + +from gstudio.models import Nodetype +from gstudio.models import Author +from gstudio.models import Metatype +from gstudio.managers import PUBLISHED +from gstudio.managers import tags_published +from gstudio.managers import nodetypes_published + + +class ManagersTestCase(TestCase): + + def setUp(self): + self.sites = [ + Site.objects.get_current(), + Site.objects.create(domain='http://domain.com', + name='Domain.com')] + self.authors = [ + User.objects.create_user(username='webmaster', + email='webmaster@example.com'), + User.objects.create_user(username='contributor', + email='contributor@example.com')] + self.metatypes = [ + Metatype.objects.create(title='Metatype 1', + slug='metatype-1'), + Metatype.objects.create(title='Metatype 2', + slug='metatype-2')] + + params = {'title': 'My nodetype 1', 'content': 'My content 1', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-1', + 'status': PUBLISHED} + self.nodetype_1 = Nodetype.objects.create(**params) + self.nodetype_1.authors.add(self.authors[0]) + self.nodetype_1.metatypes.add(*self.metatypes) + self.nodetype_1.sites.add(*self.sites) + + params = {'title': 'My nodetype 2', 'content': 'My content 2', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-2'} + self.nodetype_2 = Nodetype.objects.create(**params) + self.nodetype_2.authors.add(*self.authors) + self.nodetype_2.metatypes.add(self.metatypes[0]) + self.nodetype_2.sites.add(self.sites[0]) + + def test_tags_published(self): + self.assertEquals(tags_published().count(), Tag.objects.count()) + Tag.objects.create(name='out') + self.assertNotEquals(tags_published().count(), Tag.objects.count()) + + def test_author_published_manager_get_query_set(self): + self.assertEquals(Author.published.count(), 1) + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + self.assertEquals(Author.published.count(), 2) + self.nodetype_2.sites.remove(self.sites[0]) + self.nodetype_2.sites.add(self.sites[1]) + self.assertEquals(Author.published.count(), 1) + + def test_nodetypes_published(self): + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 1) + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 2) + self.nodetype_1.sites.clear() + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 1) + self.nodetype_1.sites.add(*self.sites) + self.nodetype_1.start_publication = datetime(2020, 1, 1) + self.nodetype_1.save() + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 1) + self.nodetype_1.start_publication = datetime(2000, 1, 1) + self.nodetype_1.save() + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 2) + self.nodetype_1.end_publication = datetime(2000, 1, 1) + self.nodetype_1.save() + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 1) + self.nodetype_1.end_publication = datetime(2020, 1, 1) + self.nodetype_1.save() + self.assertEquals(nodetypes_published(Nodetype.objects.all()).count(), 2) + + def test_nodetype_published_manager_get_query_set(self): + self.assertEquals(Nodetype.published.count(), 1) + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + self.assertEquals(Nodetype.published.count(), 2) + self.nodetype_1.sites.clear() + self.assertEquals(Nodetype.published.count(), 1) + self.nodetype_1.sites.add(*self.sites) + self.nodetype_1.start_publication = datetime(2020, 1, 1) + self.nodetype_1.save() + self.assertEquals(Nodetype.published.count(), 1) + self.nodetype_1.start_publication = datetime(2000, 1, 1) + self.nodetype_1.save() + self.assertEquals(Nodetype.published.count(), 2) + self.nodetype_1.end_publication = datetime(2000, 1, 1) + self.nodetype_1.save() + self.assertEquals(Nodetype.published.count(), 1) + self.nodetype_1.end_publication = datetime(2020, 1, 1) + self.nodetype_1.save() + self.assertEquals(Nodetype.published.count(), 2) + + def test_nodetype_published_manager_on_site(self): + self.assertEquals(Nodetype.published.on_site().count(), 2) + self.nodetype_2.sites.clear() + self.nodetype_2.sites.add(self.sites[1]) + self.assertEquals(Nodetype.published.on_site().count(), 1) + self.nodetype_1.sites.clear() + self.assertEquals(Nodetype.published.on_site().count(), 0) + + def test_nodetype_published_manager_basic_search(self): + self.assertEquals(Nodetype.published.basic_search('My ').count(), 1) + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + self.assertEquals(Nodetype.published.basic_search('My ').count(), 2) + self.assertEquals(Nodetype.published.basic_search('1').count(), 1) + self.assertEquals(Nodetype.published.basic_search('content 1').count(), 2) + + def test_nodetype_published_manager_advanced_search(self): + metatype = Metatype.objects.create( + title='SimpleMetatype', slug='simple') + self.nodetype_2.metatypes.add(metatype) + self.nodetype_2.tags = self.nodetype_2.tags + ', custom' + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + self.assertEquals( + Nodetype.published.advanced_search('content').count(), 2) + search = Nodetype.published.advanced_search('content 1') + self.assertEquals(search.count(), 1) + self.assertEquals(search.all()[0], self.nodetype_1) + self.assertEquals( + Nodetype.published.advanced_search('content 1 or 2').count(), 2) + self.assertEquals( + Nodetype.published.advanced_search('content 1 and 2').count(), 0) + self.assertEquals( + Nodetype.published.advanced_search('content 1 2').count(), 0) + self.assertEquals( + Nodetype.published.advanced_search('"My content" 1 or 2').count(), 2) + self.assertEquals( + Nodetype.published.advanced_search('-"My content" 2').count(), 0) + search = Nodetype.published.advanced_search('content -1') + self.assertEquals(search.count(), 1) + self.assertEquals(search.all()[0], self.nodetype_2) + self.assertEquals(Nodetype.published.advanced_search( + 'content metatype:SimpleMetatype').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'content metatype:simple').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'content metatype:"Metatype 1"').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'content metatype:"metatype-1"').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'content metatype:"metatype-2"').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'content tag:gstudio').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'content tag:custom').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'content author:webmaster').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'content author:contributor').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'content author:webmaster tag:gstudio').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'content author:webmaster tag:custom').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'content 1 or 2 author:webmaster').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'content 1 or 2 author:webmaster').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + '(author:webmaster content) my').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + '(author:webmaster) or (author:contributor)').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + '(author:webmaster) (author:contributor)').count(), 0) + self.assertEquals(Nodetype.published.advanced_search( + '(author:webmaster content) 1').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + '(author:webmaster content) or 2').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + '(author:contributor content) or 1').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + '(author:contributor content) or 2').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + '(author:webmaster or ("hello world")) and 2').count(), 1) + + # Complex queries + self.assertEquals(Nodetype.published.advanced_search( + '(author:admin and "content 1") or author:webmaster').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'author:admin and ("content 1" or author:webmaster)').count(), 0) + self.assertEquals(Nodetype.published.advanced_search( + 'author:admin and "content 1" or author:webmaster').count(), 0) + self.assertEquals(Nodetype.published.advanced_search( + '-(author:webmaster and "content 1")').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + '-(-author:webmaster and "content 1")').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'metatype:"metatype -1" or author:"web master"').count(), 0) + self.assertEquals(Nodetype.published.advanced_search( + 'metatype:"metatype-1" or author:"webmaster"').count(), 2) + + # Wildcards + self.assertEquals(Nodetype.published.advanced_search( + 'author:webm*').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'author:*bmas*').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'author:*master').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'author:*master metatype:*ory-2').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'author:*master or metatype:cate*').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'metatype:*ate*').count(), 2) + self.assertEquals(Nodetype.published.advanced_search( + 'author:"webmast*"').count(), 0) + self.assertEquals(Nodetype.published.advanced_search( + 'tag:"gstudio*"').count(), 0) + self.assertEquals(Nodetype.published.advanced_search( + 'tag:*inni*').count(), 2) + + def test_nodetype_published_manager_advanced_search_with_punctuation(self): + self.nodetype_2.content = 'How are you today ? Fine thank you ! OK.' + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + self.assertEquals(Nodetype.published.advanced_search( + 'today ?').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + 'today or ! or .').count(), 1) + self.assertEquals(Nodetype.published.advanced_search( + '"you today ?"').count(), 1) + + def test_nodetype_published_manager_search(self): + self.nodetype_2.content = self.nodetype_2.content + ' * ' + self.nodetype_2.status = PUBLISHED + self.nodetype_2.save() + # Be sure that basic_search does not return + # the same results of advanced_search + self.assertNotEquals( + Nodetype.published.basic_search('content 1').count(), + Nodetype.published.advanced_search('content 1').count()) + # Now check the fallback with the '*' pattern + # which will fails advanced search + self.assertEquals(Nodetype.published.search('*').count(), 1) diff --git a/gstudio/tests/metatype.py b/gstudio/tests/metatype.py new file mode 100644 index 0000000..b20a467 --- /dev/null +++ b/gstudio/tests/metatype.py @@ -0,0 +1,116 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's Metatype""" +from django.test import TestCase +from django.contrib.sites.models import Site + +from gstudio.models import Nodetype +from gstudio.models import Metatype +from gstudio.managers import PUBLISHED + + +class MetatypeTestCase(TestCase): + + def setUp(self): + self.site = Site.objects.get_current() + self.metatypes = [Metatype.objects.create(title='Metatype 1', + slug='metatype-1'), + Metatype.objects.create(title='Metatype 2', + slug='metatype-2')] + params = {'title': 'My nodetype', + 'content': 'My content', + 'tags': 'gstudio, test', + 'slug': 'my-nodetype'} + + self.nodetype = Nodetype.objects.create(**params) + self.nodetype.metatypes.add(*self.metatypes) + self.nodetype.sites.add(self.site) + + def test_nodetypes_published(self): + metatype = self.metatypes[0] + self.assertEqual(metatype.nodetypes_published().count(), 0) + self.nodetype.status = PUBLISHED + self.nodetype.save() + self.assertEqual(metatype.nodetypes_published().count(), 1) + + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'tags': 'gstudio, test', + 'status': PUBLISHED, + 'slug': 'my-second-nodetype'} + + new_nodetype = Nodetype.objects.create(**params) + new_nodetype.sites.add(self.site) + new_nodetype.metatypes.add(self.metatypes[0]) + + self.assertEqual(self.metatypes[0].nodetypes_published().count(), 2) + self.assertEqual(self.metatypes[1].nodetypes_published().count(), 1) + + def test_nodetypes_tree_path(self): + self.assertEqual(self.metatypes[0].tree_path, 'metatype-1') + self.assertEqual(self.metatypes[1].tree_path, 'metatype-2') + self.metatypes[1].parent = self.metatypes[0] + self.metatypes[1].save() + self.assertEqual(self.metatypes[1].tree_path, 'metatype-1/metatype-2') diff --git a/gstudio/tests/metaweblog.py b/gstudio/tests/metaweblog.py new file mode 100644 index 0000000..f90ddec --- /dev/null +++ b/gstudio/tests/metaweblog.py @@ -0,0 +1,360 @@ + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +s# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +"""Test cases for Gstudio's MetaWeblog API""" +from xmlrpclib import Binary +from xmlrpclib import Fault +from xmlrpclib import ServerProxy +from datetime import datetime +from tempfile import TemporaryFile + +from django.test import TestCase +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core.files.storage import default_storage + +from gstudio.models import Nodetype +from gstudio.models import Metatype +from gstudio.managers import DRAFT +from gstudio.managers import PUBLISHED +from gstudio.settings import UPLOAD_TO +from gstudio.xmlrpc.metaweblog import authenticate +from gstudio.xmlrpc.metaweblog import post_structure +from gstudio.tests.utils import TestTransport + + +class MetaWeblogTestCase(TestCase): + """Test cases for MetaWeblog""" + urls = 'gstudio.tests.urls' + + def setUp(self): + # Create data + self.webmaster = User.objects.create_superuser( + username='webmaster', + email='webmaster@example.com', + password='password') + self.contributor = User.objects.create_user( + username='contributor', + email='contributor@example.com', + password='password') + self.site = Site.objects.get_current() + self.metatypes = [ + Metatype.objects.create(title='Metatype 1', + slug='metatype-1'), + Metatype.objects.create(title='Metatype 2', + slug='metatype-2')] + params = {'title': 'My nodetype 1', 'content': 'My content 1', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-1', + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + self.nodetype_1 = Nodetype.objects.create(**params) + self.nodetype_1.authors.add(self.webmaster) + self.nodetype_1.metatypes.add(*self.metatypes) + self.nodetype_1.sites.add(self.site) + + params = {'title': 'My nodetype 2', 'content': 'My content 2', + 'creation_date': datetime(2010, 3, 15), + 'tags': 'gstudio, test', 'slug': 'my-nodetype-2'} + self.nodetype_2 = Nodetype.objects.create(**params) + self.nodetype_2.authors.add(self.webmaster) + self.nodetype_2.metatypes.add(self.metatypes[0]) + self.nodetype_2.sites.add(self.site) + # Instanciating the server proxy + self.server = ServerProxy('http://localhost:8000/xmlrpc/', + transport=TestTransport()) + + def test_authenticate(self): + self.assertRaises(Fault, authenticate, 'badcontributor', 'badpassword') + self.assertRaises(Fault, authenticate, 'contributor', 'badpassword') + self.assertRaises(Fault, authenticate, 'contributor', 'password') + self.contributor.is_staff = True + self.contributor.save() + self.assertEquals(authenticate('contributor', 'password'), + self.contributor) + self.assertRaises(Fault, authenticate, 'contributor', + 'password', 'gstudio.change_nodetype') + self.assertEquals(authenticate('webmaster', 'password'), + self.webmaster) + self.assertEquals(authenticate('webmaster', 'password', + 'gstudio.change_nodetype'), + self.webmaster) + + def test_get_users_blogs(self): + self.assertRaises(Fault, self.server.blogger.getUsersBlogs, + 'apikey', 'contributor', 'password') + self.assertEquals(self.server.blogger.getUsersBlogs( + 'apikey', 'webmaster', 'password'), + [{'url': 'http://example.com/', + 'blogid': 1, + 'blogName': 'example.com'}]) + + def test_get_user_info(self): + self.assertRaises(Fault, self.server.blogger.getUserInfo, + 'apikey', 'contributor', 'password') + self.webmaster.first_name = 'John' + self.webmaster.last_name = 'Doe' + self.webmaster.save() + self.assertEquals(self.server.blogger.getUserInfo( + 'apikey', 'webmaster', 'password'), + {'firstname': 'John', 'lastname': 'Doe', + 'url': 'http://example.com/authors/webmaster/', + 'userid': self.webmaster.pk, + 'nickname': 'webmaster', + 'email': 'webmaster@example.com'}) + + def test_get_authors(self): + self.assertRaises(Fault, self.server.wp.getAuthors, + 'apikey', 'contributor', 'password') + self.assertEquals(self.server.wp.getAuthors( + 'apikey', 'webmaster', 'password'), [ + {'user_login': 'webmaster', + 'user_id': self.webmaster.pk, + 'user_email': 'webmaster@example.com', + 'display_name': 'webmaster'}]) + + def test_get_metatypes(self): + self.assertRaises(Fault, self.server.metaWeblog.getMetatypes, + 1, 'contributor', 'password') + self.assertEquals( + self.server.metaWeblog.getMetatypes('apikey', + 'webmaster', 'password'), + [{'rssUrl': 'http://example.com/feeds/metatypes/metatype-1/', + 'description': 'Metatype 1', + 'htmlUrl': 'http://example.com/metatypes/metatype-1/', + 'metatypeId': 1, 'parentId': 0, + 'metatypeName': 'Metatype 1', + 'metatypeDescription': ''}, + {'rssUrl': 'http://example.com/feeds/metatypes/metatype-2/', + 'description': 'Metatype 2', + 'htmlUrl': 'http://example.com/metatypes/metatype-2/', + 'metatypeId': 2, 'parentId': 0, + 'metatypeName': 'Metatype 2', + 'metatypeDescription': ''}]) + self.metatypes[1].parent = self.metatypes[0] + self.metatypes[1].description = 'metatype 2 description' + self.metatypes[1].save() + self.assertEquals( + self.server.metaWeblog.getMetatypes('apikey', + 'webmaster', 'password'), + [{'rssUrl': 'http://example.com/feeds/metatypes/metatype-1/', + 'description': 'Metatype 1', + 'htmlUrl': 'http://example.com/metatypes/metatype-1/', + 'metatypeId': 1, 'parentId': 0, + 'metatypeName': 'Metatype 1', + 'metatypeDescription': ''}, + {'rssUrl': + 'http://example.com/feeds/metatypes/metatype-1/metatype-2/', + 'description': 'Metatype 2', + 'htmlUrl': + 'http://example.com/metatypes/metatype-1/metatype-2/', + 'metatypeId': 2, 'parentId': 1, + 'metatypeName': 'Metatype 2', + 'metatypeDescription': 'metatype 2 description'}]) + + def test_new_metatype(self): + metatype_struct = {'name': 'Metatype 3', 'slug': 'metatype-3', + 'description': 'Metatype 3 description', + 'parent_id': self.metatypes[0].pk} + self.assertRaises(Fault, self.server.wp.newMetatype, + 1, 'contributor', 'password', metatype_struct) + self.assertEquals(Metatype.objects.count(), 2) + new_metatype_id = self.server.wp.newMetatype( + 1, 'webmaster', 'password', metatype_struct) + self.assertEquals(Metatype.objects.count(), 3) + metatype = Metatype.objects.get(pk=new_metatype_id) + self.assertEquals(metatype.title, 'Metatype 3') + self.assertEquals(metatype.description, 'Metatype 3 description') + self.assertEquals(metatype.slug, 'metatype-3') + self.assertEquals(metatype.parent.pk, 1) + + def test_get_recent_posts(self): + self.assertRaises(Fault, self.server.metaWeblog.getRecentPosts, + 1, 'contributor', 'password', 10) + self.assertEquals(len(self.server.metaWeblog.getRecentPosts( + 1, 'webmaster', 'password', 10)), 2) + + def test_delete_post(self): + self.assertRaises(Fault, self.server.blogger.deletePost, + 'apikey', 1, 'contributor', 'password', 'publish') + self.assertEquals(Nodetype.objects.count(), 2) + self.assertTrue( + self.server.blogger.deletePost( + 'apikey', self.nodetype_1.pk, 'webmaster', 'password', 'publish')) + self.assertEquals(Nodetype.objects.count(), 1) + + def test_get_post(self): + self.assertRaises(Fault, self.server.metaWeblog.getPost, + 1, 'contributor', 'password') + post = self.server.metaWeblog.getPost( + self.nodetype_1.pk, 'webmaster', 'password') + self.assertEquals(post['title'], self.nodetype_1.title) + self.assertEquals(post['description'], '<p>My content 1</p>') + self.assertEquals(post['metatypes'], ['Metatype 1', 'Metatype 2']) + self.assertEquals(post['dateCreated'].value, '2010-01-01T00:00:00') + self.assertEquals(post['link'], + 'http://example.com/2010/01/01/my-nodetype-1/') + self.assertEquals(post['permaLink'], + 'http://example.com/2010/01/01/my-nodetype-1/') + self.assertEquals(post['postid'], self.nodetype_1.pk) + self.assertEquals(post['userid'], 'webmaster') + self.assertEquals(post['mt_excerpt'], '') + self.assertEquals(post['mt_allow_comments'], 1) + self.assertEquals(post['mt_allow_pings'], 1) + self.assertEquals(post['mt_keywords'], self.nodetype_1.tags) + self.assertEquals(post['wp_author'], 'webmaster') + self.assertEquals(post['wp_author_id'], self.webmaster.pk) + self.assertEquals(post['wp_author_display_name'], 'webmaster') + self.assertEquals(post['wp_password'], '') + self.assertEquals(post['wp_slug'], self.nodetype_1.slug) + + def test_new_post(self): + post = post_structure(self.nodetype_2, self.site) + self.assertRaises(Fault, self.server.metaWeblog.newPost, + 1, 'contributor', 'password', post, 1) + self.assertEquals(Nodetype.objects.count(), 2) + self.assertEquals(Nodetype.published.count(), 1) + self.server.metaWeblog.newPost( + 1, 'webmaster', 'password', post, 1) + self.assertEquals(Nodetype.objects.count(), 3) + self.assertEquals(Nodetype.published.count(), 2) + del post['dateCreated'] + post['wp_author_id'] = self.contributor.pk + self.server.metaWeblog.newPost( + 1, 'webmaster', 'password', post, 0) + self.assertEquals(Nodetype.objects.count(), 4) + self.assertEquals(Nodetype.published.count(), 2) + + def test_edit_post(self): + post = post_structure(self.nodetype_2, self.site) + self.assertRaises(Fault, self.server.metaWeblog.editPost, + 1, 'contributor', 'password', post, 1) + new_post_id = self.server.metaWeblog.newPost( + 1, 'webmaster', 'password', post, 0) + + nodetype = Nodetype.objects.get(pk=new_post_id) + self.assertEquals(nodetype.title, self.nodetype_2.title) + self.assertEquals(nodetype.content, self.nodetype_2.html_content) + self.assertEquals(nodetype.excerpt, self.nodetype_2.excerpt) + self.assertEquals(nodetype.slug, self.nodetype_2.slug) + self.assertEquals(nodetype.status, DRAFT) + self.assertEquals(nodetype.password, self.nodetype_2.password) + self.assertEquals(nodetype.comment_enabled, True) + self.assertEquals(nodetype.pingback_enabled, True) + self.assertEquals(nodetype.metatypes.count(), 1) + self.assertEquals(nodetype.authors.count(), 1) + self.assertEquals(nodetype.authors.all()[0], self.webmaster) + self.assertEquals(nodetype.creation_date, self.nodetype_2.creation_date) + + nodetype.title = 'Title edited' + nodetype.creation_date = datetime(2000, 1, 1) + post = post_structure(nodetype, self.site) + post['metatypes'] = '' + post['description'] = 'Content edited' + post['mt_excerpt'] = 'Content edited' + post['wp_slug'] = 'slug-edited' + post['wp_password'] = 'password' + post['mt_allow_comments'] = 2 + post['mt_allow_pings'] = 0 + + response = self.server.metaWeblog.editPost( + new_post_id, 'webmaster', 'password', post, 1) + self.assertEquals(response, True) + nodetype = Nodetype.objects.get(pk=new_post_id) + self.assertEquals(nodetype.title, post['title']) + self.assertEquals(nodetype.content, post['description']) + self.assertEquals(nodetype.excerpt, post['mt_excerpt']) + self.assertEquals(nodetype.slug, 'slug-edited') + self.assertEquals(nodetype.status, PUBLISHED) + self.assertEquals(nodetype.password, 'password') + self.assertEquals(nodetype.comment_enabled, False) + self.assertEquals(nodetype.pingback_enabled, False) + self.assertEquals(nodetype.metatypes.count(), 0) + self.assertEquals(nodetype.creation_date, datetime(2000, 1, 1)) + + del post['dateCreated'] + post['wp_author_id'] = self.contributor.pk + + response = self.server.metaWeblog.editPost( + new_post_id, 'webmaster', 'password', post, 1) + nodetype = Nodetype.objects.get(pk=new_post_id) + self.assertEquals(nodetype.authors.count(), 1) + self.assertEquals(nodetype.authors.all()[0], self.contributor) + self.assertEquals(nodetype.creation_date, datetime(2000, 1, 1)) + + def test_new_media_object(self): + file_ = TemporaryFile() + file_.write('My test content') + file_.seek(0) + media = {'name': 'gstudio_test_file.txt', + 'type': 'text/plain', + 'bits': Binary(file_.read())} + file_.close() + + self.assertRaises(Fault, self.server.metaWeblog.newMediaObject, + 1, 'contributor', 'password', media) + new_media = self.server.metaWeblog.newMediaObject( + 1, 'webmaster', 'password', media) + self.assertTrue('/gstudio_test_file' in new_media['url']) + default_storage.delete('/'.join([ + UPLOAD_TO, new_media['url'].split('/')[-1]])) diff --git a/gstudio/tests/moderator.py b/gstudio/tests/moderator.py new file mode 100644 index 0000000..58332b1 --- /dev/null +++ b/gstudio/tests/moderator.py @@ -0,0 +1,169 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +"""Test cases for Gstudio's moderator""" +from django.core import mail +from django.test import TestCase +from django.contrib import comments +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.contrib.contenttypes.models import ContentType + +from gstudio.models import Nodetype +from gstudio.managers import PUBLISHED +from gstudio.moderator import NodetypeCommentModerator + + +class NodetypeCommentModeratorTestCase(TestCase): + """Test cases for the moderator""" + + def setUp(self): + self.site = Site.objects.get_current() + self.author = User.objects.create(username='admin', + email='admin@example.com') + self.nodetype_ct_id = ContentType.objects.get_for_model(Nodetype).pk + + params = {'title': 'My test nodetype', + 'content': 'My test nodetype', + 'slug': 'my-test-nodetype', + 'status': PUBLISHED} + self.nodetype = Nodetype.objects.create(**params) + self.nodetype.sites.add(self.site) + self.nodetype.authors.add(self.author) + + def test_email(self): + comment = comments.get_model().objects.create( + comment='My Comment', user=self.author, is_public=True, + content_object=self.nodetype, site=self.site) + self.assertEquals(len(mail.outbox), 0) + moderator = NodetypeCommentModerator(Nodetype) + moderator.email_reply = False + moderator.email_authors = False + moderator.mail_comment_notification_recipients = [] + moderator.email(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 0) + moderator.email_reply = True + moderator.email_authors = True + moderator.mail_comment_notification_recipients = ['admin@example.com'] + moderator.email(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 1) + + def test_do_email_notification(self): + comment = comments.get_model().objects.create( + comment='My Comment', user=self.author, is_public=True, + content_object=self.nodetype, site=self.site) + self.assertEquals(len(mail.outbox), 0) + moderator = NodetypeCommentModerator(Nodetype) + moderator.mail_comment_notification_recipients = ['admin@example.com'] + moderator.do_email_notification(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 1) + + def test_do_email_authors(self): + comment = comments.get_model().objects.create( + comment='My Comment', user=self.author, is_public=True, + content_object=self.nodetype, site=self.site) + self.assertEquals(len(mail.outbox), 0) + moderator = NodetypeCommentModerator(Nodetype) + moderator.email_authors = True + moderator.mail_comment_notification_recipients = ['admin@example.com'] + moderator.do_email_authors(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 0) + moderator.mail_comment_notification_recipients = [] + moderator.do_email_authors(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 1) + + def test_do_email_reply(self): + comment = comments.get_model().objects.create( + comment='My Comment 1', user=self.author, is_public=True, + content_object=self.nodetype, site=self.site) + moderator = NodetypeCommentModerator(Nodetype) + moderator.email_notification_reply = True + moderator.mail_comment_notification_recipients = ['admin@example.com'] + moderator.do_email_reply(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 0) + + comment = comments.get_model().objects.create( + comment='My Comment 2', user_email='user_1@example.com', + content_object=self.nodetype, is_public=True, site=self.site) + moderator.do_email_reply(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 0) + + comment = comments.get_model().objects.create( + comment='My Comment 3', user_email='user_2@example.com', + content_object=self.nodetype, is_public=True, site=self.site) + moderator.do_email_reply(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 1) + self.assertEquals(mail.outbox[0].bcc, [u'user_1@example.com']) + + comment = comments.get_model().objects.create( + comment='My Comment 4', user=self.author, is_public=True, + content_object=self.nodetype, site=self.site) + moderator.do_email_reply(comment, self.nodetype, 'request') + self.assertEquals(len(mail.outbox), 2) + self.assertEquals(mail.outbox[1].bcc, [u'user_1@example.com', + u'user_2@example.com']) + + def test_moderate(self): + comment = comments.get_model().objects.create( + comment='My Comment', user=self.author, is_public=True, + content_object=self.nodetype, site=self.site) + moderator = NodetypeCommentModerator(Nodetype) + moderator.auto_moderate_comments = True + moderator.spam_checker_backends = () + self.assertEquals(moderator.moderate(comment, self.nodetype, 'request'), + True) + moderator.auto_moderate_comments = False + self.assertEquals(moderator.moderate(comment, self.nodetype, 'request'), + False) + self.assertEquals(comments.get_model().objects.filter( + flags__flag='spam').count(), 0) + moderator.spam_checker_backends = ( + 'gstudio.spam_checker.backends.all_is_spam',) + self.assertEquals(moderator.moderate(comment, self.nodetype, 'request'), + True) + self.assertEquals(comments.get_model().objects.filter( + flags__flag='spam').count(), 1) diff --git a/gstudio/tests/nodetype.py b/gstudio/tests/nodetype.py new file mode 100644 index 0000000..2d09336 --- /dev/null +++ b/gstudio/tests/nodetype.py @@ -0,0 +1,348 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's Nodetype""" +from __future__ import with_statement +import warnings +from datetime import datetime +from datetime import timedelta + +from django.test import TestCase +from django.conf import settings +from django.contrib import comments +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core.urlresolvers import reverse +from django.contrib.comments.models import CommentFlag + +from gstudio import models +from gstudio.models import Nodetype +from gstudio.managers import PUBLISHED +from gstudio.models import get_base_model +from gstudio.models import Nodetype +from gstudio import models as models_settings +from gstudio import url_shortener as shortener_settings + + +class NodetypeTestCase(TestCase): + + def setUp(self): + params = {'title': 'My nodetype', + 'content': 'My content', + 'slug': 'my-nodetype'} + self.nodetype = Nodetype.objects.create(**params) + + def test_discussions(self): + site = Site.objects.get_current() + self.assertEquals(self.nodetype.discussions.count(), 0) + self.assertEquals(self.nodetype.comments.count(), 0) + self.assertEquals(self.nodetype.pingbacks.count(), 0) + self.assertEquals(self.nodetype.trackbacks.count(), 0) + + comments.get_model().objects.create(comment='My Comment 1', + content_object=self.nodetype, + site=site) + self.assertEquals(self.nodetype.discussions.count(), 1) + self.assertEquals(self.nodetype.comments.count(), 1) + self.assertEquals(self.nodetype.pingbacks.count(), 0) + self.assertEquals(self.nodetype.trackbacks.count(), 0) + + comments.get_model().objects.create(comment='My Comment 2', + content_object=self.nodetype, + site=site, is_public=False) + self.assertEquals(self.nodetype.discussions.count(), 1) + self.assertEquals(self.nodetype.comments.count(), 1) + self.assertEquals(self.nodetype.pingbacks.count(), 0) + self.assertEquals(self.nodetype.trackbacks.count(), 0) + + author = User.objects.create_user(username='webmaster', + email='webmaster@example.com') + + comment = comments.get_model().objects.create( + comment='My Comment 3', + content_object=self.nodetype, + site=Site.objects.create(domain='http://toto.com', + name='Toto.com')) + comment.flags.create(user=author, flag=CommentFlag.MODERATOR_APPROVAL) + self.assertEquals(self.nodetype.discussions.count(), 2) + self.assertEquals(self.nodetype.comments.count(), 2) + self.assertEquals(self.nodetype.pingbacks.count(), 0) + self.assertEquals(self.nodetype.trackbacks.count(), 0) + + comment = comments.get_model().objects.create( + comment='My Pingback 1', content_object=self.nodetype, site=site) + comment.flags.create(user=author, flag='pingback') + self.assertEquals(self.nodetype.discussions.count(), 3) + self.assertEquals(self.nodetype.comments.count(), 2) + self.assertEquals(self.nodetype.pingbacks.count(), 1) + self.assertEquals(self.nodetype.trackbacks.count(), 0) + + comment = comments.get_model().objects.create( + comment='My Trackback 1', content_object=self.nodetype, site=site) + comment.flags.create(user=author, flag='trackback') + self.assertEquals(self.nodetype.discussions.count(), 4) + self.assertEquals(self.nodetype.comments.count(), 2) + self.assertEquals(self.nodetype.pingbacks.count(), 1) + self.assertEquals(self.nodetype.trackbacks.count(), 1) + + def test_str(self): + self.assertEquals(str(self.nodetype), 'My nodetype: draft') + + def test_word_count(self): + self.assertEquals(self.nodetype.word_count, 2) + + def test_comments_are_open(self): + original_auto_close = models.AUTO_CLOSE_COMMENTS_AFTER + models.AUTO_CLOSE_COMMENTS_AFTER = None + self.assertEquals(self.nodetype.comments_are_open, True) + models.AUTO_CLOSE_COMMENTS_AFTER = 5 + self.nodetype.start_publication = datetime.now() - timedelta(days=7) + self.nodetype.save() + self.assertEquals(self.nodetype.comments_are_open, False) + + models.AUTO_CLOSE_COMMENTS_AFTER = original_auto_close + + def test_is_actual(self): + self.assertTrue(self.nodetype.is_actual) + self.nodetype.start_publication = datetime(2020, 3, 15) + self.assertFalse(self.nodetype.is_actual) + self.nodetype.start_publication = datetime.now() + self.assertTrue(self.nodetype.is_actual) + self.nodetype.end_publication = datetime(2000, 3, 15) + self.assertFalse(self.nodetype.is_actual) + + def test_is_visible(self): + self.assertFalse(self.nodetype.is_visible) + self.nodetype.status = PUBLISHED + self.assertTrue(self.nodetype.is_visible) + self.nodetype.start_publication = datetime(2020, 3, 15) + self.assertFalse(self.nodetype.is_visible) + + def test_short_url(self): + original_shortener = shortener_settings.URL_SHORTENER_BACKEND + shortener_settings.URL_SHORTENER_BACKEND = 'gstudio.url_shortener.'\ + 'backends.default' + self.assertEquals(self.nodetype.short_url, + 'http://example.com' + + reverse('gstudio_nodetype_shortlink', + args=[self.nodetype.pk])) + shortener_settings.URL_SHORTENER_BACKEND = original_shortener + + def test_previous_nodetype(self): + site = Site.objects.get_current() + self.assertFalse(self.nodetype.previous_nodetype) + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'slug': 'my-second-nodetype', + 'creation_date': datetime(2000, 1, 1), + 'status': PUBLISHED} + self.second_nodetype = Nodetype.objects.create(**params) + self.second_nodetype.sites.add(site) + self.assertEquals(self.nodetype.previous_nodetype, self.second_nodetype) + params = {'title': 'My third nodetype', + 'content': 'My third content', + 'slug': 'my-third-nodetype', + 'creation_date': datetime(2001, 1, 1), + 'status': PUBLISHED} + self.third_nodetype = Nodetype.objects.create(**params) + self.third_nodetype.sites.add(site) + self.assertEquals(self.nodetype.previous_nodetype, self.third_nodetype) + self.assertEquals(self.third_nodetype.previous_nodetype, self.second_nodetype) + + def test_next_nodetype(self): + site = Site.objects.get_current() + self.assertFalse(self.nodetype.next_nodetype) + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'slug': 'my-second-nodetype', + 'creation_date': datetime(2100, 1, 1), + 'status': PUBLISHED} + self.second_nodetype = Nodetype.objects.create(**params) + self.second_nodetype.sites.add(site) + self.assertEquals(self.nodetype.next_nodetype, self.second_nodetype) + params = {'title': 'My third nodetype', + 'content': 'My third content', + 'slug': 'my-third-nodetype', + 'creation_date': datetime(2050, 1, 1), + 'status': PUBLISHED} + self.third_nodetype = Nodetype.objects.create(**params) + self.third_nodetype.sites.add(site) + self.assertEquals(self.nodetype.next_nodetype, self.third_nodetype) + self.assertEquals(self.third_nodetype.next_nodetype, self.second_nodetype) + + def test_related_published(self): + site = Site.objects.get_current() + self.assertFalse(self.nodetype.related_published) + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'slug': 'my-second-nodetype', + 'status': PUBLISHED} + self.second_nodetype = Nodetype.objects.create(**params) + self.second_nodetype.related.add(self.nodetype) + self.assertEquals(len(self.nodetype.related_published), 0) + + self.second_nodetype.sites.add(site) + self.assertEquals(len(self.nodetype.related_published), 1) + self.assertEquals(len(self.second_nodetype.related_published), 0) + + self.nodetype.status = PUBLISHED + self.nodetype.save() + self.nodetype.sites.add(site) + self.assertEquals(len(self.nodetype.related_published), 1) + self.assertEquals(len(self.second_nodetype.related_published), 1) + + +class NodetypeHtmlContentTestCase(TestCase): + + def setUp(self): + params = {'title': 'My nodetype', + 'content': 'My content', + 'slug': 'my-nodetype'} + self.nodetype = Nodetype(**params) + self.original_debug = settings.DEBUG + self.original_rendering = models_settings.MARKUP_LANGUAGE + settings.DEBUG = False + + def tearDown(self): + settings.DEBUG = self.original_debug + models_settings.MARKUP_LANGUAGE = self.original_rendering + + def test_html_content_default(self): + models_settings.MARKUP_LANGUAGE = None + self.assertEquals(self.nodetype.html_content, '<p>My content</p>') + + self.nodetype.content = 'Hello world !\n' \ + ' this is my content' + self.assertEquals(self.nodetype.html_content, + '<p>Hello world !<br /> this is my content</p>') + + def test_html_content_textitle(self): + models_settings.MARKUP_LANGUAGE = 'textile' + self.nodetype.content = 'Hello world !\n\n' \ + 'this is my content :\n\n' \ + '* Item 1\n* Item 2' + html_content = self.nodetype.html_content + try: + self.assertEquals(html_content, + '\t<p>Hello world !</p>\n\n\t' \ + '<p>this is my content :</p>\n\n\t' \ + '<ul>\n\t\t<li>Item 1</li>\n\t\t' \ + '<li>Item 2</li>\n\t</ul>') + except AssertionError: + self.assertEquals(html_content, self.nodetype.content) + + def test_html_content_markdown(self): + models_settings.MARKUP_LANGUAGE = 'markdown' + self.nodetype.content = 'Hello world !\n\n' \ + 'this is my content :\n\n' \ + '* Item 1\n* Item 2' + html_content = self.nodetype.html_content + try: + self.assertEquals(html_content, + '<p>Hello world !</p>\n' \ + '<p>this is my content :</p>'\ + '\n<ul>\n<li>Item 1</li>\n' \ + '<li>Item 2</li>\n</ul>') + except AssertionError: + self.assertEquals(html_content, self.nodetype.content) + + def test_html_content_restructuredtext(self): + models_settings.MARKUP_LANGUAGE = 'restructuredtext' + self.nodetype.content = 'Hello world !\n\n' \ + 'this is my content :\n\n' \ + '* Item 1\n* Item 2' + html_content = self.nodetype.html_content + try: + self.assertEquals(html_content, + '<p>Hello world !</p>\n' \ + '<p>this is my content :</p>'\ + '\n<ul class="simple">\n<li>Item 1</li>\n' \ + '<li>Item 2</li>\n</ul>\n') + except AssertionError: + self.assertEquals(html_content, self.nodetype.content) + +# this class can be removed since the base abstract class is no longer present. +class NodetypeGetBaseModelTestCase(TestCase): + + def setUp(self): + self.original_nodetype_base_model = models_settings.NODETYPE_BASE_MODEL + + def tearDown(self): + models_settings.NODETYPE_BASE_MODEL = self.original_nodetype_base_model + + def test_get_base_model(self): + models_settings.NODETYPE_BASE_MODEL = '' + self.assertEquals(get_base_model(), Nodetype) + + models_settings.NODETYPE_BASE_MODEL = 'mymodule.myclass' + try: + with warnings.catch_warnings(record=True) as w: + self.assertEquals(get_base_model(), Nodetype) + self.assertTrue(issubclass(w[-1].metatype, RuntimeWarning)) + except AttributeError: + # Fail under Python2.5, because of'warnings.catch_warnings' + pass + + models_settings.NODETYPE_BASE_MODEL = 'gstudio.models.Nodetype' + self.assertEquals(get_base_model(), Nodetype) diff --git a/gstudio/tests/ping.py b/gstudio/tests/ping.py new file mode 100644 index 0000000..85dd463 --- /dev/null +++ b/gstudio/tests/ping.py @@ -0,0 +1,187 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's ping""" +import cStringIO +from urllib2 import URLError +from urllib import addinfourl +from django.test import TestCase + +from gstudio.models import Nodetype +from gstudio.ping import URLRessources +from gstudio.ping import DirectoryPinger +from gstudio.ping import ExternalUrlsPinger + + +class DirectoryPingerTestCase(TestCase): + """Test cases for DirectoryPinger""" + def setUp(self): + params = {'title': 'My nodetype', + 'content': 'My content', + 'tags': 'gstudio, test', + 'slug': 'my-nodetype'} + self.nodetype = Nodetype.objects.create(**params) + self.pinger = DirectoryPinger('http://localhost', [self.nodetype], + start_now=False) + + def test_ping_nodetype(self): + self.assertEquals( + self.pinger.ping_nodetype(self.nodetype), + {'message': 'http://localhost is an invalid directory.', + 'flerror': True}) + + +class ExternalUrlsPingerTestCase(TestCase): + """Test cases for ExternalUrlsPinger""" + + def setUp(self): + params = {'title': 'My nodetype', + 'content': 'My content', + 'tags': 'gstudio, test', + 'slug': 'my-nodetype'} + self.nodetype = Nodetype.objects.create(**params) + self.pinger = ExternalUrlsPinger(self.nodetype, start_now=False) + + def test_is_external_url(self): + r = URLRessources() + self.assertEquals(self.pinger.is_external_url( + 'http://example.com/', 'http://google.com/'), True) + self.assertEquals(self.pinger.is_external_url( + 'http://example.com/toto/', 'http://google.com/titi/'), True) + self.assertEquals(self.pinger.is_external_url( + 'http://example.com/blog/', 'http://example.com/page/'), False) + self.assertEquals(self.pinger.is_external_url( + '%s/blog/' % r.site_url, r.site_url), False) + self.assertEquals(self.pinger.is_external_url( + 'http://google.com/', r.site_url), True) + self.assertEquals(self.pinger.is_external_url( + '/blog/', r.site_url), False) + + def test_find_external_urls(self): + r = URLRessources() + external_urls = self.pinger.find_external_urls(self.nodetype) + self.assertEquals(external_urls, []) + self.nodetype.content = """ + <p>This is a <a href="http://fantomas.willbreak.it/">link</a> + to a site.</p> + <p>This is a <a href="%s/blog/">link</a> within my site.</p> + <p>This is a <a href="/blog/">relative link</a> within my site.</p> + """ % r.site_url + self.nodetype.save() + external_urls = self.pinger.find_external_urls(self.nodetype) + self.assertEquals(external_urls, ['http://fantomas.willbreak.it/']) + + def test_find_pingback_href(self): + result = self.pinger.find_pingback_href('') + self.assertEquals(result, None) + result = self.pinger.find_pingback_href(""" + <html><head><link rel="pingback" href="/xmlrpc/" /></head> + <body></body></html> + """) + self.assertEquals(result, '/xmlrpc/') + result = self.pinger.find_pingback_href(""" + <html><head><LINK hrEF="/xmlrpc/" REL="PingBack" /></head> + <body></body></html> + """) + self.assertEquals(result, '/xmlrpc/') + result = self.pinger.find_pingback_href(""" + <html><head><LINK REL="PingBack" /></head><body></body></html> + """) + self.assertEquals(result, None) + + def fake_urlopen(self, url): + """Fake urlopen using test client""" + if 'example' in url: + response = cStringIO.StringIO('') + return addinfourl(response, {'X-Pingback': '/xmlrpc.php', + 'Content-Type': 'text/html'}, url) + elif 'localhost' in url: + response = cStringIO.StringIO( + '<link rel="pingback" href="/xmlrpc/">') + return addinfourl(response, {'Content-Type': 'text/xhtml'}, url) + elif 'google' in url: + response = cStringIO.StringIO('PNG CONTENT') + return addinfourl(response, {'content-type': 'image/png'}, url) + elif 'error' in url: + raise URLError('Invalid ressource') + + def test_find_pingback_urls(self): + # Set up a stub around urlopen + import gstudio.ping + self.original_urlopen = gstudio.ping.urlopen + gstudio.ping.urlopen = self.fake_urlopen + + urls = ['http://localhost/', 'http://example.com/', 'http://error', + 'http://www.google.co.uk/images/nav_logo72.png'] + self.assertEquals( + self.pinger.find_pingback_urls(urls), + {'http://localhost/': 'http://localhost/xmlrpc/', + 'http://example.com/': 'http://example.com/xmlrpc.php'}) + # Remove stub + gstudio.ping.urlopen = self.original_urlopen + + def test_pingback_url(self): + self.assertEquals(self.pinger.pingback_url('http://localhost', + 'http://error.com'), + 'http://error.com cannot be pinged.') diff --git a/gstudio/tests/pingback.py b/gstudio/tests/pingback.py new file mode 100644 index 0000000..4bc295c --- /dev/null +++ b/gstudio/tests/pingback.py @@ -0,0 +1,253 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +"""Test cases for Gstudio's PingBack API""" +import cStringIO +from datetime import datetime +from urlparse import urlsplit +from urllib2 import HTTPError +from xmlrpclib import ServerProxy + +from django.test import TestCase +from django.contrib import comments +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.contrib.contenttypes.models import ContentType + +from BeautifulSoup import BeautifulSoup + +from gstudio.models import Nodetype +from gstudio.models import Metatype +from gstudio.managers import PUBLISHED +from gstudio.tests.utils import TestTransport +from gstudio.xmlrpc.pingback import generate_pingback_content +from gstudio import url_shortener as shortener_settings + + +class PingBackTestCase(TestCase): + """Test cases for pingbacks""" + urls = 'gstudio.tests.urls' + + def fake_urlopen(self, url): + """Fake urlopen using client if domain + correspond to current_site else HTTPError""" + scheme, netloc, path, query, fragment = urlsplit(url) + if not netloc: + raise + if self.site.domain == netloc: + response = cStringIO.StringIO(self.client.get(url).content) + return response + raise HTTPError(url, 404, 'unavailable url', {}, None) + + def setUp(self): + # Use default URL shortener backend, to avoid networks errors + self.original_shortener = shortener_settings.URL_SHORTENER_BACKEND + shortener_settings.URL_SHORTENER_BACKEND = 'gstudio.url_shortener.'\ + 'backends.default' + # Set up a stub around urlopen + import gstudio.xmlrpc.pingback + self.original_urlopen = gstudio.xmlrpc.pingback.urlopen + gstudio.xmlrpc.pingback.urlopen = self.fake_urlopen + # Preparing site + self.site = Site.objects.get_current() + self.site.domain = 'localhost:8000' + self.site.save() + # Creating tests nodetypes + self.author = User.objects.create_user(username='webmaster', + email='webmaster@example.com') + self.metatype = Metatype.objects.create(title='test', slug='test') + params = {'title': 'My first nodetype', + 'content': 'My first content', + 'slug': 'my-first-nodetype', + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + self.first_nodetype = Nodetype.objects.create(**params) + self.first_nodetype.sites.add(self.site) + self.first_nodetype.metatypes.add(self.metatype) + self.first_nodetype.authors.add(self.author) + + params = {'title': 'My second nodetype', + 'content': 'My second content with link ' + 'to <a href="http://%s%s">first nodetype</a>' + ' and other links : %s %s.' % ( + self.site.domain, + self.first_nodetype.get_absolute_url(), + 'http://localhost:8000/error-404/', + 'http://example.com/'), + 'slug': 'my-second-nodetype', + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + self.second_nodetype = Nodetype.objects.create(**params) + self.second_nodetype.sites.add(self.site) + self.second_nodetype.metatypes.add(self.metatype) + self.second_nodetype.authors.add(self.author) + # Instanciating the server proxy + self.server = ServerProxy('http://localhost:8000/xmlrpc/', + transport=TestTransport()) + + def tearDown(self): + import gstudio.xmlrpc.pingback + gstudio.xmlrpc.pingback.urlopen = self.original_urlopen + shortener_settings.URL_SHORTENER_BACKEND = self.original_shortener + + def test_generate_pingback_content(self): + soup = BeautifulSoup(self.second_nodetype.content) + target = 'http://%s%s' % (self.site.domain, + self.first_nodetype.get_absolute_url()) + + self.assertEquals( + generate_pingback_content(soup, target, 1000), + 'My second content with link to first nodetype and other links : ' + 'http://localhost:8000/error-404/ http://example.com/.') + self.assertEquals( + generate_pingback_content(soup, target, 50), + '...ond content with link to first nodetype and other lin...') + + soup = BeautifulSoup('<a href="%s">test link</a>' % target) + self.assertEquals( + generate_pingback_content(soup, target, 6), 'test l...') + + soup = BeautifulSoup('test <a href="%s">link</a>' % target) + self.assertEquals( + generate_pingback_content(soup, target, 8), '...est link') + self.assertEquals( + generate_pingback_content(soup, target, 9), 'test link') + + def test_pingback_ping(self): + target = 'http://%s%s' % ( + self.site.domain, self.first_nodetype.get_absolute_url()) + source = 'http://%s%s' % ( + self.site.domain, self.second_nodetype.get_absolute_url()) + + # Error code 0 : A generic fault code + response = self.server.pingback.ping('toto', 'titi') + self.assertEquals(response, 0) + response = self.server.pingback.ping('http://%s/' % self.site.domain, + 'http://%s/' % self.site.domain) + self.assertEquals(response, 0) + + # Error code 16 : The source URI does not exist. + response = self.server.pingback.ping('http://example.com/', target) + self.assertEquals(response, 16) + + # Error code 17 : The source URI does not contain a link to + # the target URI and so cannot be used as a source. + response = self.server.pingback.ping(source, 'toto') + self.assertEquals(response, 17) + + # Error code 32 : The target URI does not exist. + response = self.server.pingback.ping( + source, 'http://localhost:8000/error-404/') + self.assertEquals(response, 32) + response = self.server.pingback.ping(source, 'http://example.com/') + self.assertEquals(response, 32) + + # Error code 33 : The target URI cannot be used as a target. + response = self.server.pingback.ping(source, 'http://localhost:8000/') + self.assertEquals(response, 33) + self.first_nodetype.pingback_enabled = False + self.first_nodetype.save() + response = self.server.pingback.ping(source, target) + self.assertEquals(response, 33) + + # Validate pingback + self.assertEquals(self.first_nodetype.comments.count(), 0) + self.first_nodetype.pingback_enabled = True + self.first_nodetype.save() + response = self.server.pingback.ping(source, target) + self.assertEquals( + response, + 'Pingback from %s to %s registered.' % (source, target)) + self.assertEquals(self.first_nodetype.pingbacks.count(), 1) + self.assertTrue(self.second_nodetype.title in \ + self.first_nodetype.pingbacks[0].user_name) + + # Error code 48 : The pingback has already been registered. + response = self.server.pingback.ping(source, target) + self.assertEquals(response, 48) + + def test_pingback_extensions_get_pingbacks(self): + target = 'http://%s%s' % ( + self.site.domain, self.first_nodetype.get_absolute_url()) + source = 'http://%s%s' % ( + self.site.domain, self.second_nodetype.get_absolute_url()) + + response = self.server.pingback.ping(source, target) + self.assertEquals( + response, 'Pingback from %s to %s registered.' % (source, target)) + + response = self.server.pingback.extensions.getPingbacks( + 'http://example.com/') + self.assertEquals(response, 32) + + response = self.server.pingback.extensions.getPingbacks( + 'http://localhost:8000/error-404/') + self.assertEquals(response, 32) + + response = self.server.pingback.extensions.getPingbacks( + 'http://localhost:8000/2010/') + self.assertEquals(response, 33) + + response = self.server.pingback.extensions.getPingbacks(source) + self.assertEquals(response, []) + + response = self.server.pingback.extensions.getPingbacks(target) + self.assertEquals(response, [ + 'http://localhost:8000/2010/01/01/my-second-nodetype/']) + + comment = comments.get_model().objects.create( + content_type=ContentType.objects.get_for_model(Nodetype), + object_pk=self.first_nodetype.pk, + site=self.site, comment='Test pingback', + user_url='http://example.com/blog/1/', + user_name='Test pingback') + comment.flags.create(user=self.author, flag='pingback') + + response = self.server.pingback.extensions.getPingbacks(target) + self.assertEquals(response, [ + 'http://localhost:8000/2010/01/01/my-second-nodetype/', + 'http://example.com/blog/1/']) diff --git a/gstudio/tests/quick_nodetype.py b/gstudio/tests/quick_nodetype.py new file mode 100644 index 0000000..066eb95 --- /dev/null +++ b/gstudio/tests/quick_nodetype.py @@ -0,0 +1,103 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +"""Test cases for Gstudio's quick nodetype""" +from django.test import TestCase +from django.contrib.auth.models import User + +from gstudio import settings +from gstudio.models import Nodetype +from gstudio.managers import DRAFT + + +class QuickNodetypeTestCase(TestCase): + """Test cases for quick_nodetype view""" + urls = 'gstudio.tests.urls' + + def setUp(self): + self.original_wysiwyg = settings.WYSIWYG + settings.WYSIWYG = None + + def tearDown(self): + settings.WYSIWYG = self.original_wysiwyg + + def test_quick_nodetype(self): + User.objects.create_user('user', 'user@example.com', 'password') + User.objects.create_superuser('admin', 'admin@example.com', 'password') + + response = self.client.get('/quick_nodetype/', follow=True) + self.assertEquals( + response.redirect_chain, + [('http://testserver/accounts/login/?next=/quick_nodetype/', 302)]) + self.client.login(username='user', password='password') + response = self.client.get('/quick_nodetype/', follow=True) + self.assertEquals( + response.redirect_chain, + [('http://testserver/accounts/login/?next=/quick_nodetype/', 302)]) + self.client.logout() + self.client.login(username='admin', password='password') + response = self.client.get('/quick_nodetype/', follow=True) + self.assertEquals(response.redirect_chain, + [('http://testserver/admin/gstudio/nodetype/add/', 302)]) + response = self.client.post('/quick_nodetype/', {'title': 'test'}, + follow=True) + self.assertEquals(response.redirect_chain, + [('http://testserver/admin/gstudio/nodetype/add/' \ + '?tags=&title=test&sites=1&content=' \ + '%3Cp%3E%3C%2Fp%3E&authors=2&slug=test', 302)]) + response = self.client.post('/quick_nodetype/', + {'title': 'test', 'tags': 'test', + 'content': 'Test content', + 'save_draft': ''}, follow=True) + nodetype = Nodetype.objects.get(title='test') + self.assertEquals(response.redirect_chain, + [('http://testserver%s' % nodetype.get_absolute_url(), + 302)]) + self.assertEquals(nodetype.status, DRAFT) + self.assertEquals(nodetype.title, 'test') + self.assertEquals(nodetype.tags, 'test') + self.assertEquals(nodetype.content, '<p>Test content</p>') diff --git a/gstudio/tests/signals.py b/gstudio/tests/signals.py new file mode 100644 index 0000000..0cc6529 --- /dev/null +++ b/gstudio/tests/signals.py @@ -0,0 +1,137 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +"""Test cases for Gstudio's signals""" +from django.test import TestCase + +from gstudio.models import Nodetype +from gstudio.managers import DRAFT +from gstudio.managers import PUBLISHED +from gstudio.signals import disable_for_loaddata +from gstudio.signals import ping_directories_handler +from gstudio.signals import ping_external_urls_handler + + +class SignalsTestCase(TestCase): + """Test cases for signals""" + + def test_disable_for_loaddata(self): + self.top = 0 + + @disable_for_loaddata + def make_top(): + self.top += 1 + + def call(): + return make_top() + + call() + self.assertEquals(self.top, 1) + # Okay the command is executed + + def test_ping_directories_handler(self): + # Set up a stub around DirectoryPinger + self.top = 0 + + def fake_pinger(*ka, **kw): + self.top += 1 + + import gstudio.ping + from gstudio import settings + self.original_pinger = gstudio.ping.DirectoryPinger + gstudio.ping.DirectoryPinger = fake_pinger + + params = {'title': 'My nodetype', + 'content': 'My content', + 'status': PUBLISHED, + 'slug': 'my-nodetype'} + nodetype = Nodetype.objects.create(**params) + self.assertEquals(nodetype.is_visible, True) + settings.PING_DIRECTORIES = () + ping_directories_handler('sender', **{'instance': nodetype}) + self.assertEquals(self.top, 0) + settings.PING_DIRECTORIES = ('toto',) + settings.SAVE_PING_DIRECTORIES = True + ping_directories_handler('sender', **{'instance': nodetype}) + self.assertEquals(self.top, 1) + nodetype.status = DRAFT + ping_directories_handler('sender', **{'instance': nodetype}) + self.assertEquals(self.top, 1) + + # Remove stub + gstudio.ping.DirectoryPinger = self.original_pinger + + def test_ping_external_urls_handler(self): + # Set up a stub around ExternalUrlsPinger + self.top = 0 + + def fake_pinger(*ka, **kw): + self.top += 1 + + import gstudio.ping + from gstudio import settings + self.original_pinger = gstudio.ping.ExternalUrlsPinger + gstudio.ping.ExternalUrlsPinger = fake_pinger + + params = {'title': 'My nodetype', + 'content': 'My content', + 'status': PUBLISHED, + 'slug': 'my-nodetype'} + nodetype = Nodetype.objects.create(**params) + self.assertEquals(nodetype.is_visible, True) + settings.SAVE_PING_EXTERNAL_URLS = False + ping_external_urls_handler('sender', **{'instance': nodetype}) + self.assertEquals(self.top, 0) + settings.SAVE_PING_EXTERNAL_URLS = True + ping_external_urls_handler('sender', **{'instance': nodetype}) + self.assertEquals(self.top, 1) + nodetype.status = 0 + ping_external_urls_handler('sender', **{'instance': nodetype}) + self.assertEquals(self.top, 1) + + # Remove stub + gstudio.ping.ExternalUrlsPinger = self.original_pinger diff --git a/gstudio/tests/sitemaps.py b/gstudio/tests/sitemaps.py new file mode 100644 index 0000000..c2ed549 --- /dev/null +++ b/gstudio/tests/sitemaps.py @@ -0,0 +1,128 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +"""Test cases for Gstudio's sitemaps""" +from django.test import TestCase +from django.contrib.auth.models import User +from django.contrib.sites.models import Site + +from tagging.models import Tag + +from gstudio.models import Nodetype +from gstudio.models import Author +from gstudio.models import Metatype +from gstudio.managers import PUBLISHED +from gstudio.sitemaps import NodetypeSitemap +from gstudio.sitemaps import MetatypeSitemap +from gstudio.sitemaps import AuthorSitemap +from gstudio.sitemaps import TagSitemap + + +class GstudioSitemapsTestCase(TestCase): + """Test cases for Sitemaps classes provided""" + urls = 'gstudio.tests.urls' + + def setUp(self): + self.site = Site.objects.get_current() + self.author = User.objects.create(username='admin', + email='admin@example.com') + self.metatype = Metatype.objects.create(title='Tests', slug='tests') + params = {'title': 'My nodetype 1', 'content': 'My content 1', + 'tags': 'gstudio, test', 'slug': 'my-nodetype-1', + 'status': PUBLISHED} + self.nodetype_1 = Nodetype.objects.create(**params) + self.nodetype_1.authors.add(self.author) + self.nodetype_1.metatypes.add(self.metatype) + self.nodetype_1.sites.add(self.site) + + params = {'title': 'My nodetype 2', 'content': 'My content 2', + 'tags': 'gstudio', 'slug': 'my-nodetype-2', + 'status': PUBLISHED} + self.nodetype_2 = Nodetype.objects.create(**params) + self.nodetype_2.authors.add(self.author) + self.nodetype_2.metatypes.add(self.metatype) + self.nodetype_2.sites.add(self.site) + + def test_nodetype_sitemap(self): + sitemap = NodetypeSitemap() + self.assertEquals(len(sitemap.items()), 2) + self.assertEquals(sitemap.lastmod(self.nodetype_1), + self.nodetype_1.last_update) + + def test_metatype_sitemap(self): + sitemap = MetatypeSitemap() + self.assertEquals(len(sitemap.items()), 1) + self.assertEquals(sitemap.lastmod(self.metatype), + self.nodetype_2.creation_date) + self.assertEquals(sitemap.lastmod(Metatype.objects.create( + title='New', slug='new')), None) + self.assertEquals(sitemap.priority(self.metatype), '1.0') + + def test_author_sitemap(self): + sitemap = AuthorSitemap() + authors = sitemap.items() + self.assertEquals(len(authors), 1) + self.assertEquals(sitemap.lastmod(authors[0]), + self.nodetype_2.creation_date) + self.assertEquals(sitemap.lastmod(Author.objects.create( + username='New', email='new@example.com')), None) + self.assertEquals(sitemap.location(self.author), '/authors/admin/') + + def test_tag_sitemap(self): + sitemap = TagSitemap() + gstudio_tag = Tag.objects.get(name='gstudio') + self.assertEquals(len(sitemap.items()), 2) + self.assertEquals(sitemap.lastmod(gstudio_tag), + self.nodetype_2.creation_date) + self.assertEquals(sitemap.priority(gstudio_tag), '1.0') + self.assertEquals(sitemap.location(gstudio_tag), '/tags/gstudio/') + + def test_metatype_sitemap_zero_division_error(self): + Nodetype.objects.all().delete() + metatype_sitemap = MetatypeSitemap() + metatype_sitemap.items() + self.assertEquals(metatype_sitemap.priority(self.metatype), '0.5') diff --git a/gstudio/tests/spam_checker.py b/gstudio/tests/spam_checker.py new file mode 100644 index 0000000..e40ae94 --- /dev/null +++ b/gstudio/tests/spam_checker.py @@ -0,0 +1,40 @@ +"""Test cases for Gstudio's spam_checker""" +from __future__ import with_statement +import warnings + +from django.test import TestCase + +from gstudio.spam_checker import get_spam_checker +from gstudio.spam_checker.backends.all_is_spam import backend + + +class SpamCheckerTestCase(TestCase): + """Test cases for gstudio.spam_checker""" + + def test_get_spam_checker(self): + try: + with warnings.catch_warnings(record=True) as w: + self.assertEquals(get_spam_checker('mymodule.myclass'), None) + self.assertTrue(issubclass(w[-1].metatype, RuntimeWarning)) + self.assertEquals( + str(w[-1].message), + 'mymodule.myclass backend cannot be imported') + except AttributeError: + # Fail under Python2.5, because of'warnings.catch_warnings' + pass + + try: + with warnings.catch_warnings(record=True) as w: + self.assertEquals( + get_spam_checker('gstudio.tests.custom_spam_checker'), None) + self.assertTrue(issubclass(w[-1].metatype, RuntimeWarning)) + self.assertEquals( + str(w[-1].message), + 'This backend only exists for testing') + except AttributeError: + # Fail under Python2.5, because of'warnings.catch_warnings' + pass + + self.assertEquals( + get_spam_checker('gstudio.spam_checker.backends.all_is_spam'), + backend) diff --git a/gstudio/tests/templates/gstudio/_nodetype_detail.html b/gstudio/tests/templates/gstudio/_nodetype_detail.html new file mode 100644 index 0000000..0ffd2e6 --- /dev/null +++ b/gstudio/tests/templates/gstudio/_nodetype_detail.html @@ -0,0 +1,3 @@ +<h1>{{ object.title }}</h1> + +{{ object.content|linebreaks }} diff --git a/gstudio/tests/templates/gstudio/base.html b/gstudio/tests/templates/gstudio/base.html new file mode 100644 index 0000000..6b4226d --- /dev/null +++ b/gstudio/tests/templates/gstudio/base.html @@ -0,0 +1,9 @@ +<html> + <head> + <title>Gnowledge Studio - {% block title %}{% endblock %}</title> + </head> + <body> + {% block content %}{% endblock %} + </body> +</html> + diff --git a/gstudio/tests/templates/gstudio/nodetype_detail.html b/gstudio/tests/templates/gstudio/nodetype_detail.html new file mode 100644 index 0000000..43491f7 --- /dev/null +++ b/gstudio/tests/templates/gstudio/nodetype_detail.html @@ -0,0 +1,7 @@ +{% extends "gstudio/base.html" %} + +{% block title %}{{ object.title }}{% endblock %} + +{% block content %} + {{ object.html_content|safe }} +{% endblock %} diff --git a/gstudio/tests/templates/gstudio/nodetype_list.html b/gstudio/tests/templates/gstudio/nodetype_list.html new file mode 100644 index 0000000..269e0ae --- /dev/null +++ b/gstudio/tests/templates/gstudio/nodetype_list.html @@ -0,0 +1,10 @@ +{% extends "gstudio/base.html" %} + +{% block content %} + +{% for object in object_list %} + {{ object.html_content|safe }} +{% endfor %} + +{% endblock %} + diff --git a/gstudio/tests/templates/gstudio/nodetype_search.html b/gstudio/tests/templates/gstudio/nodetype_search.html new file mode 100644 index 0000000..54b8914 --- /dev/null +++ b/gstudio/tests/templates/gstudio/nodetype_search.html @@ -0,0 +1 @@ +{% extends "gstudio/nodetype_list.html" %} diff --git a/gstudio/tests/templatetags.py b/gstudio/tests/templatetags.py new file mode 100644 index 0000000..37d47c2 --- /dev/null +++ b/gstudio/tests/templatetags.py @@ -0,0 +1,590 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's templatetags""" +from datetime import datetime + +from django.test import TestCase +from django.template import Context +from django.template import Template +from django.template import TemplateSyntaxError +from django.contrib import comments +from django.core.paginator import Paginator +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.contrib.comments.models import CommentFlag + +from tagging.models import Tag + +from gstudio.models import Nodetype +from gstudio.models import Author +from gstudio.models import Metatype +from gstudio.managers import DRAFT +from gstudio.managers import PUBLISHED +from gstudio.templatetags.gstudio_tags import get_authors +from gstudio.templatetags.gstudio_tags import get_gravatar +from gstudio.templatetags.gstudio_tags import get_tag_cloud +from gstudio.templatetags.gstudio_tags import get_metatypes +from gstudio.templatetags.gstudio_tags import gstudio_pagination +from gstudio.templatetags.gstudio_tags import get_recent_nodetypes +from gstudio.templatetags.gstudio_tags import get_random_nodetypes +from gstudio.templatetags.gstudio_tags import gstudio_breadcrumbs +from gstudio.templatetags.gstudio_tags import get_popular_nodetypes +from gstudio.templatetags.gstudio_tags import get_similar_nodetypes +from gstudio.templatetags.gstudio_tags import get_recent_comments +from gstudio.templatetags.gstudio_tags import get_recent_linkbacks +from gstudio.templatetags.gstudio_tags import get_calendar_nodetypes +from gstudio.templatetags.gstudio_tags import get_archives_nodetypes +from gstudio.templatetags.gstudio_tags import get_featured_nodetypes +from gstudio.templatetags.gstudio_tags import get_archives_nodetypes_tree + + +class TemplateTagsTestCase(TestCase): + """Test cases for Template tags""" + + def setUp(self): + params = {'title': 'My nodetype', + 'content': 'My content', + 'tags': 'gstudio, test', + 'creation_date': datetime(2010, 1, 1), + 'slug': 'my-nodetype'} + self.nodetype = Nodetype.objects.create(**params) + + def publish_nodetype(self): + self.nodetype.status = PUBLISHED + self.nodetype.featured = True + self.nodetype.sites.add(Site.objects.get_current()) + self.nodetype.save() + + def test_get_metatypes(self): + context = get_metatypes() + self.assertEquals(len(context['metatypes']), 0) + self.assertEquals(context['template'], 'gstudio/tags/metatypes.html') + + Metatype.objects.create(title='Metatype 1', slug='metatype-1') + context = get_metatypes('custom_template.html') + self.assertEquals(len(context['metatypes']), 1) + self.assertEquals(context['template'], 'custom_template.html') + + def test_get_authors(self): + context = get_authors() + self.assertEquals(len(context['authors']), 0) + self.assertEquals(context['template'], 'gstudio/tags/authors.html') + + user = User.objects.create_user(username='webmaster', + email='webmaster@example.com') + self.nodetype.authors.add(user) + self.publish_nodetype() + context = get_authors('custom_template.html') + self.assertEquals(len(context['authors']), 1) + self.assertEquals(context['template'], 'custom_template.html') + + def test_get_recent_nodetypes(self): + context = get_recent_nodetypes() + self.assertEquals(len(context['nodetypes']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/recent_nodetypes.html') + + self.publish_nodetype() + context = get_recent_nodetypes(3, 'custom_template.html') + self.assertEquals(len(context['nodetypes']), 1) + self.assertEquals(context['template'], 'custom_template.html') + context = get_recent_nodetypes(0) + self.assertEquals(len(context['nodetypes']), 0) + + def test_get_featured_nodetypes(self): + context = get_featured_nodetypes() + self.assertEquals(len(context['nodetypes']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/featured_nodetypes.html') + + self.publish_nodetype() + context = get_featured_nodetypes(3, 'custom_template.html') + self.assertEquals(len(context['nodetypes']), 1) + self.assertEquals(context['template'], 'custom_template.html') + context = get_featured_nodetypes(0) + self.assertEquals(len(context['nodetypes']), 0) + + def test_get_random_nodetypes(self): + context = get_random_nodetypes() + self.assertEquals(len(context['nodetypes']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/random_nodetypes.html') + + self.publish_nodetype() + context = get_random_nodetypes(3, 'custom_template.html') + self.assertEquals(len(context['nodetypes']), 1) + self.assertEquals(context['template'], 'custom_template.html') + context = get_random_nodetypes(0) + self.assertEquals(len(context['nodetypes']), 0) + + def test_get_popular_nodetypes(self): + context = get_popular_nodetypes() + self.assertEquals(len(context['nodetypes']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/popular_nodetypes.html') + + self.publish_nodetype() + context = get_popular_nodetypes(3, 'custom_template.html') + self.assertEquals(len(context['nodetypes']), 0) + self.assertEquals(context['template'], 'custom_template.html') + + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'tags': 'gstudio, test', + 'status': PUBLISHED, + 'slug': 'my-second-nodetype'} + site = Site.objects.get_current() + second_nodetype = Nodetype.objects.create(**params) + second_nodetype.sites.add(site) + + comments.get_model().objects.create(comment='My Comment 1', site=site, + content_object=self.nodetype) + comments.get_model().objects.create(comment='My Comment 2', site=site, + content_object=self.nodetype) + comments.get_model().objects.create(comment='My Comment 3', site=site, + content_object=second_nodetype) + context = get_popular_nodetypes(3) + self.assertEquals(context['nodetypes'], [self.nodetype, second_nodetype]) + self.nodetype.status = DRAFT + self.nodetype.save() + context = get_popular_nodetypes(3) + self.assertEquals(context['nodetypes'], [second_nodetype]) + + def test_get_similar_nodetypes(self): + self.publish_nodetype() + source_context = Context({'object': self.nodetype}) + context = get_similar_nodetypes(source_context) + self.assertEquals(len(context['nodetypes']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/similar_nodetypes.html') + + params = {'title': 'My second nodetype', + 'content': 'This is the second nodetype of my tests.', + 'tags': 'gstudio, test', + 'status': PUBLISHED, + 'slug': 'my-second-nodetype'} + site = Site.objects.get_current() + second_nodetype = Nodetype.objects.create(**params) + second_nodetype.sites.add(site) + + source_context = Context({'object': second_nodetype}) + context = get_similar_nodetypes(source_context, 3, + 'custom_template.html', + flush=True) + self.assertEquals(len(context['nodetypes']), 1) + self.assertEquals(context['template'], 'custom_template.html') + + def test_get_archives_nodetypes(self): + context = get_archives_nodetypes() + self.assertEquals(len(context['archives']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/archives_nodetypes.html') + + self.publish_nodetype() + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'tags': 'gstudio, test', + 'status': PUBLISHED, + 'creation_date': datetime(2009, 1, 1), + 'slug': 'my-second-nodetype'} + site = Site.objects.get_current() + second_nodetype = Nodetype.objects.create(**params) + second_nodetype.sites.add(site) + + context = get_archives_nodetypes('custom_template.html') + self.assertEquals(len(context['archives']), 2) + self.assertEquals(context['archives'][0], datetime(2010, 1, 1)) + self.assertEquals(context['archives'][1], datetime(2009, 1, 1)) + self.assertEquals(context['template'], 'custom_template.html') + + def test_get_archives_tree(self): + context = get_archives_nodetypes_tree() + self.assertEquals(len(context['archives']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/archives_nodetypes_tree.html') + + self.publish_nodetype() + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'tags': 'gstudio, test', + 'status': PUBLISHED, + 'creation_date': datetime(2009, 1, 10), + 'slug': 'my-second-nodetype'} + site = Site.objects.get_current() + second_nodetype = Nodetype.objects.create(**params) + second_nodetype.sites.add(site) + + context = get_archives_nodetypes_tree('custom_template.html') + self.assertEquals(len(context['archives']), 2) + self.assertEquals(context['archives'][0], datetime(2009, 1, 10)) + self.assertEquals(context['archives'][1], datetime(2010, 1, 1)) + self.assertEquals(context['template'], 'custom_template.html') + + def test_get_calendar_nodetypes(self): + source_context = Context() + context = get_calendar_nodetypes(source_context) + self.assertEquals(context['previous_month'], None) + self.assertEquals(context['next_month'], None) + self.assertEquals(context['template'], 'gstudio/tags/calendar.html') + + self.publish_nodetype() + context = get_calendar_nodetypes(source_context, + template='custom_template.html') + self.assertEquals(context['previous_month'], datetime(2010, 1, 1)) + self.assertEquals(context['next_month'], None) + self.assertEquals(context['template'], 'custom_template.html') + + context = get_calendar_nodetypes(source_context, 2009, 1) + self.assertEquals(context['previous_month'], None) + self.assertEquals(context['next_month'], datetime(2010, 1, 1)) + + source_context = Context({'month': datetime(2009, 1, 1)}) + context = get_calendar_nodetypes(source_context) + self.assertEquals(context['previous_month'], None) + self.assertEquals(context['next_month'], datetime(2010, 1, 1)) + + source_context = Context({'month': datetime(2010, 1, 1)}) + context = get_calendar_nodetypes(source_context) + self.assertEquals(context['previous_month'], None) + self.assertEquals(context['next_month'], None) + + params = {'title': 'My second nodetype', + 'content': 'My second content', + 'tags': 'gstudio, test', + 'status': PUBLISHED, + 'creation_date': datetime(2008, 1, 1), + 'slug': 'my-second-nodetype'} + site = Site.objects.get_current() + second_nodetype = Nodetype.objects.create(**params) + second_nodetype.sites.add(site) + + source_context = Context() + context = get_calendar_nodetypes(source_context, 2009, 1) + self.assertEquals(context['previous_month'], datetime(2008, 1, 1)) + self.assertEquals(context['next_month'], datetime(2010, 1, 1)) + context = get_calendar_nodetypes(source_context) + self.assertEquals(context['previous_month'], datetime(2010, 1, 1)) + self.assertEquals(context['next_month'], None) + + def test_get_recent_comments(self): + site = Site.objects.get_current() + context = get_recent_comments() + self.assertEquals(len(context['comments']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/recent_comments.html') + + comment_1 = comments.get_model().objects.create( + comment='My Comment 1', site=site, + content_object=self.nodetype) + context = get_recent_comments(3, 'custom_template.html') + self.assertEquals(len(context['comments']), 0) + self.assertEquals(context['template'], 'custom_template.html') + + self.publish_nodetype() + context = get_recent_comments() + self.assertEquals(len(context['comments']), 1) + + author = User.objects.create_user(username='webmaster', + email='webmaster@example.com') + comment_2 = comments.get_model().objects.create( + comment='My Comment 2', site=site, + content_object=self.nodetype) + comment_2.flags.create(user=author, + flag=CommentFlag.MODERATOR_APPROVAL) + context = get_recent_comments() + self.assertEquals(list(context['comments']), [comment_2, comment_1]) + + def test_get_recent_linkbacks(self): + user = User.objects.create_user(username='webmaster', + email='webmaster@example.com') + site = Site.objects.get_current() + context = get_recent_linkbacks() + self.assertEquals(len(context['linkbacks']), 0) + self.assertEquals(context['template'], + 'gstudio/tags/recent_linkbacks.html') + + linkback_1 = comments.get_model().objects.create( + comment='My Linkback 1', site=site, + content_object=self.nodetype) + linkback_1.flags.create(user=user, flag='pingback') + context = get_recent_linkbacks(3, 'custom_template.html') + self.assertEquals(len(context['linkbacks']), 0) + self.assertEquals(context['template'], 'custom_template.html') + + self.publish_nodetype() + context = get_recent_linkbacks() + self.assertEquals(len(context['linkbacks']), 1) + + linkback_2 = comments.get_model().objects.create( + comment='My Linkback 2', site=site, + content_object=self.nodetype) + linkback_2.flags.create(user=user, flag='trackback') + context = get_recent_linkbacks() + self.assertEquals(list(context['linkbacks']), [linkback_2, linkback_1]) + + def test_gstudio_pagination(self): + class FakeRequest(object): + def __init__(self, get_dict): + self.GET = get_dict + + source_context = Context({'request': FakeRequest( + {'page': '1', 'key': 'val'})}) + paginator = Paginator(range(200), 10) + + context = gstudio_pagination(source_context, paginator.page(1)) + self.assertEquals(context['page'].number, 1) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [18, 19, 20]) + self.assertEquals(context['GET_string'], '&key=val') + self.assertEquals(context['template'], 'gstudio/tags/pagination.html') + + source_context = Context({'request': FakeRequest({})}) + context = gstudio_pagination(source_context, paginator.page(2)) + self.assertEquals(context['page'].number, 2) + self.assertEquals(context['begin'], [1, 2, 3, 4]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [18, 19, 20]) + self.assertEquals(context['GET_string'], '') + + context = gstudio_pagination(source_context, paginator.page(3)) + self.assertEquals(context['begin'], [1, 2, 3, 4, 5]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(6)) + self.assertEquals(context['begin'], [1, 2, 3, 4, 5, 6, 7, 8]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(11)) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], [9, 10, 11, 12, 13]) + self.assertEquals(context['end'], [18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(15)) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [13, 14, 15, 16, 17, 18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(18)) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [16, 17, 18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(19)) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [17, 18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(20)) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [18, 19, 20]) + + context = gstudio_pagination(source_context, paginator.page(10), + begin_pages=1, end_pages=3, + before_pages=4, after_pages=3, + template='custom_template.html') + self.assertEquals(context['begin'], [1]) + self.assertEquals(context['middle'], [6, 7, 8, 9, 10, 11, 12, 13]) + self.assertEquals(context['end'], [18, 19, 20]) + self.assertEquals(context['template'], 'custom_template.html') + + paginator = Paginator(range(50), 10) + context = gstudio_pagination(source_context, paginator.page(1)) + self.assertEquals(context['begin'], [1, 2, 3, 4, 5]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], []) + + paginator = Paginator(range(60), 10) + context = gstudio_pagination(source_context, paginator.page(1)) + self.assertEquals(context['begin'], [1, 2, 3, 4, 5, 6]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], []) + + paginator = Paginator(range(70), 10) + context = gstudio_pagination(source_context, paginator.page(1)) + self.assertEquals(context['begin'], [1, 2, 3]) + self.assertEquals(context['middle'], []) + self.assertEquals(context['end'], [5, 6, 7]) + + def test_gstudio_breadcrumbs(self): + class FakeRequest(object): + def __init__(self, path): + self.path = path + + source_context = Context({'request': FakeRequest('/')}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 1) + self.assertEquals(context['breadcrumbs'][0].name, 'Blog') + self.assertEquals(context['breadcrumbs'][0].url, + reverse('gstudio_nodetype_archive_index')) + self.assertEquals(context['separator'], '/') + self.assertEquals(context['template'], 'gstudio/tags/breadcrumbs.html') + + context = gstudio_breadcrumbs(source_context, + '>', 'Weblog', 'custom_template.html') + self.assertEquals(len(context['breadcrumbs']), 1) + self.assertEquals(context['breadcrumbs'][0].name, 'Weblog') + self.assertEquals(context['separator'], '>') + self.assertEquals(context['template'], 'custom_template.html') + + source_context = Context( + {'request': FakeRequest(self.nodetype.get_absolute_url()), + 'object': self.nodetype}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 5) + + cat_1 = Metatype.objects.create(title='Metatype 1', slug='metatype-1') + source_context = Context( + {'request': FakeRequest(cat_1.get_absolute_url()), + 'object': cat_1}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 3) + cat_2 = Metatype.objects.create(title='Metatype 2', slug='metatype-2', + parent=cat_1) + source_context = Context( + {'request': FakeRequest(cat_2.get_absolute_url()), + 'object': cat_2}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 4) + + tag = Tag.objects.get(name='test') + source_context = Context( + {'request': FakeRequest(reverse('gstudio_tag_detail', + args=['test'])), + 'object': tag}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 3) + + User.objects.create_user(username='webmaster', + email='webmaster@example.com') + author = Author.objects.get(username='webmaster') + source_context = Context( + {'request': FakeRequest(author.get_absolute_url()), + 'object': author}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 3) + + source_context = Context( + {'request': FakeRequest(reverse( + 'gstudio_nodetype_archive_year', args=[2011]))}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 2) + + source_context = Context({'request': FakeRequest(reverse( + 'gstudio_nodetype_archive_month', args=[2011, '03']))}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 3) + + source_context = Context({'request': FakeRequest(reverse( + 'gstudio_nodetype_archive_day', args=[2011, '03', 15]))}) + context = gstudio_breadcrumbs(source_context) + self.assertEquals(len(context['breadcrumbs']), 4) + # More tests can be done here, for testing path and objects in context + + def test_get_gravatar(self): + self.assertEquals( + get_gravatar('webmaster@example.com'), + 'http://www.gravatar.com/avatar/86d4fd4a22de452' + 'a9228298731a0b592.jpg?s=80&r=g') + self.assertEquals( + get_gravatar(' WEBMASTER@example.com ', 15, 'x', '404'), + 'http://www.gravatar.com/avatar/86d4fd4a22de452' + 'a9228298731a0b592.jpg?s=15&r=x&d=404') + + def test_get_tags(self): + Tag.objects.create(name='tag') + t = Template(""" + {% load gstudio_tags %} + {% get_tags as nodetype_tags %} + {{ nodetype_tags|join:", " }} + """) + html = t.render(Context()) + self.assertEquals(html.strip(), '') + self.publish_nodetype() + html = t.render(Context()) + self.assertEquals(html.strip(), 'test, gstudio') + + template_error_as = """ + {% load gstudio_tags %} + {% get_tags a_s nodetype_tags %}""" + self.assertRaises(TemplateSyntaxError, Template, template_error_as) + + template_error_args = """ + {% load gstudio_tags %} + {% get_tags as nodetype tags %}""" + self.assertRaises(TemplateSyntaxError, Template, template_error_args) + + def test_get_tag_cloud(self): + context = get_tag_cloud() + self.assertEquals(len(context['tags']), 0) + self.assertEquals(context['template'], 'gstudio/tags/tag_cloud.html') + self.publish_nodetype() + context = get_tag_cloud(6, 'custom_template.html') + self.assertEquals(len(context['tags']), 2) + self.assertEquals(context['template'], 'custom_template.html') diff --git a/gstudio/tests/url_shortener.py b/gstudio/tests/url_shortener.py new file mode 100644 index 0000000..7901d6a --- /dev/null +++ b/gstudio/tests/url_shortener.py @@ -0,0 +1,112 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's url_shortener""" +from __future__ import with_statement +import warnings + +from django.test import TestCase + +from gstudio.url_shortener import get_url_shortener +from gstudio import url_shortener as us_settings +from gstudio.url_shortener.backends.default import backend as default_backend + + +class URLShortenerTestCase(TestCase): + """Test cases for gstudio.url_shortener""" + + def setUp(self): + self.original_backend = us_settings.URL_SHORTENER_BACKEND + + def tearDown(self): + us_settings.URL_SHORTENER_BACKEND = self.original_backend + + def test_get_url_shortener(self): + us_settings.URL_SHORTENER_BACKEND = 'mymodule.myclass' + try: + with warnings.catch_warnings(record=True) as w: + self.assertEquals(get_url_shortener(), default_backend) + self.assertTrue(issubclass(w[-1].metatype, RuntimeWarning)) + self.assertEquals( + str(w[-1].message), + 'mymodule.myclass backend cannot be imported') + except AttributeError: + # Fail under Python2.5, because of'warnings.catch_warnings' + pass + + us_settings.URL_SHORTENER_BACKEND = 'gstudio.tests.custom_url_shortener' + try: + with warnings.catch_warnings(record=True) as w: + self.assertEquals(get_url_shortener(), default_backend) + self.assertTrue(issubclass(w[-1].metatype, RuntimeWarning)) + self.assertEquals( + str(w[-1].message), + 'This backend only exists for testing') + except AttributeError: + # Fail under Python2.5, because of'warnings.catch_warnings' + pass + + us_settings.URL_SHORTENER_BACKEND = 'gstudio.url_shortener'\ + '.backends.default' + self.assertEquals(get_url_shortener(), default_backend) diff --git a/gstudio/tests/urls.py b/gstudio/tests/urls.py new file mode 100644 index 0000000..96a34b0 --- /dev/null +++ b/gstudio/tests/urls.py @@ -0,0 +1,37 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test urls for the gstudio project""" +from django.contrib import admin +from django.conf.urls.defaults import url +from django.conf.urls.defaults import include +from django.conf.urls.defaults import patterns + +from gstudio.urls import urlpatterns + +admin.autodiscover() + +handler500 = 'django.views.defaults.server_error' +handler404 = 'django.views.defaults.page_not_found' + +urlpatterns += patterns( + '', + url(r'^channel-test/$', 'gstudio.views.channels.nodetype_channel', + {'query': 'test'}), + url(r'^comments/', include('django.contrib.comments.urls')), + url(r'^xmlrpc/$', 'django_xmlrpc.views.handle_xmlrpc'), + url(r'^admin/', include(admin.site.urls)), + ) diff --git a/gstudio/tests/utils.py b/gstudio/tests/utils.py new file mode 100644 index 0000000..230b6ef --- /dev/null +++ b/gstudio/tests/utils.py @@ -0,0 +1,90 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Utils for Gstudio's tests""" +import StringIO +from xmlrpclib import Transport + +from django.test.client import Client + + +class TestTransport(Transport): + """Handles connections to XML-RPC server + through Django test client.""" + + def __init__(self, *args, **kwargs): + Transport.__init__(self, *args, **kwargs) + self.client = Client() + + def request(self, host, handler, request_body, verbose=0): + self.verbose = verbose + response = self.client.post(handler, + request_body, + content_type="text/xml") + res = StringIO.StringIO(response.content) + setattr(res, 'getheader', lambda *args: '') # For Python >= 2.7 + res.seek(0) + if not hasattr(res, 'getheader'): + setattr(res, 'getheader', lambda *args: "") + return self.parse_response(res) diff --git a/gstudio/tests/views.py b/gstudio/tests/views.py new file mode 100644 index 0000000..8d55912 --- /dev/null +++ b/gstudio/tests/views.py @@ -0,0 +1,363 @@ +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# This project incorporates work covered by the following copyright and permission notice: + +# Copyright (c) 2009, Julien Fache +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright (c) 2011, 2012 Free Software Foundation + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +"""Test cases for Gstudio's views""" +from datetime import datetime + +from django.conf import settings +from django.test import TestCase +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.template import TemplateDoesNotExist +from django.utils.translation import ugettext_lazy as _ + +from gstudio.models import Nodetype +from gstudio.models import Metatype +from gstudio.managers import PUBLISHED +from gstudio.settings import PAGINATION + + +class ViewsBaseCase(TestCase): + """ + Setup and utility function base case. + """ + + def setUp(self): + self.old_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + self.old_TEMPLATE_LOADERS = settings.TEMPLATE_LOADERS + settings.TEMPLATE_LOADERS = ( + ('django.template.loaders.cached.Loader', ( + 'django.template.loaders.app_directories.Loader', + 'django.template.loaders.eggs.Loader', + ) + ), + ) + settings.TEMPLATE_CONTEXT_PROCESSORS = ( + 'django.core.context_processors.request', + ) + + self.site = Site.objects.get_current() + self.author = User.objects.create_superuser( + username='admin', email='admin@example.com', password='password') + self.metatype = Metatype.objects.create(title='Tests', slug='tests') + params = {'title': 'Test 1', + 'content': 'First test nodetype published', + 'slug': 'test-1', + 'tags': 'tests', + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + nodetype = Nodetype.objects.create(**params) + nodetype.sites.add(self.site) + nodetype.metatypes.add(self.metatype) + nodetype.authors.add(self.author) + + params = {'title': 'Test 2', + 'content': 'Second test nodetype published', + 'slug': 'test-2', + 'tags': 'tests', + 'creation_date': datetime(2010, 6, 1), + 'status': PUBLISHED} + nodetype = Nodetype.objects.create(**params) + nodetype.sites.add(self.site) + nodetype.metatypes.add(self.metatype) + nodetype.authors.add(self.author) + + def tearDown(self): + settings.TEMPLATE_CONTEXT_PROCESSORS = self.old_CONTEXT_PROCESSORS + settings.TEMPLATE_LOADERS = self.old_TEMPLATE_LOADERS + + def create_published_nodetype(self): + params = {'title': 'My test nodetype', + 'content': 'My test content', + 'slug': 'my-test-nodetype', + 'tags': 'tests', + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + nodetype = Nodetype.objects.create(**params) + nodetype.sites.add(self.site) + nodetype.metatypes.add(self.metatype) + nodetype.authors.add(self.author) + return nodetype + + def check_publishing_context(self, url, first_expected, + second_expected=None): + """Test the numbers of nodetypes in context of an url,""" + response = self.client.get(url) + self.assertEquals(len(response.context['object_list']), first_expected) + if second_expected: + self.create_published_nodetype() + response = self.client.get(url) + self.assertEquals( + len(response.context['object_list']), second_expected) + return response + + +class GstudioViewsTestCase(ViewsBaseCase): + """ + Test cases for generic views used in the application, + for reproducing and correcting issue : + http://github.com/gnowgi/django-gstudio/issues#issue/3 + """ + urls = 'gstudio.tests.urls' + + def test_gstudio_nodetype_archive_index(self): + self.check_publishing_context('/', 2, 3) + + def test_gstudio_nodetype_archive_year(self): + self.check_publishing_context('/2010/', 2, 3) + + def test_gstudio_nodetype_archive_month(self): + self.check_publishing_context('/2010/01/', 1, 2) + + def test_gstudio_nodetype_archive_day(self): + self.check_publishing_context('/2010/01/01/', 1, 2) + + def test_gstudio_nodetype_shortlink(self): + response = self.client.get('/1/', follow=True) + self.assertEquals(response.redirect_chain, + [('http://testserver/2010/01/01/test-1/', 301)]) + + def test_gstudio_nodetype_detail(self): + nodetype = self.create_published_nodetype() + nodetype.sites.clear() + # Check a 404 error, but the 404.html may no exist + try: + self.assertRaises(TemplateDoesNotExist, self.client.get, + '/2010/01/01/my-test-nodetype/') + except AssertionError: + response = self.client.get('/2010/01/01/my-test-nodetype/') + self.assertEquals(response.status_code, 404) + + nodetype.template = 'gstudio/_nodetype_detail.html' + nodetype.save() + nodetype.sites.add(Site.objects.get_current()) + response = self.client.get('/2010/01/01/my-test-nodetype/') + self.assertEquals(response.status_code, 200) + self.assertTemplateUsed(response, 'gstudio/_nodetype_detail.html') + + def test_gstudio_nodetype_detail_login(self): + nodetype = self.create_published_nodetype() + nodetype.login_required = True + nodetype.save() + response = self.client.get('/2010/01/01/my-test-nodetype/') + self.assertTemplateUsed(response, 'gstudio/login.html') + + def test_gstudio_nodetype_detail_password(self): + nodetype = self.create_published_nodetype() + nodetype.password = 'password' + nodetype.save() + response = self.client.get('/2010/01/01/my-test-nodetype/') + self.assertTemplateUsed(response, 'gstudio/password.html') + self.assertEquals(response.context['error'], False) + response = self.client.post('/2010/01/01/my-test-nodetype/', + {'password': 'bad_password'}) + self.assertTemplateUsed(response, 'gstudio/password.html') + self.assertEquals(response.context['error'], True) + response = self.client.post('/2010/01/01/my-test-nodetype/', + {'password': 'password'}) + self.assertEquals(response.status_code, 302) + + def test_gstudio_nodetype_channel(self): + self.check_publishing_context('/channel-test/', 2, 3) + + def test_gstudio_metatype_list(self): + self.check_publishing_context('/metatypes/', 1) + nodetype = Nodetype.objects.all()[0] + nodetype.metatypes.add(Metatype.objects.create(title='New metatype', + slug='new-metatype')) + self.check_publishing_context('/metatypes/', 2) + + def test_gstudio_metatype_detail(self): + response = self.check_publishing_context('/metatypes/tests/', 2, 3) + self.assertTemplateUsed(response, 'gstudio/metatype/nodetype_list.html') + self.assertEquals(response.context['metatype'].slug, 'tests') + + def test_gstudio_metatype_detail_paginated(self): + """Test case reproducing issue #42 on metatype + detail view paginated""" + for i in range(PAGINATION): + params = {'title': 'My nodetype %i' % i, + 'content': 'My content %i' % i, + 'slug': 'my-nodetype-%i' % i, + 'creation_date': datetime(2010, 1, 1), + 'status': PUBLISHED} + nodetype = Nodetype.objects.create(**params) + nodetype.sites.add(self.site) + nodetype.metatypes.add(self.metatype) + nodetype.authors.add(self.author) + response = self.client.get('/metatypes/tests/') + self.assertEquals(len(response.context['object_list']), PAGINATION) + response = self.client.get('/metatypes/tests/?page=2') + self.assertEquals(len(response.context['object_list']), 2) + response = self.client.get('/metatypes/tests/page/2/') + self.assertEquals(len(response.context['object_list']), 2) + self.assertEquals(response.context['metatype'].slug, 'tests') + + def test_gstudio_author_list(self): + self.check_publishing_context('/authors/', 1) + nodetype = Nodetype.objects.all()[0] + nodetype.authors.add(User.objects.create(username='new-user', + email='new_user@example.com')) + self.check_publishing_context('/authors/', 2) + + def test_gstudio_author_detail(self): + response = self.check_publishing_context('/authors/admin/', 2, 3) + self.assertTemplateUsed(response, 'gstudio/author/nodetype_list.html') + self.assertEquals(response.context['author'].username, 'admin') + + def test_gstudio_tag_list(self): + self.check_publishing_context('/tags/', 1) + nodetype = Nodetype.objects.all()[0] + nodetype.tags = 'tests, tag' + nodetype.save() + self.check_publishing_context('/tags/', 2) + + def test_gstudio_tag_detail(self): + response = self.check_publishing_context('/tags/tests/', 2, 3) + self.assertTemplateUsed(response, 'gstudio/tag/nodetype_list.html') + self.assertEquals(response.context['tag'].name, 'tests') + + def test_gstudio_nodetype_search(self): + self.check_publishing_context('/search/?pattern=test', 2, 3) + response = self.client.get('/search/?pattern=ab') + self.assertEquals(len(response.context['object_list']), 0) + self.assertEquals(response.context['error'], + _('The pattern is too short')) + response = self.client.get('/search/') + self.assertEquals(len(response.context['object_list']), 0) + self.assertEquals(response.context['error'], + _('No pattern to search found')) + + def test_gstudio_sitemap(self): + response = self.client.get('/sitemap/') + self.assertEquals(len(response.context['nodetypes']), 2) + self.assertEquals(len(response.context['metatypes']), 1) + nodetype = self.create_published_nodetype() + nodetype.metatypes.add(Metatype.objects.create(title='New metatype', + slug='new-metatype')) + response = self.client.get('/sitemap/') + self.assertEquals(len(response.context['nodetypes']), 3) + self.assertEquals(len(response.context['metatypes']), 2) + + def test_gstudio_trackback(self): + # Check a 404 error, but the 404.html may no exist + try: + self.assertRaises(TemplateDoesNotExist, self.client.post, + '/trackback/404/') + except AssertionError: + response = self.client.post('/trackback/404/') + self.assertEquals(response.status_code, 404) + self.assertEquals( + self.client.post('/trackback/1/').status_code, 301) + self.assertEquals( + self.client.get('/trackback/1/').status_code, 301) + nodetype = Nodetype.objects.get(slug='test-1') + nodetype.pingback_enabled = False + nodetype.save() + self.assertEquals( + self.client.post('/trackback/1/', + {'url': 'http://example.com'}).content, + '<?xml version="1.0" encoding="utf-8"?>\n<response>\n \n ' + '<error>1</error>\n <message>Trackback is not enabled for ' + 'Test 1</message>\n \n</response>\n') + nodetype.pingback_enabled = True + nodetype.save() + self.assertEquals( + self.client.post('/trackback/1/', + {'url': 'http://example.com'}).content, + '<?xml version="1.0" encoding="utf-8"?>\n<response>\n \n ' + '<error>0</error>\n \n</response>\n') + self.assertEquals( + self.client.post('/trackback/1/', + {'url': 'http://example.com'}).content, + '<?xml version="1.0" encoding="utf-8"?>\n<response>\n \n ' + '<error>1</error>\n <message>Trackback is already registered' + '</message>\n \n</response>\n') + + +class GstudioCustomDetailViews(ViewsBaseCase): + """ + Tests with an alternate urls.py that modifies how author_detail, + tags_detail and metatypes_detail views to be called with a custom + template_name keyword argument and an extra_context. + """ + urls = 'gstudio.tests.custom_views_detail_urls' + + def test_custom_metatype_detail(self): + response = self.check_publishing_context('/metatypes/tests/', 2, 3) + self.assertTemplateUsed(response, 'gstudio/nodetype_list.html') + self.assertEquals(response.context['metatype'].slug, 'tests') + self.assertEquals(response.context['extra'], 'context') + + def test_custom_author_detail(self): + response = self.check_publishing_context('/authors/admin/', 2, 3) + self.assertTemplateUsed(response, 'gstudio/nodetype_list.html') + self.assertEquals(response.context['author'].username, 'admin') + self.assertEquals(response.context['extra'], 'context') + + def test_custom_tag_detail(self): + response = self.check_publishing_context('/tags/tests/', 2, 3) + self.assertTemplateUsed(response, 'gstudio/nodetype_list.html') + self.assertEquals(response.context['tag'].name, 'tests') + self.assertEquals(response.context['extra'], 'context') |