Completed
Branch master (e3faea)
by Koen
01:21
created

TestValidation   F

Complexity

Total Complexity 112

Size/Duplication

Total Lines 966
Duplicated Lines 51.66 %
Metric Value
wmc 112
dl 499
loc 966
rs 1.263

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TestValidation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
import unittest
3
4
from sqlalchemy.orm.exc import NoResultFound
5
from atramhasis.data.datamanagers import SkosManager, LanguagesManager, ConceptSchemeManager
6
from atramhasis.errors import ValidationError
7
8
9
try:
10
    from unittest.mock import Mock
11
except ImportError:
12
    from mock import Mock  # pragma: no cover
13
import colander
14
from pyramid import testing
15
from skosprovider_sqlalchemy.models import Concept, Collection, LabelType, Language, Thing
16
from atramhasis.validators import (
17
    ConceptScheme as ConceptSchemeSchema,
18
    Concept as ConceptSchema,
19
    concept_schema_validator,
20
    conceptscheme_schema_validator,
21
    LanguageTag, languagetag_validator)
22
23
24
def filter_by_mock_language(id):
25
    filter_mock = Mock()
26
    if id in ['af', 'flup']:
27
        filter_mock.count = Mock(return_value=0)
28
    else:
29
        filter_mock.count = Mock(return_value=1)
30
    return filter_mock
31
32
33
def filter_by_mock_concept(concept_id, conceptscheme_id):
34
    concept = Concept(concept_id=concept_id, conceptscheme_id=conceptscheme_id)
35
    concept.type = 'concept'
36
    if concept_id == 2:
37
        broader_concept = Concept(concept_id=1, conceptscheme_id=conceptscheme_id)
38
        broader_concepts = set()
39
        broader_concepts.add(broader_concept)
40
        concept.broader_concepts = broader_concepts
41
    if concept_id == 7:
42
        narrower_concept = Concept(concept_id=14, conceptscheme_id=conceptscheme_id)
43
        narrower_concepts = set()
44
        narrower_concepts.add(narrower_concept)
45
        concept.narrower_concepts = narrower_concepts
46
    if concept_id == 666:
47
        concept = Collection(concept_id=concept_id, conceptscheme_id=conceptscheme_id)
48
        concept.type = 'collection'
49
    if concept_id == 667:
50
        concept = Collection(concept_id=concept_id, conceptscheme_id=conceptscheme_id)
51
        concept.type = 'collection'
52
        memberof = Collection(concept_id=60, conceptscheme_id=conceptscheme_id)
53
        memberofs = set()
54
        memberofs.add(memberof)
55
        concept.member_of = memberofs
56
    if concept_id == 62:
57
        concept = Collection(concept_id=concept_id, conceptscheme_id=conceptscheme_id)
58
        concept.type = 'collection'
59
        member = Collection(concept_id=666, conceptscheme_id=conceptscheme_id)
60
        members = set()
61
        members.add(member)
62
        concept.members = members
63
    if concept_id == 777:
64
        if conceptscheme_id != 3:
65
            raise NoResultFound()
66
    filter_mock = Mock()
67
    filter_mock.one = Mock(return_value=concept)
68
    return filter_mock
69
70
71
def list_all_types():
72
    types = [LabelType('hiddenLabel', 'A hidden label.'), LabelType('altLabel', 'An alternative label.'),
73
             LabelType('prefLabel', 'A preferred label.')]
74
    return types
75
76
77
def list_all_languages():
78
    languages = [Language('nl', 'Dutch'), Language('en', 'English')]
79
    return languages
80
81
82
def create_query_mock(some_class):
83
    query_mock = Mock()
84
    if some_class in [Concept, Collection, Thing]:
85
        query_mock.filter_by = Mock(side_effect=filter_by_mock_concept)
86
    elif some_class == Language:
87
        query_mock.filter_by = Mock(side_effect=filter_by_mock_language)
88
    if some_class == LabelType:
89
        query_mock.all = Mock(side_effect=list_all_types)
90
    if some_class == Language:
91
        query_mock.all = Mock(side_effect=list_all_languages)
92
    return query_mock
93
94
95
def session_maker():
96
    session_mock = Mock()
97
    session_mock.query = Mock(side_effect=create_query_mock)
98
    return session_mock
99
100
101
class TestValidation(unittest.TestCase):
102
    def setUp(self):
103
        self.config = testing.setUp()
104
        self.request = testing.DummyRequest()
105
        session = session_maker()
106
        self.request.data_managers = {'skos_manager': SkosManager(session),
107
                                      'conceptscheme_manager': ConceptSchemeManager(session),
108
                                      'languages_manager': LanguagesManager(session)}
109
        self.concept_schema = ConceptSchema(
110
            validator=concept_schema_validator
111
        ).bind(
112
            request=self.request,
113
            conceptscheme_id=1
114
        )
115
        self.language = LanguageTag(
116
            validator=languagetag_validator
117
        ).bind(
118
            request=self.request,
119
            new=True
120
        )
121
        self.conceptscheme_schema = ConceptSchemeSchema(
122
            validator=conceptscheme_schema_validator
123
        ).bind(
124
            request=self.request
125
        )
126
        self.json_concept = {
127
            "narrower": [{"id": 8}, {"id": 7}, {"id": 9}],
128
            "label": "Belgium",
129
            "type": "concept",
130
            "id": 4,
131
            "broader": [{"id": 2}],
132
            "related": [{"id": 5}],
133
            "labels": [{
134
                           "label": "Belgium",
135
                           "type": "prefLabel",
136
                           "language": "en"
137
                       }],
138
            "notes": [{
139
                          "note": "een notitie",
140
                          "type": "note",
141
                          "language": "nl"
142
                      }],
143
            "sources": [{
144
                "citation": "Van Daele K. 2014"
145
            }],
146
            "member_of": [{"id": 666}]
147
        }
148
        self.json_collection = {
149
            "id": 0,
150
            "labels": [{
151
                           "language": "nl-BE",
152
                           "label": "Stijlen en culturen",
153
                           "type": "prefLabel"
154
                       }],
155
            "type": "collection",
156
            "label": "Stijlen en culturen",
157
            "members": [{"id": 61}, {"id": 60}],
158
            "notes": [{
159
                          "note": "een notitie",
160
                          "type": "note",
161
                          "language": "nl"
162
                      }],
163
            "member_of": [{"id": 666}]
164
        }
165
        self.json_conceptscheme = {
166
            "labels": [{
167
                           "language": "nl-BE",
168
                           "label": "Stijlen en culturen",
169
                           "type": "prefLabel"
170
                       }],
171
            "label": "Stijlen en culturen",
172
            "notes": [{
173
                          "note": "een notitie",
174
                          "type": "note",
175
                          "language": "nl"
176
                      }],
177
            "sources": [{
178
                "citation": "Van Daele K. 2014"
179
            }]
180
        }
181
182
    def tearDown(self):
183
        testing.tearDown()
184
185
    def test_validation_conceptscheme(self):
186
        validated_conceptscheme = self.conceptscheme_schema.deserialize(self.json_conceptscheme)
187
        self.assertIsNotNone(validated_conceptscheme)
188
        self.assertEqual(1, len(validated_conceptscheme['labels']))
189
        self.assertEqual(1, len(validated_conceptscheme['notes']))
190
        self.assertEqual(1, len(validated_conceptscheme['sources']))
191
192
    def test_invalid_conceptscheme(self):
193
        self.json_conceptscheme.pop('labels')
194
        self.assertRaises(ValidationError, self.conceptscheme_schema.deserialize, self.json_conceptscheme)
195
196
    def test_validation_concept(self):
197
        error_raised = False
198
        validated_concept = None
199
        try:
200
            validated_concept = self.concept_schema.deserialize(self.json_concept)
201
        except ValidationError:
202
            error_raised = True
203
        self.assertFalse(error_raised)
204
        self.assertIsNotNone(validated_concept)
205
        self.assertEqual(1, len(validated_concept['labels']))
206
        self.assertEqual(1, len(validated_concept['notes']))
207
        self.assertEqual(1, len(validated_concept['sources']))
208
        self.assertEqual(3, len(validated_concept['narrower']))
209
        self.assertEqual(1, len(validated_concept['broader']))
210
        self.assertEqual(1, len(validated_concept['related']))
211
        self.assertEqual(1, len(validated_concept['member_of']))
212
213
    def test_max_preflabels_2_en(self):
214
        self.json_concept['labels'].append({
215
            "label": "B",
216
            "type": "prefLabel",
217
            "language": "en"
218
        })
219
        error_raised = False
220
        error = None
221
        validated_concept = None
222
        try:
223
            validated_concept = self.concept_schema.deserialize(self.json_concept)
224
        except ValidationError as e:
225
            error_raised = True
226
            error = e
227
        self.assertTrue(error_raised)
228
        self.assertIsNone(validated_concept)
229
        self.assertTrue(isinstance(error, ValidationError))
230
        self.assertIn({'labels': 'Only one prefLabel per language allowed.'}, error.errors)
231
232
    def test_max_preflabels_1_en_1_nl(self):
233
        self.json_concept['labels'].append({
234
            "label": "B",
235
            "type": "prefLabel",
236
            "language": "nl"
237
        })
238
        error_raised = False
239
        validated_concept = None
240
        try:
241
            validated_concept = self.concept_schema.deserialize(self.json_concept)
242
        except ValidationError:
243
            error_raised = True
244
        self.assertFalse(error_raised)
245
        self.assertIsNotNone(validated_concept)
246
247
    def test_related_concept_type_concept(self):
248
        error_raised = False
249
        validated_concept = None
250
        try:
251
            validated_concept = self.concept_schema.deserialize(self.json_concept)
252
        except ValidationError:
253
            error_raised = True
254
        self.assertFalse(error_raised)
255
        self.assertIsNotNone(validated_concept)
256
257
    def test_related_concept_type_collection(self):
258
        self.json_concept['related'].append({"id": 666})
259
        error_raised = False
260
        error = None
261
        validated_concept = None
262
        try:
263
            validated_concept = self.concept_schema.deserialize(self.json_concept)
264
        except ValidationError as e:
265
            error_raised = True
266
            error = e
267
        self.assertTrue(error_raised)
268
        self.assertIsNone(validated_concept)
269
        self.assertTrue(isinstance(error, ValidationError))
270
        self.assertIn({'related': 'A narrower, broader or related concept'
271
                                  ' should always be a concept, not a collection'}, error.errors)
272
273
    def test_collection_with_related(self):
274
        # Collections can not have related relations
275
        self.json_collection['related'] = []
276
        self.json_collection['related'].append({"id": 2})
277
        error_raised = False
278
        error = None
279
        validated_concept = None
280
        try:
281
            validated_concept = self.concept_schema.deserialize(self.json_collection)
282
        except ValidationError as e:
283
            error_raised = True
284
            error = e
285
        self.assertTrue(error_raised)
286
        self.assertIsNone(validated_concept)
287
        self.assertTrue(isinstance(error, ValidationError))
288
        self.assertIn({'related': 'Only concepts can have narrower/broader/related relations'}, error.errors)
289
290
    def test_narrower_concept_type_concept(self):
291
        error_raised = False
292
        validated_concept = None
293
        try:
294
            validated_concept = self.concept_schema.deserialize(self.json_concept)
295
        except ValidationError:
296
            error_raised = True
297
        self.assertFalse(error_raised)
298
        self.assertIsNotNone(validated_concept)
299
300
    def test_narrower_concept_type_collection(self):
301
        self.json_concept['narrower'].append({"id": 666})
302
        error_raised = False
303
        error = None
304
        validated_concept = None
305
        try:
306
            validated_concept = self.concept_schema.deserialize(self.json_concept)
307
        except ValidationError as e:
308
            error_raised = True
309
            error = e
310
        self.assertTrue(error_raised)
311
        self.assertIsNone(validated_concept)
312
        self.assertTrue(isinstance(error, ValidationError))
313
        self.assertIn({'narrower': 'A narrower, broader or related concept'
314
                                   ' should always be a concept, not a collection'}, error.errors)
315
316
    def test_collection_with_narrower(self):
317
        # Collections can not have narrower relations
318
        self.json_collection['narrower'] = []
319
        self.json_collection['narrower'].append({"id": 2})
320
        error_raised = False
321
        error = None
322
        validated_concept = None
323
        try:
324
            validated_concept = self.concept_schema.deserialize(self.json_collection)
325
        except ValidationError as e:
326
            error_raised = True
327
            error = e
328
        self.assertTrue(error_raised)
329
        self.assertIsNone(validated_concept)
330
        self.assertTrue(isinstance(error, ValidationError))
331
        self.assertIn({'narrower': 'Only concepts can have narrower/broader/related relations'}, error.errors)
332
333
    def test_broader_concept_type_concept(self):
334
        error_raised = False
335
        validated_concept = None
336
        try:
337
            validated_concept = self.concept_schema.deserialize(self.json_concept)
338
        except ValidationError:
339
            error_raised = True
340
        self.assertFalse(error_raised)
341
        self.assertIsNotNone(validated_concept)
342
343
    def test_broader_concept_type_collection(self):
344
        self.json_concept['broader'].append({"id": 666})
345
        error_raised = False
346
        error = None
347
        validated_concept = None
348
        try:
349
            validated_concept = self.concept_schema.deserialize(self.json_concept)
350
        except ValidationError as e:
351
            error_raised = True
352
            error = e
353
        self.assertTrue(error_raised)
354
        self.assertIsNone(validated_concept)
355
        self.assertTrue(isinstance(error, ValidationError))
356
        self.assertIn({'broader': 'A narrower, broader or related concept should always be'
357
                                  ' a concept, not a collection'}, error.errors)
358
359
    def test_collection_with_broader(self):
360
        # Collections can not have broader relations
361
        self.json_collection['broader'] = []
362
        self.json_collection['broader'].append({"id": 2})
363
        error_raised = False
364
        validated_concept = None
365
        try:
366
            validated_concept = self.concept_schema.deserialize(self.json_collection)
367
        except ValidationError:
368
            error_raised = True
369
        self.assertTrue(error_raised)
370
        self.assertIsNone(validated_concept)
371
372
    def test_related_concept_different_conceptscheme(self):
373
        self.json_concept['related'].append({"id": 777})
374
        error_raised = False
375
        error = None
376
        validated_concept = None
377
        try:
378
            validated_concept = self.concept_schema.deserialize(self.json_concept)
379
        except ValidationError as e:
380
            error_raised = True
381
            error = e
382
        self.assertTrue(error_raised)
383
        self.assertIsNone(validated_concept)
384
        self.assertTrue(isinstance(error, ValidationError))
385
        self.assertIn({'related': 'Concept not found, check concept_id. Please be aware members'
386
                                  ' should be within one scheme'}, error.errors)
387
388
    def test_narrower_concept_different_conceptscheme(self):
389
        self.json_concept['narrower'].append({"id": 777})
390
        error_raised = False
391
        error = None
392
        validated_concept = None
393
        try:
394
            validated_concept = self.concept_schema.deserialize(self.json_concept)
395
        except ValidationError as e:
396
            error_raised = True
397
            error = e
398
        self.assertTrue(error_raised)
399
        self.assertIsNone(validated_concept)
400
        self.assertTrue(isinstance(error, ValidationError))
401
        self.assertIn({'narrower': 'Concept not found, check concept_id. Please be aware members'
402
                                   ' should be within one scheme'}, error.errors)
403
404
405
    def test_narrower_concept_to_self(self):
406
        self.json_concept['narrower'].append({"id": 4})
407
        error_raised = False
408
        error = None
409
        validated_concept = None
410
        try:
411
            validated_concept = self.concept_schema.deserialize(self.json_concept)
412
        except ValidationError as e:
413
            error_raised = True
414
            error = e
415
        self.assertTrue(error_raised)
416
        self.assertIsNone(validated_concept)
417
        self.assertTrue(isinstance(error, ValidationError))
418
        self.assertIn({'narrower': 'A concept or collection cannot be related to itself'}, error.errors)
419
420
    def test_broader_concept_different_conceptscheme(self):
421
        self.json_concept['broader'].append({"id": 777})
422
        error_raised = False
423
        error = None
424
        validated_concept = None
425
        try:
426
            validated_concept = self.concept_schema.deserialize(self.json_concept)
427
        except ValidationError as e:
428
            error_raised = True
429
            error = e
430
        self.assertTrue(error_raised)
431
        self.assertIsNone(validated_concept)
432
        self.assertTrue(isinstance(error, ValidationError))
433
        self.assertIn({'broader': 'Concept not found, check concept_id. Please be aware members'
434
                                  ' should be within one scheme'}, error.errors)
435
436
    def test_broader_concept_hierarchy(self):
437
        self.json_concept['broader'].append({"id": 14})
438
        error_raised = False
439
        error = None
440
        validated_concept = None
441
        try:
442
            validated_concept = self.concept_schema.deserialize(self.json_concept)
443
        except ValidationError as e:
444
            error_raised = True
445
            error = e
446
        self.assertTrue(error_raised)
447
        self.assertIsNone(validated_concept)
448
        self.assertTrue(isinstance(error, ValidationError))
449
        self.assertIn({'broader': 'The broader concept of a concept must not itself '
450
                                  'be a narrower concept of the concept being edited.'}, error.errors)
451
452
    def test_broader_concept_hierarchy_no_narrower(self):
453
        self.json_concept['broader'].append({"id": 8})
454
        self.json_concept['narrower'] = []
455
        error_raised = False
456
        validated_concept = None
457
        try:
458
            validated_concept = self.concept_schema.deserialize(self.json_concept)
459
        except ValidationError:
460
            error_raised = True
461
        self.assertFalse(error_raised)
462
        self.assertIsNotNone(validated_concept)
463
464
    def test_narrower_concept_hierarchy(self):
465
        self.json_concept['narrower'].append({"id": 1})
466
        error_raised = False
467
        error = None
468
        validated_concept = None
469
        try:
470
            validated_concept = self.concept_schema.deserialize(self.json_concept)
471
        except ValidationError as e:
472
            error_raised = True
473
            error = e
474
        self.assertTrue(error_raised)
475
        self.assertIsNone(validated_concept)
476
        self.assertTrue(isinstance(error, ValidationError))
477
        self.assertIn({'narrower': 'The narrower concept of a concept must not itself '
478
                                   'be a broader concept of the concept being edited.'}, error.errors)
479
480
    def test_narrower_concept_hierarchy_no_broader(self):
481
        self.json_concept['narrower'].append({"id": 1})
482
        self.json_concept['broader'] = []
483
        error_raised = False
484
        validated_concept = None
485
        try:
486
            validated_concept = self.concept_schema.deserialize(self.json_concept)
487
        except ValidationError:
488
            error_raised = True
489
        self.assertFalse(error_raised)
490
        self.assertIsNotNone(validated_concept)
491
492
    def test_validation_collection(self):
493
        error_raised = False
494
        validated_collection = None
495
        try:
496
            validated_collection = self.concept_schema.deserialize(self.json_collection)
497
        except ValidationError as e:
498
            error_raised = True
499
        self.assertFalse(error_raised)
500
        self.assertIsNotNone(validated_collection)
501
        self.assertEqual(2, len(validated_collection['members']))
502
        self.assertEqual(1, len(validated_collection['labels']))
503
        self.assertEqual(1, len(validated_collection['notes']))
504
505
    def test_member_concept_different_conceptscheme(self):
506
        error_raised = False
507
        error = None
508
        validated_collection = None
509
        self.json_collection['members'].append({"id": 777})
510
        try:
511
            validated_collection = self.concept_schema.deserialize(self.json_collection)
512
        except ValidationError as e:
513
            error_raised = True
514
            error = e
515
        self.assertTrue(error_raised)
516
        self.assertIsNone(validated_collection)
517
        self.assertTrue(isinstance(error, ValidationError))
518
        self.assertIn({'members': 'Concept not found, check concept_id. Please be aware members'
519
                                  ' should be within one scheme'}, error.errors)
520
521
    def test_label_type(self):
522
        error_raised = False
523
        validated_concept = None
524
        self.json_concept['labels'].append({
525
            "label": "Belgium",
526
            "type": "altLabel",
527
            "language": "en"
528
        })
529
        try:
530
            validated_concept = self.concept_schema.deserialize(self.json_concept)
531
        except ValidationError:
532
            error_raised = True
533
        self.assertFalse(error_raised)
534
        self.assertIsNotNone(validated_concept)
535
536
    def test_label_type_invalid(self):
537
        error_raised = False
538
        error = None
539
        validated_concept = None
540
        self.json_concept['labels'].append({
541
            "label": "Belgium",
542
            "type": "testLabelInvalid",
543
            "language": "en"
544
        })
545
        try:
546
            validated_concept = self.concept_schema.deserialize(self.json_concept)
547
        except ValidationError as e:
548
            error_raised = True
549
            error = e
550
        self.assertTrue(error_raised)
551
        self.assertIsNone(validated_concept)
552
        self.assertTrue(isinstance(error, ValidationError))
553
        self.assertIn({'labels': 'Invalid labeltype.'}, error.errors)
554
555
    def test_label_language_invalid(self):
556
        error_raised = False
557
        error = None
558
        validated_concept = None
559
        self.json_concept['labels'].append({
560
            "label": "Belgium",
561
            "type": "altLabel",
562
            "language": "eng"
563
        })
564
        try:
565
            validated_concept = self.concept_schema.deserialize(self.json_concept)
566
        except ValidationError as e:
567
            error_raised = True
568
            error = e
569
        self.assertTrue(error_raised)
570
        self.assertIsNone(validated_concept)
571
        self.assertTrue(isinstance(error, ValidationError))
572
        self.assertIn({'labels': 'Invalid language tag: Unknown code \'eng\', Missing language tag in \'eng\'.'},
573
                      error.errors)
574
575
    def test_label_language_missing(self):
576
        error_raised = False
577
        validated_concept = None
578
        self.json_concept['labels'].append({
579
            "label": "Belgium",
580
            "type": "altLabel",
581
            "language": "af"
582
        })
583
        try:
584
            validated_concept = self.concept_schema.deserialize(self.json_concept)
585
        except ValidationError as e:
586
            error_raised = True
587
        self.assertFalse(error_raised)
588
        self.assertIsNotNone(validated_concept)
589
590
    def test_label_invalid(self):
591
        error_raised = False
592
        error = None
593
        validated_concept = None
594
        self.json_concept['labels'].append({
595
            "note": "Belgium",
596
            "type": "altLabel",
597
            "language": "en"
598
        })
599
        try:
600
            validated_concept = self.concept_schema.deserialize(self.json_concept)
601
        except colander.Invalid as e:
602
            error_raised = True
603
            error = e
604
        self.assertTrue(error_raised)
605
        self.assertIsNone(validated_concept)
606
        self.assertTrue(isinstance(error, colander.Invalid))
607
608
    def test_note_invalid(self):
609
        error_raised = False
610
        error = None
611
        validated_concept = None
612
        self.json_concept['notes'].append({
613
            "label": "een notitie",
614
            "type": "note",
615
            "language": "nl"
616
        })
617
        try:
618
            validated_concept = self.concept_schema.deserialize(self.json_concept)
619
        except colander.Invalid as e:
620
            error_raised = True
621
            error = e
622
        self.assertTrue(error_raised)
623
        self.assertIsNone(validated_concept)
624
        self.assertTrue(isinstance(error, colander.Invalid))
625
626
    def test_memberof_concept_type_collection(self):
627
        # A Collection/Concept can be a member_of a Collection
628
        error_raised = False
629
        validated_concept = None
630
        try:
631
            validated_concept = self.concept_schema.deserialize(self.json_concept)
632
        except ValidationError:
633
            error_raised = True
634
        self.assertFalse(error_raised)
635
        self.assertIsNotNone(validated_concept)
636
637
    def test_memberof_concept_type_concept(self):
638
        # Nothing can be a member_of a Concept
639
        self.json_concept['member_of'].append({"id": 2})
640
        error_raised = False
641
        error = None
642
        validated_concept = None
643
        try:
644
            validated_concept = self.concept_schema.deserialize(self.json_concept)
645
        except ValidationError as e:
646
            error_raised = True
647
            error = e
648
        self.assertTrue(error_raised)
649
        self.assertIsNone(validated_concept)
650
        self.assertTrue(isinstance(error, ValidationError))
651
        self.assertIn({'member_of': 'A member_of parent should always be a collection'}, error.errors)
652
653
    def test_members_collection_unique(self):
654
        # A Collection is a Set (every element of the Collection should be unique).
655
        self.json_collection['members'].append({"id": 61})
656
        error_raised = False
657
        error = None
658
        validated_concept = None
659
        try:
660
            validated_concept = self.concept_schema.deserialize(self.json_collection)
661
        except ValidationError as e:
662
            error_raised = True
663
            error = e
664
        self.assertTrue(error_raised)
665
        self.assertIsNone(validated_concept)
666
        self.assertTrue(isinstance(error, ValidationError))
667
        self.assertIn({'members': 'All members of a collection should be unique.'}, error.errors)
668
669
    def test_concept_members(self):
670
        # A Concept does not have members.
671
        self.json_concept['members'] = []
672
        self.json_concept['members'].append({"id": 2})
673
        error_raised = False
674
        error = None
675
        validated_concept = None
676
        try:
677
            validated_concept = self.concept_schema.deserialize(self.json_concept)
678
        except ValidationError as e:
679
            error_raised = True
680
            error = e
681
        self.assertTrue(error_raised)
682
        self.assertIsNone(validated_concept)
683
        self.assertTrue(isinstance(error, ValidationError))
684
        self.assertIn({'members': 'Only collections can have members.'}, error.errors)
685
686
    def test_memberof_concept_hierarchy_simple(self):
687
        # The hierarchy should not contain loops
688
        self.json_collection['members'].append({"id": 666})
689
        error_raised = False
690
        error = None
691
        validated_concept = None
692
        try:
693
            validated_concept = self.concept_schema.deserialize(self.json_collection)
694
        except ValidationError as e:
695
            error_raised = True
696
            error = e
697
        self.assertTrue(error_raised)
698
        self.assertIsNone(validated_concept)
699
        self.assertTrue(isinstance(error, ValidationError))
700
        self.assertIn({'member_of': 'The parent member_of collection of a concept must not itself'
701
                                    ' be a member of the concept being edited.'}, error.errors)
702
703
    def test_memberof_concept_hierarchy_deep(self):
704
        # The hierarchy should not contain loops
705
        self.json_collection['members'].append({"id": 62})
706
        error_raised = False
707
        error = None
708
        validated_concept = None
709
        try:
710
            validated_concept = self.concept_schema.deserialize(self.json_collection)
711
        except ValidationError as e:
712
            error_raised = True
713
            error = e
714
        self.assertTrue(error_raised)
715
        self.assertIsNone(validated_concept)
716
        self.assertTrue(isinstance(error, ValidationError))
717
        self.assertIn({'member_of': 'The parent member_of collection of a concept must not itself'
718
                                    ' be a member of the concept being edited.'}, error.errors)
719
720
    def test_members_concept_hierarchy_simple(self):
721
        # The hierarchy should not contain loops
722
        self.json_collection['member_of'].append({"id": 61})
723
        error_raised = False
724
        error = None
725
        validated_concept = None
726
        try:
727
            validated_concept = self.concept_schema.deserialize(self.json_collection)
728
        except ValidationError as e:
729
            error_raised = True
730
            error = e
731
        self.assertTrue(error_raised)
732
        self.assertIsNone(validated_concept)
733
        self.assertTrue(isinstance(error, ValidationError))
734
        self.assertIn({'members': 'The item of a members collection must not itself be a parent of'
735
                                  ' the concept/collection being edited.'}, error.errors)
736
737
    def test_members_concept_hierarchy_deep(self):
738
        # The hierarchy should not contain loops
739
        self.json_collection['member_of'].append({"id": 667})
740
        error_raised = False
741
        error = None
742
        validated_concept = None
743
        try:
744
            validated_concept = self.concept_schema.deserialize(self.json_collection)
745
        except ValidationError as e:
746
            error_raised = True
747
            error = e
748
        self.assertTrue(error_raised)
749
        self.assertIsNone(validated_concept)
750
        self.assertTrue(isinstance(error, ValidationError))
751
        self.assertIn({'members': 'The item of a members collection must not itself be a parent of'
752
                                  ' the concept/collection being edited.'}, error.errors)
753
754
    def test_min_labels_rule(self):
755
        error_raised = False
756
        validated_concept = None
757
        try:
758
            validated_concept = self.concept_schema.deserialize(self.json_concept)
759
        except ValidationError:
760
            error_raised = True
761
        self.assertFalse(error_raised)
762
        self.assertIsNotNone(validated_concept)
763
764
    def test_min_labels_rule_empty_labels(self):
765
        error_raised = False
766
        validated_concept = None
767
        self.json_concept['labels'] = []
768
        error = None
769
        try:
770
            validated_concept = self.concept_schema.deserialize(self.json_concept)
771
        except ValidationError as e:
772
            error_raised = True
773
            error = e
774
        self.assertTrue(error_raised)
775
        self.assertIsNone(validated_concept)
776
        self.assertIsNotNone(error)
777
        self.assertTrue(isinstance(error, ValidationError))
778
        self.assertIn({'labels': 'At least one label is necessary'}, error.errors)
779
780
    def test_min_labels_rule_no_labels(self):
781
        error_raised = False
782
        validated_concept = None
783
        json_concept = {
784
            "narrower": [{"id": 8}, {"id": 7}, {"id": 9}],
785
            "type": "concept",
786
            "id": 4,
787
            "broader": [{"id": 2}],
788
            "related": [{"id": 5}],
789
            "notes": [{
790
                          "note": "een notitie",
791
                          "type": "note",
792
                          "language": "nl"
793
                      }],
794
            "member_of": [{"id": 666}]
795
        }
796
        error = None
797
        try:
798
            validated_concept = self.concept_schema.deserialize(json_concept)
799
        except ValidationError as e:
800
            error_raised = True
801
            error = e
802
        self.assertTrue(error_raised)
803
        self.assertIsNone(validated_concept)
804
        self.assertIsNotNone(error)
805
        self.assertTrue(isinstance(error, ValidationError))
806
        self.assertIn({'labels': 'At least one label is necessary'}, error.errors)
807
808
    def test_concept_matches_rule(self):
809
        error_raised = False
810
        validated_concept = None
811
        json_concept = {
812
            "type": "collection",
813
            "labels": [{
814
                           "language": "nl",
815
                           "label": "Stijlen en culturen",
816
                           "type": "prefLabel"
817
                       }],
818
            "id": 4,
819
            "members": [{"id": 666}],
820
            "matches": {"exactMatch": ["urn:sample:666"], "broadMatch": ["urn:somewhere:93"]}
821
        }
822
        error = None
823
        try:
824
            validated_concept = self.concept_schema.deserialize(json_concept)
825
        except ValidationError as e:
826
            error_raised = True
827
            error = e
828
        self.assertTrue(error_raised)
829
        self.assertIsNone(validated_concept)
830
        self.assertIsNotNone(error)
831
        self.assertTrue(isinstance(error, ValidationError))
832
        self.assertIn({'matches': 'Only concepts can have matches'}, error.errors)
833
834
    def test_concept_matches_unique_rule(self):
835
        error_raised = False
836
        validated_concept = None
837
        json_concept = {
838
            "type": "concept",
839
            "labels": [{
840
                           "language": "nl",
841
                           "label": "Stijlen en culturen",
842
                           "type": "prefLabel"
843
                       }],
844
            "id": 4,
845
            "member_of": [{"id": 666}],
846
            "matches": {"exact": ["urn:sample:666"], "broad": ["urn:sample:666"]}
847
        }
848
        error = None
849
        try:
850
            validated_concept = self.concept_schema.deserialize(json_concept)
851
        except ValidationError as e:
852
            error_raised = True
853
            error = e
854
        self.assertTrue(error_raised)
855
        self.assertIsNone(validated_concept)
856
        self.assertIsNotNone(error)
857
        self.assertTrue(isinstance(error, ValidationError))
858
        self.assertIn({'matches': 'All matches of a concept should be unique.'}, error.errors)
859
860
    def test_concept_matches_unique_rule_pass(self):
861
        error_raised = False
862
        validated_concept = None
863
        json_concept = {
864
            "type": "concept",
865
            "labels": [{
866
                           "language": "nl",
867
                           "label": "Stijlen en culturen",
868
                           "type": "prefLabel"
869
                       }],
870
            "id": 4,
871
            "member_of": [{"id": 666}],
872
            "matches": {"exactMatch": ["urn:sample:666"], "broadMatch": ["urn:sample:93"]}
873
        }
874
        try:
875
            validated_concept = self.concept_schema.deserialize(json_concept)
876
        except ValidationError:
877
            error_raised = True
878
        self.assertFalse(error_raised)
879
        self.assertIsNotNone(validated_concept)
880
881
    def test_languages_pass(self):
882
        error_raised = False
883
        validated_language = None
884
        json_language = {
885
            "id": "af",
886
            "name": "Afrikaans"
887
        }
888
        try:
889
            validated_language = self.language.deserialize(json_language)
890
        except ValidationError:
891
            error_raised = True
892
        self.assertFalse(error_raised)
893
        self.assertIsNotNone(validated_language)
894
895
    def test_languages_duplicate(self):
896
        error_raised = False
897
        validated_language = None
898
        json_language = {
899
            "id": "en",
900
            "name": "English"
901
        }
902
        error = None
903
        try:
904
            validated_language = self.language.deserialize(json_language)
905
        except ValidationError as e:
906
            error_raised = True
907
            error = e
908
        self.assertTrue(error_raised)
909
        self.assertIsNone(validated_language)
910
        self.assertIsNotNone(error)
911
        self.assertIn({'id': 'Duplicate language tag: en'}, error.errors)
912
913
    def test_languages_edit_not_raise_duplicate(self):
914
        error_raised = False
915
        validated_language = None
916
        json_language = {
917
            "id": "en",
918
            "name": "English"
919
        }
920
        language = LanguageTag(
921
            validator=languagetag_validator
922
        ).bind(
923
            request=self.request,
924
            new=False
925
        )
926
        try:
927
            validated_language = language.deserialize(json_language)
928
        except ValidationError:
929
            error_raised = True
930
        self.assertFalse(error_raised)
931
        self.assertIsNotNone(validated_language)
932
933
    def test_languages_invalid(self):
934
        error_raised = False
935
        validated_language = None
936
        json_language = {
937
            "id": "flup",
938
            "name": "test"
939
        }
940
        error = None
941
        try:
942
            validated_language = self.language.deserialize(json_language)
943
        except ValidationError as e:
944
            error_raised = True
945
            error = e
946
        self.assertTrue(error_raised)
947
        self.assertIsNone(validated_language)
948
        self.assertIsNotNone(error)
949
        self.assertIn({"id": "Invalid language tag: Unknown code 'flup', Missing language tag in 'flup'."},
950
                      error.errors)
951
952
    def test_subordinate_arrays(self):
953
        error_raised = False
954
        validated_json = None
955
        self.json_concept['subordinate_arrays'] = [{"id": 667}]
956
        try:
957
            validated_json = self.concept_schema.deserialize(self.json_concept)
958
        except ValidationError:
959
            error_raised = True
960
        self.assertFalse(error_raised)
961
        self.assertIsNotNone(validated_json)
962
963
    def test_subordinate_arrays_no_concept(self):
964
        error_raised = False
965
        validated_json = None
966
        error = None
967
        self.json_collection['subordinate_arrays'] = [{"id": 666}]
968
        try:
969
            validated_json = self.concept_schema.deserialize(self.json_collection)
970
        except ValidationError as e:
971
            error_raised = True
972
            error = e
973
        self.assertTrue(error_raised)
974
        self.assertIsNone(validated_json)
975
        self.assertIsNotNone(error)
976
        self.assertIn({'subordinate_arrays': 'Only concept can have subordinate arrays.'}, error.errors)
977
978
    def test_subordinate_arrays_no_collection(self):
979
        error_raised = False
980
        validated_json = None
981
        error = None
982
        self.json_concept['subordinate_arrays'] = [{"id": 7}]
983
        try:
984
            validated_json = self.concept_schema.deserialize(self.json_concept)
985
        except ValidationError as e:
986
            error_raised = True
987
            error = e
988
        self.assertTrue(error_raised)
989
        self.assertIsNone(validated_json)
990
        self.assertIsNotNone(error)
991
        self.assertIn({'subordinate_arrays': 'A subordinate array should always be a collection'}, error.errors)
992
993
    def test_subordinate_arrays_hierarchy(self):
994
        error_raised = False
995
        validated_json = None
996
        error = None
997
        self.json_concept['subordinate_arrays'] = [{"id": 666}]
998
        try:
999
            validated_json = self.concept_schema.deserialize(self.json_concept)
1000
        except ValidationError as e:
1001
            error_raised = True
1002
            error = e
1003
        self.assertTrue(error_raised)
1004
        self.assertIsNone(validated_json)
1005
        self.assertIsNotNone(error)
1006
        self.assertIn({
1007
                          'subordinate_arrays': 'The subordinate_array collection of a concept must not itself be a parent of the concept being edited.'},
1008
                      error.errors)
1009
1010
    def test_superordinates(self):
1011
        error_raised = False
1012
        validated_json = None
1013
        self.json_collection['superordinates'] = [{"id": 7}]
1014
        try:
1015
            validated_json = self.concept_schema.deserialize(self.json_collection)
1016
        except ValidationError:
1017
            error_raised = True
1018
        self.assertFalse(error_raised)
1019
        self.assertIsNotNone(validated_json)
1020
1021
    def test_superordinates_no_concept(self):
1022
        error_raised = False
1023
        validated_json = None
1024
        error = None
1025
        self.json_collection['superordinates'] = [{"id": 666}]
1026
        try:
1027
            validated_json = self.concept_schema.deserialize(self.json_collection)
1028
        except ValidationError as e:
1029
            error_raised = True
1030
            error = e
1031
        self.assertTrue(error_raised)
1032
        self.assertIsNone(validated_json)
1033
        self.assertIsNotNone(error)
1034
        self.assertIn({'superordinates': 'A superordinate should always be a concept'}, error.errors)
1035
1036
    def test_superordinates_no_collection(self):
1037
        error_raised = False
1038
        validated_json = None
1039
        error = None
1040
        self.json_concept['superordinates'] = [{"id": 7}]
1041
        try:
1042
            validated_json = self.concept_schema.deserialize(self.json_concept)
1043
        except ValidationError as e:
1044
            error_raised = True
1045
            error = e
1046
        self.assertTrue(error_raised)
1047
        self.assertIsNone(validated_json)
1048
        self.assertIsNotNone(error)
1049
        self.assertIn({'superordinates': 'Only collection can have superordinates.'}, error.errors)
1050
1051
    def test_superordinates_hierarchy(self):
1052
        error_raised = False
1053
        validated_json = None
1054
        error = None
1055
        self.json_collection['superordinates'] = [{"id": 61}]
1056
        try:
1057
            validated_json = self.concept_schema.deserialize(self.json_collection)
1058
        except ValidationError as e:
1059
            error_raised = True
1060
            error = e
1061
        self.assertTrue(error_raised)
1062
        self.assertIsNone(validated_json)
1063
        self.assertIsNotNone(error)
1064
        self.assertIn({
1065
                          'superordinates': 'The superordinates of a collection must not itself be a member of the collection being edited.'},
1066
                      error.errors)
1067
1068