Skip to content

Commit b7ad117

Browse files
committed
Added tests for disallowed characters in tags, abudida tag handling, invalid data types in tags, duplicate tag scenarios, and unique_together scenarios. Changed invalid tag tests to use a call to TagsSerializerField. Used TaggableManager and Resource calls for other duplicate and unique_together scenarios.
1 parent c2d08c2 commit b7ad117

File tree

1 file changed

+170
-37
lines changed

1 file changed

+170
-37
lines changed

project/tagging/tests.py

+170-37
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,210 @@
11
import unittest
2-
from django.core.exceptions import ValidationError
2+
from rest_framework.exceptions import ValidationError
3+
from resources.factories import ResourceFactory
4+
from factory import PostGenerationMethodCall, LazyAttribute, create, create_batch
35
from django.test import TestCase
4-
from .models import CustomTag
6+
from taggit.managers import TaggableManager
7+
from .models import CustomTag, TaggedItems
8+
from tagging.managers import CustomTaggableManager
9+
from .serializers import TagsSerializerField
10+
511

612

713
class CustomTagTests(TestCase):
8-
def test_valid_slugs(self):
14+
15+
def setUp(self):
16+
self.resource_1, self.resource_2 = create_batch(ResourceFactory, 2)
17+
18+
19+
def test_unicode_slugify(self):
920
test_tags = [
1021
{"name": "programming", "expected_slug": "programming"},
22+
{"name": "PROGRAMMING", "expected_slug": "programming"},
1123
{"name": "PyCon", "expected_slug": "pycon"},
24+
{"name": "PYCON", "expected_slug": "pycon"},
1225
{"name": "local storage", "expected_slug": "local-storage"},
26+
{"name": "local-storage", "expected_slug": "local-storage"},
27+
{"name": "local/storage", "expected_slug": "localstorage"},
1328
{"name": "PEN testing", "expected_slug": "pen-testing"},
1429
{"name": "תִּיכְנוּת", "expected_slug": "תיכנות"},
1530
{"name": " 프로그램 작성", "expected_slug": "프로그램-작성"},
1631
{"name": "程式设计", "expected_slug": "程式设计"},
1732
{"name": "برمجة", "expected_slug": "برمجة"},
18-
{"name": "आनंद", "expected_slug": "आनद"},
33+
{"name": "आनंद", "expected_slug": "आनंद"},
1934
{"name": "лягушачий", "expected_slug": "лягушачий"},
35+
{"name":"Система управления базами данных", "expected_slug": "система-управления-базами-данных"},
2036
{"name": "教程", "expected_slug": "教程"},
21-
{"name": "Inicio r\u00e1pido", "expected_slug": "inicio-r\u00e1pido"},
37+
{"name": "Inicio ápido", "expected_slug": "inicio-ápido"},
2238
{"name": "最后", "expected_slug": "最后"},
2339
{"name": " 欲求不満", "expected_slug": "欲求不満"},
24-
{"name": "စမ်းသပ်ခြင်း", "expected_slug": "စမသပခင"},
25-
{"name": "ฐานข้อมูล", "expected_slug": "ฐานขอมล"},
40+
{"name":"数据库 管理 系统", "expected_slug":"数据库-管理-系统"},
41+
{"name": "စမ်းသပ်ခြင်း", "expected_slug": "စမ်းသပ်ခြင်း"},
42+
{"name": "ฐานข้อมูล", "expected_slug": "ฐานข้อมูล"},
2643
{"name": "основы", "expected_slug": "основы"},
2744
{"name": "אַלגערידאַם", "expected_slug": "אלגערידאם"},
2845
{"name": "自動化する", "expected_slug": "自動化する"},
46+
{"name":"מאגר מידע ניהול מערכת", "expected_slug":"מאגר-מידע-ניהול-מערכת"},
2947
{"name": "sjálfvirkan", "expected_slug": "sjálfvirkan"},
3048
{"name": "پژوهش ", "expected_slug": "پژوهش"},
31-
{"name": " గ్రాఫ్", "expected_slug": "గరఫ"},
49+
{"name": " గ్రాఫ్", "expected_slug": "గ్రాఫ్"},
3250
{"name": "데이터 베이스", "expected_slug": "데이터-베이스"},
3351
{"name": "stòran-dàta", "expected_slug": "stòran-dàta"},
52+
{"name": "የመረጃ ቋት አስተዳደር ስርዓት", "expected_slug": "የመረጃ-ቋት-አስተዳደር-ስርዓት"}
3453
]
3554

3655
for entry in test_tags:
3756
tag = CustomTag(name=entry["name"])
3857
tag.save()
3958
self.assertEqual(tag.slug, entry["expected_slug"])
4059

41-
@unittest.skip('https://github.com/codebuddies/backend/issues/123')
42-
def test_brahmic_abugida_slugs(self):
60+
def test_brahmic_abugida_slugify(self):
4361
test_tags = [
44-
{"name": "हिंदी में जानकारी", "expected_slug": "TODO"},
45-
{"name": "प्रयास है", "expected_slug": "TODO"},
46-
{"name": "స్వయంచాలక", "expected_slug": "TODO"},
47-
]
62+
{"name": "কর্মক্ষমতা পরীক্ষামূলক ", "expected_slug": "কর্মক্ষমতা-পরীক্ষামূলক"}, #Bangla
63+
{"name": "ડેટાબેઝ મેનેજમેન્ટ સિસ્ટમ", "expected_slug": "ડેટાબેઝ-મેનેજમેન્ટ-સિસ્ટમ"}, #Gujarati
64+
{"name": " हिंदी में जानकारी ", "expected_slug": "हिंदी-में-जानकारी"}, #Hindi
65+
{"name": "ಡೇಟಾಬೇಸ್ ನಿರ್ವಹಣಾ ವ್ಯವಸ್ಥೆ", "expected_slug": "ಡೇಟಾಬೇಸ್-ನಿರ್ವಹಣಾ-ವ್ಯವಸ್ಥೆ"}, #Kannada
66+
{"name": "ការសម្តែង ការសាកល្បង", "expected_slug": "ការសម្តែង-ការសាកល្បង"}, #Khmer
67+
{"name": "ການປະຕິບັດ ການທົດສອບ", "expected_slug": "ການປະຕິບັດ-ການທົດສອບ"}, #Lao
68+
{"name": " စွမ်းဆောင်ရည် စမ်းသပ်ခြင်း ", "expected_slug": "စွမ်းဆောင်ရည်-စမ်းသပ်ခြင်း"}, #Myanmar
69+
{"name": " စွမ်းဆောင်ရည် စမ်းသပ်ခြင်း ", "expected_slug": "စွမ်းဆောင်ရည်-စမ်းသပ်ခြင်း"}, #Myanmar ex space
70+
{"name": "പ്രകടനം പരിശോധിക്കുന്നു","expected_slug": "പ്രകടനം-പരിശോധിക്കുന്നു" }, #Malayalam
71+
{"name":"කාර්ය සාධනය පරීක්ෂා කිරීම","expected_slug":"කාර්ය-සාධනය-පරීක්ෂා-කිරීම" }, #Sinhala
72+
{"name":"தரவுத்தள மேலாண்மை அமைப்பு","expected_slug":"தரவுத்தள-மேலாண்மை-அமைப்பு"}, #Tamil
73+
{"name": "స్వయంచాలక", "expected_slug": "స్వయంచాలక"}, #Telugu
74+
{"name": "ระบบจัดการฐานข้อมูล", "expected_slug": "ระบบจัดการฐานข้อมูล"}, #Thai
75+
]
4876

4977
for entry in test_tags:
5078
tag = CustomTag(name=entry["name"])
5179
tag.save()
5280
self.assertEqual(tag.slug, entry["expected_slug"])
5381

54-
def test_invalid_slugs(self):
55-
invalid_tag_names = [
56-
"❤🐸",
57-
"🐸",
58-
" %",
59-
"//",
82+
def test_invalid_tag_characters(self):
83+
disallowed = [
84+
[" हिंदी 🐙में👌 जानकारी 🌄"], #emoji
85+
["ડેટાબેઝ 😀 મેનેજમેન્ટ 😶 સિસ્ટમ😿 "], #emoticons
86+
["စွမ်းဆောင်ရည် 🙵 စမ်းသ❡❢❣❤❥❦❧ပ်ခြင်း🙗🙞"], #dingbats
87+
["🇳🇪ಡೇಟಾಬೇಸ್🇨🇱 ನಿರ್ವಹಣಾ ವ್ಯವಸ್ಥೆ🇲🇩"], #flags
88+
["⑵데Ⓚ이터➈ 베이스⒮ⓑ"], #enclosed letters and numbers
89+
["⅀欲𝛀求𝜳不𝝫満"], #math symbols
90+
["ля♋гуш🈺ачи♓й"], #other symbols
91+
["⇪אַלגע↪ריד↹אַם⇜⇝"], #arrows
92+
["Inicio ♚🂊🂋🁍🁎🁮ápido🁯🁰🀂🀃🀄🀅🀆"], #game symbols
93+
["s𝆹𝅥𝅯𝆺𝆺𝅥𝆺𝅥𝅮jálfv𝅘𝅥𝅱𝅘𝅥𝅲𝅙𝅚𝅛irkan𝇜"] #music symbols
94+
]
95+
serializer = TagsSerializerField(model_field='tags')
96+
97+
for tag in disallowed:
98+
with self.assertRaisesMessage(ValidationError, 'Emoji, pictograps, and symbols are not supported in tags.' ):
99+
serializer.validate(tag)
100+
101+
def test_nonstring_tag(self):
102+
disallowed = [
103+
[1, "float"],
104+
["butterfly", ["toast", "chicken"]],
105+
["peanut", ("sunshine",)]
106+
]
107+
serializer = TagsSerializerField(model_field='tags')
108+
109+
for tag in disallowed:
110+
with self.assertRaisesMessage(ValidationError, 'All tags must be of type str.'):
111+
serializer.validate(tag)
112+
113+
def test_nonlist_tag(self):
114+
disallowed = [1, "float", ("sunshine","buttercup"), {"loser": "wins"}]
115+
serializer = TagsSerializerField(model_field='tags')
116+
117+
for tag in disallowed:
118+
with self.assertRaisesMessage(ValidationError, 'Expected a list of tag names but got type '):
119+
serializer.validate(tag)
120+
121+
def test_duplicate_slug_handling(self):
122+
'''
123+
See https://github.com/jazzband/django-taggit/issues/448#issuecomment-414474054 &
124+
https://github.com/wagtail/wagtail/issues/4786#issuecomment-426436030 for the expected behavior when
125+
django-taggit has TAGGIT_CASE_INSENSITIVE=True.
126+
'''
127+
128+
starter_tags = [
129+
{"name": "programming", "expected_slug": "programming", "expected_name": "programming"},
130+
{"name": "PyCon", "expected_slug": "pycon", "expected_name": "PyCon"},
131+
{"name": "local storage", "expected_slug": "local-storage", "expected_name": "local storage"},
132+
{"name": "PEN testing", "expected_slug": "pen-testing", "expected_name": "PEN testing"},
133+
{"name": "database system", "expected_slug": "database-system", "expected_name": "database system"},
134+
]
135+
136+
duped_tags = [
137+
# DUPE: all entries below should hand back the starter_tag 'programming' tag
138+
{"name": "PROGRAMMING", "expected_slug": "programming", "expected_name": "programming"},
139+
{"name": "PrOgRaMmInG", "expected_slug": "programming", "expected_name": "programming"},
140+
{"name": "PROgramming", "expected_slug": "programming", "expected_name": "programming"},
141+
142+
# DUPE: all entries below should hand back the starter_tag 'pycon' tag
143+
{"name": "PyCon", "expected_slug": "pycon", "expected_name": "PyCon"},
144+
{"name": "PYCON", "expected_slug": "pycon", "expected_name": "PyCon"},
145+
{"name": "PYcon", "expected_slug": "pycon", "expected_name": "PyCon"},
146+
147+
# DUPE: all entries below should hand back starter_tag 'local-storage' tag
148+
{"name": "LOCAL STORAGE", "expected_slug": "local-storage", "expected_name": "local storage"},
149+
{"name": "local storage", "expected_slug": "local-storage", "expected_name": "local storage"},
150+
{"name": "lOcal Storage", "expected_slug": "localstorage", "expected_name": "local storage"},
151+
{"name": "Local Storage", "expected_slug": "localstorage", "expected_name": "local storage"},
152+
153+
# DUPE: all entries below should hand back starter_tag 'pen-testing' tag
154+
{"name": "pen testing", "expected_slug": "pen-testing", "expected_name": "PEN testing"},
155+
{"name": "PEN TESTING", "expected_slug": "pen-testing", "expected_name": "PEN testing"},
156+
{"name": "pen TESTING", "expected_slug": "pen-testing", "expected_name": "PEN testing"},
157+
{"name": "pEn tEstIng", "expected_slug": "pen-testing", "expected_name": "PEN testing"},
158+
159+
#DUPE: all entries below should hand back starter_tag 'database-system' tag
160+
{"name": "DATABASE SYSTEM", "expected_slug": "database-system", "expected_name": "database system"},
161+
{"name": "Database System", "expected_slug": "database-system", "expected_name": "database system"},
162+
{"name": "Database SYSTEM", "expected_slug": "database-system", "expected_name": "database system"},
163+
{"name": "dAtAbAsE sYsTeM", "expected_slug": "database-system", "expected_name": "database system"},
164+
]
165+
166+
#create tags for resource_1 and resource_2
167+
self.resource_1.tags.add(*(tag['name'] for tag in starter_tags))
168+
self.resource_2.tags.add(*(tag['name'] for tag in duped_tags))
169+
170+
#check that the tags created and attached to resource_1 are the expected format
171+
self.assertEqual(sorted(self.resource_1.tags.names()), sorted(tag['expected_name'] for tag in starter_tags))
172+
self.assertEqual(sorted(self.resource_1.tags.slugs()), sorted(tag['expected_slug'] for tag in starter_tags))
173+
174+
#check that the tags for resource_2 use the tags created for resource_1, because they match case-insesitively
175+
self.assertEqual(sorted(self.resource_1.tags.names()), sorted(self.resource_2.tags.names()))
176+
self.assertEqual(sorted(self.resource_1.tags.slugs()), sorted(self.resource_2.tags.slugs()))
177+
178+
179+
def test_unique_together_name_slug_pairs(self):
180+
'''
181+
See https://github.com/jazzband/django-taggit/issues/448#issuecomment-414474054 &
182+
https://github.com/wagtail/wagtail/issues/4786#issuecomment-426436030 for the expected behavior when
183+
django-taggit has TAGGIT_CASE_INSENSITIVE=True.
184+
'''
185+
186+
unique_together_tags = [
187+
#These values slug to the same thing but the name is unique, so the two are unique together & are saved.
188+
{"name": "Database: System", "expected_slug": "database-system", "expected_name": "Database: System"},
189+
{"name": "Database/ System", "expected_slug": "database-system", "expected_name": "Database/ System"},
190+
{"name": "Database-System", "expected_slug": "database-system", "expected_name": "Database-System"},
191+
192+
{"name": "pro/gramming", "expected_slug": "programming", "expected_name": "pro/gramming"},
193+
{"name": "pro*gramming", "expected_slug": "programming", "expected_name": "pro*gramming"},
194+
195+
{"name": "PRO: GRAMMING", "expected_slug": "pro-gramming", "expected_name": "PRO: GRAMMING"},
196+
{"name": "pro gramming", "expected_slug": "pro-gramming", "expected_name": "pro gramming"},
197+
198+
{"name": "Local-Storage", "expected_slug": "local-storage", "expected_name": "Local-Storage"},
199+
{"name": "Local :Storage", "expected_slug": "local-storage", "expected_name": "Local :Storage"},
200+
201+
{"name": "Local/Storage", "expected_slug": "localstorage", "expected_name": "Local/Storage"},
202+
{"name": "*Local Storage*", "expected_slug": "local-storage", "expected_name": "*Local Storage*"},
60203
]
61-
for name in invalid_tag_names:
62-
with self.assertRaises(ValidationError):
63-
tag = CustomTag(name=name)
64-
tag.save()
65-
66-
def test_duplicates(self):
67-
tag1 = CustomTag(name='javascript')
68-
tag1.save()
69-
70-
# fail if we try to generate more tags with the same slug
71-
with self.assertRaises(ValidationError):
72-
tag2 = CustomTag(name='Javascript')
73-
tag2.save()
74-
75-
with self.assertRaises(ValidationError):
76-
tag3 = CustomTag(name='JaVascripT%/')
77-
tag3.save()
204+
205+
#add these new tags to resource_1, check to see they are added and *not* treated as dupes
206+
self.resource_1.tags.add(*(tag['name'] for tag in unique_together_tags))
207+
tagging_results = sorted((tag['name'], tag["slug"]) for tag in self.resource_1.tags.all_fields())
208+
209+
self.assertEqual(tagging_results,
210+
sorted((tag['expected_name'], tag['expected_slug']) for tag in unique_together_tags))

0 commit comments

Comments
 (0)