summaryrefslogtreecommitdiff
path: root/gstudio/tests
diff options
context:
space:
mode:
authorgnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
committergnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
commit7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch)
treed2afc3463fd49625a9be482012f5c3bfcf7c42b9 /gstudio/tests
downloadgnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz
master trunk of gnowsys-studio
Diffstat (limited to 'gstudio/tests')
-rw-r--r--gstudio/tests/__init__.py121
-rw-r--r--gstudio/tests/admin.py151
-rw-r--r--gstudio/tests/comparison.py115
-rw-r--r--gstudio/tests/custom_spam_checker.py75
-rw-r--r--gstudio/tests/custom_url_shortener.py75
-rw-r--r--gstudio/tests/custom_views_detail_urls.py108
-rw-r--r--gstudio/tests/feeds.py360
-rw-r--r--gstudio/tests/managers.py313
-rw-r--r--gstudio/tests/metatype.py116
-rw-r--r--gstudio/tests/metaweblog.py360
-rw-r--r--gstudio/tests/moderator.py169
-rw-r--r--gstudio/tests/nodetype.py348
-rw-r--r--gstudio/tests/ping.py187
-rw-r--r--gstudio/tests/pingback.py253
-rw-r--r--gstudio/tests/quick_nodetype.py103
-rw-r--r--gstudio/tests/signals.py137
-rw-r--r--gstudio/tests/sitemaps.py128
-rw-r--r--gstudio/tests/spam_checker.py40
-rw-r--r--gstudio/tests/templates/gstudio/_nodetype_detail.html3
-rw-r--r--gstudio/tests/templates/gstudio/base.html9
-rw-r--r--gstudio/tests/templates/gstudio/nodetype_detail.html7
-rw-r--r--gstudio/tests/templates/gstudio/nodetype_list.html10
-rw-r--r--gstudio/tests/templates/gstudio/nodetype_search.html1
-rw-r--r--gstudio/tests/templatetags.py590
-rw-r--r--gstudio/tests/url_shortener.py112
-rw-r--r--gstudio/tests/urls.py37
-rw-r--r--gstudio/tests/utils.py90
-rw-r--r--gstudio/tests/views.py363
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&amp;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&amp;r=g')
+ self.assertEquals(
+ get_gravatar(' WEBMASTER@example.com ', 15, 'x', '404'),
+ 'http://www.gravatar.com/avatar/86d4fd4a22de452'
+ 'a9228298731a0b592.jpg?s=15&amp;r=x&amp;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')