|
1 | 1 | 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 |
3 | 5 | 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 | + |
5 | 11 |
|
6 | 12 |
|
7 | 13 | 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): |
9 | 20 | test_tags = [
|
10 | 21 | {"name": "programming", "expected_slug": "programming"},
|
| 22 | + {"name": "PROGRAMMING", "expected_slug": "programming"}, |
11 | 23 | {"name": "PyCon", "expected_slug": "pycon"},
|
| 24 | + {"name": "PYCON", "expected_slug": "pycon"}, |
12 | 25 | {"name": "local storage", "expected_slug": "local-storage"},
|
| 26 | + {"name": "local-storage", "expected_slug": "local-storage"}, |
| 27 | + {"name": "local/storage", "expected_slug": "localstorage"}, |
13 | 28 | {"name": "PEN testing", "expected_slug": "pen-testing"},
|
14 | 29 | {"name": "תִּיכְנוּת", "expected_slug": "תיכנות"},
|
15 | 30 | {"name": " 프로그램 작성", "expected_slug": "프로그램-작성"},
|
16 | 31 | {"name": "程式设计", "expected_slug": "程式设计"},
|
17 | 32 | {"name": "برمجة", "expected_slug": "برمجة"},
|
18 |
| - {"name": "आनंद", "expected_slug": "आनद"}, |
| 33 | + {"name": "आनंद", "expected_slug": "आनंद"}, |
19 | 34 | {"name": "лягушачий", "expected_slug": "лягушачий"},
|
| 35 | + {"name":"Система управления базами данных", "expected_slug": "система-управления-базами-данных"}, |
20 | 36 | {"name": "教程", "expected_slug": "教程"},
|
21 |
| - {"name": "Inicio r\u00e1pido", "expected_slug": "inicio-r\u00e1pido"}, |
| 37 | + {"name": "Inicio ápido", "expected_slug": "inicio-ápido"}, |
22 | 38 | {"name": "最后", "expected_slug": "最后"},
|
23 | 39 | {"name": " 欲求不満", "expected_slug": "欲求不満"},
|
24 |
| - {"name": "စမ်းသပ်ခြင်း", "expected_slug": "စမသပခင"}, |
25 |
| - {"name": "ฐานข้อมูล", "expected_slug": "ฐานขอมล"}, |
| 40 | + {"name":"数据库 管理 系统", "expected_slug":"数据库-管理-系统"}, |
| 41 | + {"name": "စမ်းသပ်ခြင်း", "expected_slug": "စမ်းသပ်ခြင်း"}, |
| 42 | + {"name": "ฐานข้อมูล", "expected_slug": "ฐานข้อมูล"}, |
26 | 43 | {"name": "основы", "expected_slug": "основы"},
|
27 | 44 | {"name": "אַלגערידאַם", "expected_slug": "אלגערידאם"},
|
28 | 45 | {"name": "自動化する", "expected_slug": "自動化する"},
|
| 46 | + {"name":"מאגר מידע ניהול מערכת", "expected_slug":"מאגר-מידע-ניהול-מערכת"}, |
29 | 47 | {"name": "sjálfvirkan", "expected_slug": "sjálfvirkan"},
|
30 | 48 | {"name": "پژوهش ", "expected_slug": "پژوهش"},
|
31 |
| - {"name": " గ్రాఫ్", "expected_slug": "గరఫ"}, |
| 49 | + {"name": " గ్రాఫ్", "expected_slug": "గ్రాఫ్"}, |
32 | 50 | {"name": "데이터 베이스", "expected_slug": "데이터-베이스"},
|
33 | 51 | {"name": "stòran-dàta", "expected_slug": "stòran-dàta"},
|
| 52 | + {"name": "የመረጃ ቋት አስተዳደር ስርዓት", "expected_slug": "የመረጃ-ቋት-አስተዳደር-ስርዓት"} |
34 | 53 | ]
|
35 | 54 |
|
36 | 55 | for entry in test_tags:
|
37 | 56 | tag = CustomTag(name=entry["name"])
|
38 | 57 | tag.save()
|
39 | 58 | self.assertEqual(tag.slug, entry["expected_slug"])
|
40 | 59 |
|
41 |
| - @unittest.skip('https://github.com/codebuddies/backend/issues/123') |
42 |
| - def test_brahmic_abugida_slugs(self): |
| 60 | + def test_brahmic_abugida_slugify(self): |
43 | 61 | 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 | + ] |
48 | 76 |
|
49 | 77 | for entry in test_tags:
|
50 | 78 | tag = CustomTag(name=entry["name"])
|
51 | 79 | tag.save()
|
52 | 80 | self.assertEqual(tag.slug, entry["expected_slug"])
|
53 | 81 |
|
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*"}, |
60 | 203 | ]
|
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